diff options
author | Calin Juravle <calin@google.com> | 2014-11-04 16:40:20 +0000 |
---|---|---|
committer | Calin Juravle <calin@google.com> | 2014-11-06 14:42:58 +0000 |
commit | d0d4852847432368b090c184d6639e573538dccf (patch) | |
tree | 47e31fe860ff1c3ace2f3f5945aa69689d42d998 | |
parent | a88b7b93e28ea86969dd3ec6a6bf6929d697fc31 (diff) | |
download | art-d0d4852847432368b090c184d6639e573538dccf.tar.gz art-d0d4852847432368b090c184d6639e573538dccf.tar.bz2 art-d0d4852847432368b090c184d6639e573538dccf.zip |
[optimizing compiler] Add div-int and exception handling.
- for backends: arm, x86, x86_64
- fixed a register allocator bug: the request for a fixed register for
the first input was ignored if the output was kSameAsFirstInput
- added divide by zero exception
- more tests
- shuffle around some code in the builder to reduce the number of lines
of code for a single function.
Change-Id: Id3a515e02bfbc66cd9d16cb9746f7551bdab3d42
-rw-r--r-- | compiler/optimizing/builder.cc | 143 | ||||
-rw-r--r-- | compiler/optimizing/builder.h | 7 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_arm.cc | 51 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_arm64.cc | 1 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_x86.cc | 96 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_x86_64.cc | 98 | ||||
-rw-r--r-- | compiler/optimizing/codegen_test.cc | 9 | ||||
-rw-r--r-- | compiler/optimizing/nodes.h | 28 | ||||
-rw-r--r-- | compiler/optimizing/prepare_for_register_allocation.cc | 4 | ||||
-rw-r--r-- | compiler/optimizing/prepare_for_register_allocation.h | 1 | ||||
-rw-r--r-- | compiler/optimizing/register_allocator.cc | 14 | ||||
-rw-r--r-- | compiler/optimizing/register_allocator_test.cc | 41 | ||||
-rw-r--r-- | test/417-optimizing-arith-div/src/Main.java | 55 |
13 files changed, 489 insertions, 59 deletions
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc index 4ce23d725b..d30f3e38c5 100644 --- a/compiler/optimizing/builder.cc +++ b/compiler/optimizing/builder.cc @@ -537,6 +537,27 @@ bool HGraphBuilder::BuildStaticFieldAccess(const Instruction& instruction, return true; } +void HGraphBuilder::BuildCheckedDiv(const Instruction& instruction, + uint32_t dex_offset, + Primitive::Type type, + bool second_is_lit) { + DCHECK(type == Primitive::kPrimInt); + + HInstruction* first = LoadLocal(instruction.VRegB(), type); + HInstruction* second = second_is_lit + ? GetIntConstant(instruction.VRegC()) + : LoadLocal(instruction.VRegC(), type); + if (!second->IsIntConstant() || (second->AsIntConstant()->GetValue() == 0)) { + second = new (arena_) HDivZeroCheck(second, dex_offset); + Temporaries temps(graph_, 1); + current_block_->AddInstruction(second); + temps.Add(current_block_->GetLastInstruction()); + } + + current_block_->AddInstruction(new (arena_) HDiv(type, first, second)); + UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction()); +} + void HGraphBuilder::BuildArrayAccess(const Instruction& instruction, uint32_t dex_offset, bool is_put, @@ -617,6 +638,60 @@ void HGraphBuilder::BuildFillArrayData(HInstruction* object, } } +void HGraphBuilder::BuildFillArrayData(const Instruction& instruction, uint32_t dex_offset) { + Temporaries temps(graph_, 1); + HInstruction* array = LoadLocal(instruction.VRegA_31t(), Primitive::kPrimNot); + HNullCheck* null_check = new (arena_) HNullCheck(array, dex_offset); + current_block_->AddInstruction(null_check); + temps.Add(null_check); + + HInstruction* length = new (arena_) HArrayLength(null_check); + current_block_->AddInstruction(length); + + int32_t payload_offset = instruction.VRegB_31t() + dex_offset; + const Instruction::ArrayDataPayload* payload = + reinterpret_cast<const Instruction::ArrayDataPayload*>(code_start_ + payload_offset); + const uint8_t* data = payload->data; + uint32_t element_count = payload->element_count; + + // Implementation of this DEX instruction seems to be that the bounds check is + // done before doing any stores. + HInstruction* last_index = GetIntConstant(payload->element_count - 1); + current_block_->AddInstruction(new (arena_) HBoundsCheck(last_index, length, dex_offset)); + + switch (payload->element_width) { + case 1: + BuildFillArrayData(null_check, + reinterpret_cast<const int8_t*>(data), + element_count, + Primitive::kPrimByte, + dex_offset); + break; + case 2: + BuildFillArrayData(null_check, + reinterpret_cast<const int16_t*>(data), + element_count, + Primitive::kPrimShort, + dex_offset); + break; + case 4: + BuildFillArrayData(null_check, + reinterpret_cast<const int32_t*>(data), + element_count, + Primitive::kPrimInt, + dex_offset); + break; + case 8: + BuildFillWideArrayData(null_check, + reinterpret_cast<const int64_t*>(data), + element_count, + dex_offset); + break; + default: + LOG(FATAL) << "Unknown element width for " << payload->element_width; + } +} + void HGraphBuilder::BuildFillWideArrayData(HInstruction* object, const int64_t* data, uint32_t element_count, @@ -901,6 +976,11 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32 break; } + case Instruction::DIV_INT: { + BuildCheckedDiv(instruction, dex_offset, Primitive::kPrimInt, false); + break; + } + case Instruction::DIV_FLOAT: { Binop_23x<HDiv>(instruction, Primitive::kPrimFloat); break; @@ -966,6 +1046,11 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32 break; } + case Instruction::DIV_INT_2ADDR: { + BuildCheckedDiv(instruction, dex_offset, Primitive::kPrimInt, false); + break; + } + case Instruction::DIV_FLOAT_2ADDR: { Binop_12x<HDiv>(instruction, Primitive::kPrimFloat); break; @@ -1006,6 +1091,12 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32 break; } + case Instruction::DIV_INT_LIT16: + case Instruction::DIV_INT_LIT8: { + BuildCheckedDiv(instruction, dex_offset, Primitive::kPrimInt, true); + break; + } + case Instruction::NEW_INSTANCE: { current_block_->AddInstruction( new (arena_) HNewInstance(dex_offset, instruction.VRegB_21c())); @@ -1040,57 +1131,7 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32 } case Instruction::FILL_ARRAY_DATA: { - Temporaries temps(graph_, 1); - HInstruction* array = LoadLocal(instruction.VRegA_31t(), Primitive::kPrimNot); - HNullCheck* null_check = new (arena_) HNullCheck(array, dex_offset); - current_block_->AddInstruction(null_check); - temps.Add(null_check); - - HInstruction* length = new (arena_) HArrayLength(null_check); - current_block_->AddInstruction(length); - - int32_t payload_offset = instruction.VRegB_31t() + dex_offset; - const Instruction::ArrayDataPayload* payload = - reinterpret_cast<const Instruction::ArrayDataPayload*>(code_start_ + payload_offset); - const uint8_t* data = payload->data; - uint32_t element_count = payload->element_count; - - // Implementation of this DEX instruction seems to be that the bounds check is - // done before doing any stores. - HInstruction* last_index = GetIntConstant(payload->element_count - 1); - current_block_->AddInstruction(new (arena_) HBoundsCheck(last_index, length, dex_offset)); - - switch (payload->element_width) { - case 1: - BuildFillArrayData(null_check, - reinterpret_cast<const int8_t*>(data), - element_count, - Primitive::kPrimByte, - dex_offset); - break; - case 2: - BuildFillArrayData(null_check, - reinterpret_cast<const int16_t*>(data), - element_count, - Primitive::kPrimShort, - dex_offset); - break; - case 4: - BuildFillArrayData(null_check, - reinterpret_cast<const int32_t*>(data), - element_count, - Primitive::kPrimInt, - dex_offset); - break; - case 8: - BuildFillWideArrayData(null_check, - reinterpret_cast<const int64_t*>(data), - element_count, - dex_offset); - break; - default: - LOG(FATAL) << "Unknown element width for " << payload->element_width; - } + BuildFillArrayData(instruction, dex_offset); break; } diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h index f25415bc3e..f09f729549 100644 --- a/compiler/optimizing/builder.h +++ b/compiler/optimizing/builder.h @@ -120,6 +120,11 @@ class HGraphBuilder : public ValueObject { Primitive::Type input_type, Primitive::Type result_type); + void BuildCheckedDiv(const Instruction& instruction, + uint32_t dex_offset, + Primitive::Type type, + bool second_is_lit); + void BuildReturn(const Instruction& instruction, Primitive::Type type); // Builds an instance field access node and returns whether the instruction is supported. @@ -150,6 +155,8 @@ class HGraphBuilder : public ValueObject { uint32_t* args, uint32_t register_index); + void BuildFillArrayData(const Instruction& instruction, uint32_t dex_offset); + // Fills the given object with data as specified in the fill-array-data // instruction. Currently only used for non-reference and non-floating point // arrays. diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index 39e564a7c4..982d6d4031 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -92,6 +92,22 @@ class NullCheckSlowPathARM : public SlowPathCodeARM { DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathARM); }; +class DivZeroCheckSlowPathARM : public SlowPathCodeARM { + public: + explicit DivZeroCheckSlowPathARM(HDivZeroCheck* instruction) : instruction_(instruction) {} + + virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); + __ Bind(GetEntryLabel()); + arm_codegen->InvokeRuntime( + QUICK_ENTRY_POINT(pThrowDivZero), instruction_, instruction_->GetDexPc()); + } + + private: + HDivZeroCheck* const instruction_; + DISALLOW_COPY_AND_ASSIGN(DivZeroCheckSlowPathARM); +}; + class StackOverflowCheckSlowPathARM : public SlowPathCodeARM { public: StackOverflowCheckSlowPathARM() {} @@ -785,6 +801,7 @@ void CodeGeneratorARM::InvokeRuntime(int32_t entry_point_offset, DCHECK(instruction->IsSuspendCheck() || instruction->IsBoundsCheck() || instruction->IsNullCheck() + || instruction->IsDivZeroCheck() || !IsLeafMethod()); } @@ -1533,7 +1550,12 @@ void LocationsBuilderARM::VisitDiv(HDiv* div) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(div, LocationSummary::kNoCall); switch (div->GetResultType()) { - case Primitive::kPrimInt: + case Primitive::kPrimInt: { + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + break; + } case Primitive::kPrimLong: { LOG(FATAL) << "Not implemented div type" << div->GetResultType(); break; @@ -1558,7 +1580,11 @@ void InstructionCodeGeneratorARM::VisitDiv(HDiv* div) { Location second = locations->InAt(1); switch (div->GetResultType()) { - case Primitive::kPrimInt: + case Primitive::kPrimInt: { + __ sdiv(out.As<Register>(), first.As<Register>(), second.As<Register>()); + break; + } + case Primitive::kPrimLong: { LOG(FATAL) << "Not implemented div type" << div->GetResultType(); break; @@ -1581,6 +1607,27 @@ void InstructionCodeGeneratorARM::VisitDiv(HDiv* div) { } } +void LocationsBuilderARM::VisitDivZeroCheck(HDivZeroCheck* instruction) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + locations->SetInAt(0, Location::RequiresRegister()); + if (instruction->HasUses()) { + locations->SetOut(Location::SameAsFirstInput()); + } +} + +void InstructionCodeGeneratorARM::VisitDivZeroCheck(HDivZeroCheck* instruction) { + SlowPathCodeARM* slow_path = new (GetGraph()->GetArena()) DivZeroCheckSlowPathARM(instruction); + codegen_->AddSlowPath(slow_path); + + LocationSummary* locations = instruction->GetLocations(); + Location value = locations->InAt(0); + + DCHECK(value.IsRegister()) << value; + __ cmp(value.As<Register>(), ShifterOperand(0)); + __ b(slow_path->GetEntryLabel(), EQ); +} + void LocationsBuilderARM::VisitNewInstance(HNewInstance* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall); diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index 1c5db9e15b..c26b0ab472 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -537,6 +537,7 @@ InstructionCodeGeneratorARM64::InstructionCodeGeneratorARM64(HGraph* graph, M(ClinitCheck) \ M(DoubleConstant) \ M(Div) \ + M(DivZeroCheck) \ M(FloatConstant) \ M(LoadClass) \ M(LoadString) \ diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 8132e81798..b2d9187b3b 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -85,6 +85,36 @@ class NullCheckSlowPathX86 : public SlowPathCodeX86 { DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathX86); }; +class DivZeroCheckSlowPathX86 : public SlowPathCodeX86 { + public: + explicit DivZeroCheckSlowPathX86(HDivZeroCheck* instruction) : instruction_(instruction) {} + + virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + __ Bind(GetEntryLabel()); + __ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pThrowDivZero))); + codegen->RecordPcInfo(instruction_, instruction_->GetDexPc()); + } + + private: + HDivZeroCheck* const instruction_; + DISALLOW_COPY_AND_ASSIGN(DivZeroCheckSlowPathX86); +}; + +class DivMinusOneSlowPathX86 : public SlowPathCodeX86 { + public: + explicit DivMinusOneSlowPathX86(Register reg) : reg_(reg) {} + + virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + __ Bind(GetEntryLabel()); + __ negl(reg_); + __ jmp(GetExitLabel()); + } + + private: + Register reg_; + DISALLOW_COPY_AND_ASSIGN(DivMinusOneSlowPathX86); +}; + class StackOverflowCheckSlowPathX86 : public SlowPathCodeX86 { public: StackOverflowCheckSlowPathX86() {} @@ -1471,7 +1501,14 @@ void LocationsBuilderX86::VisitDiv(HDiv* div) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(div, LocationSummary::kNoCall); switch (div->GetResultType()) { - case Primitive::kPrimInt: + case Primitive::kPrimInt: { + locations->SetInAt(0, Location::RegisterLocation(EAX)); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::SameAsFirstInput()); + // Intel uses edx:eax as the dividend. + locations->AddTemp(Location::RegisterLocation(EDX)); + break; + } case Primitive::kPrimLong: { LOG(FATAL) << "Not implemented div type" << div->GetResultType(); break; @@ -1496,7 +1533,32 @@ void InstructionCodeGeneratorX86::VisitDiv(HDiv* div) { DCHECK(first.Equals(locations->Out())); switch (div->GetResultType()) { - case Primitive::kPrimInt: + case Primitive::kPrimInt: { + Register first_reg = first.As<Register>(); + Register second_reg = second.As<Register>(); + DCHECK_EQ(EAX, first_reg); + DCHECK_EQ(EDX, locations->GetTemp(0).As<Register>()); + + SlowPathCodeX86* slow_path = + new (GetGraph()->GetArena()) DivMinusOneSlowPathX86(first_reg); + codegen_->AddSlowPath(slow_path); + + // 0x80000000/-1 triggers an arithmetic exception! + // Dividing by -1 is actually negation and -0x800000000 = 0x80000000 so + // it's safe to just use negl instead of more complex comparisons. + + __ cmpl(second_reg, Immediate(-1)); + __ j(kEqual, slow_path->GetEntryLabel()); + + // edx:eax <- sign-extended of eax + __ cdq(); + // eax = quotient, edx = remainder + __ idivl(second_reg); + + __ Bind(slow_path->GetExitLabel()); + break; + } + case Primitive::kPrimLong: { LOG(FATAL) << "Not implemented div type" << div->GetResultType(); break; @@ -1517,6 +1579,36 @@ void InstructionCodeGeneratorX86::VisitDiv(HDiv* div) { } } +void LocationsBuilderX86::VisitDivZeroCheck(HDivZeroCheck* instruction) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + locations->SetInAt(0, Location::Any()); + if (instruction->HasUses()) { + locations->SetOut(Location::SameAsFirstInput()); + } +} + +void InstructionCodeGeneratorX86::VisitDivZeroCheck(HDivZeroCheck* instruction) { + SlowPathCodeX86* slow_path = new (GetGraph()->GetArena()) DivZeroCheckSlowPathX86(instruction); + codegen_->AddSlowPath(slow_path); + + LocationSummary* locations = instruction->GetLocations(); + Location value = locations->InAt(0); + + if (value.IsRegister()) { + __ testl(value.As<Register>(), value.As<Register>()); + } else if (value.IsStackSlot()) { + __ cmpl(Address(ESP, value.GetStackIndex()), Immediate(0)); + } else { + DCHECK(value.IsConstant()) << value; + if (value.GetConstant()->AsIntConstant()->GetValue() == 0) { + __ jmp(slow_path->GetEntryLabel()); + } + return; + } + __ j(kEqual, slow_path->GetEntryLabel()); +} + void LocationsBuilderX86::VisitNewInstance(HNewInstance* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall); diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index d9eb17c0e9..2bd76c12b4 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -90,6 +90,37 @@ class NullCheckSlowPathX86_64 : public SlowPathCodeX86_64 { DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathX86_64); }; +class DivZeroCheckSlowPathX86_64 : public SlowPathCodeX86_64 { + public: + explicit DivZeroCheckSlowPathX86_64(HDivZeroCheck* instruction) : instruction_(instruction) {} + + virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + __ Bind(GetEntryLabel()); + __ gs()->call( + Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pThrowDivZero), true)); + codegen->RecordPcInfo(instruction_, instruction_->GetDexPc()); + } + + private: + HDivZeroCheck* const instruction_; + DISALLOW_COPY_AND_ASSIGN(DivZeroCheckSlowPathX86_64); +}; + +class DivMinusOneSlowPathX86_64 : public SlowPathCodeX86_64 { + public: + explicit DivMinusOneSlowPathX86_64(Register reg) : reg_(reg) {} + + virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + __ Bind(GetEntryLabel()); + __ negl(CpuRegister(reg_)); + __ jmp(GetExitLabel()); + } + + private: + Register reg_; + DISALLOW_COPY_AND_ASSIGN(DivMinusOneSlowPathX86_64); +}; + class StackOverflowCheckSlowPathX86_64 : public SlowPathCodeX86_64 { public: StackOverflowCheckSlowPathX86_64() {} @@ -1396,7 +1427,14 @@ void LocationsBuilderX86_64::VisitDiv(HDiv* div) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(div, LocationSummary::kNoCall); switch (div->GetResultType()) { - case Primitive::kPrimInt: + case Primitive::kPrimInt: { + locations->SetInAt(0, Location::RegisterLocation(RAX)); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::SameAsFirstInput()); + // Intel uses edx:eax as the dividend. + locations->AddTemp(Location::RegisterLocation(RDX)); + break; + } case Primitive::kPrimLong: { LOG(FATAL) << "Not implemented div type" << div->GetResultType(); break; @@ -1421,7 +1459,32 @@ void InstructionCodeGeneratorX86_64::VisitDiv(HDiv* div) { DCHECK(first.Equals(locations->Out())); switch (div->GetResultType()) { - case Primitive::kPrimInt: + case Primitive::kPrimInt: { + CpuRegister first_reg = first.As<CpuRegister>(); + CpuRegister second_reg = second.As<CpuRegister>(); + DCHECK_EQ(RAX, first_reg.AsRegister()); + DCHECK_EQ(RDX, locations->GetTemp(0).As<CpuRegister>().AsRegister()); + + SlowPathCodeX86_64* slow_path = + new (GetGraph()->GetArena()) DivMinusOneSlowPathX86_64(first_reg.AsRegister()); + codegen_->AddSlowPath(slow_path); + + // 0x80000000/-1 triggers an arithmetic exception! + // Dividing by -1 is actually negation and -0x800000000 = 0x80000000 so + // it's safe to just use negl instead of more complex comparisons. + + __ cmpl(second_reg, Immediate(-1)); + __ j(kEqual, slow_path->GetEntryLabel()); + + // edx:eax <- sign-extended of eax + __ cdq(); + // eax = quotient, edx = remainder + __ idivl(second_reg); + + __ Bind(slow_path->GetExitLabel()); + break; + } + case Primitive::kPrimLong: { LOG(FATAL) << "Not implemented div type" << div->GetResultType(); break; @@ -1442,6 +1505,37 @@ void InstructionCodeGeneratorX86_64::VisitDiv(HDiv* div) { } } +void LocationsBuilderX86_64::VisitDivZeroCheck(HDivZeroCheck* instruction) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + locations->SetInAt(0, Location::Any()); + if (instruction->HasUses()) { + locations->SetOut(Location::SameAsFirstInput()); + } +} + +void InstructionCodeGeneratorX86_64::VisitDivZeroCheck(HDivZeroCheck* instruction) { + SlowPathCodeX86_64* slow_path = + new (GetGraph()->GetArena()) DivZeroCheckSlowPathX86_64(instruction); + codegen_->AddSlowPath(slow_path); + + LocationSummary* locations = instruction->GetLocations(); + Location value = locations->InAt(0); + + if (value.IsRegister()) { + __ testl(value.As<CpuRegister>(), value.As<CpuRegister>()); + } else if (value.IsStackSlot()) { + __ cmpl(Address(CpuRegister(RSP), value.GetStackIndex()), Immediate(0)); + } else { + DCHECK(value.IsConstant()) << value; + if (value.GetConstant()->AsIntConstant()->GetValue() == 0) { + __ jmp(slow_path->GetEntryLabel()); + } + return; + } + __ j(kEqual, slow_path->GetEntryLabel()); +} + void LocationsBuilderX86_64::VisitNewInstance(HNewInstance* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall); diff --git a/compiler/optimizing/codegen_test.cc b/compiler/optimizing/codegen_test.cc index 68fcb25036..6d4514f0ea 100644 --- a/compiler/optimizing/codegen_test.cc +++ b/compiler/optimizing/codegen_test.cc @@ -543,4 +543,13 @@ TEST(CodegenTest, MaterializedCondition2) { } } +TEST(CodegenTest, ReturnDivIntLit8) { + const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + Instruction::CONST_4 | 4 << 12 | 0 << 8, + Instruction::DIV_INT_LIT8, 3 << 8 | 0, + Instruction::RETURN); + + TestCode(data, true, 1); +} + } // namespace art diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 376e740998..6ee4dfc1eb 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -478,6 +478,7 @@ class HBasicBlock : public ArenaObject<kArenaAllocMisc> { M(Compare, BinaryOperation) \ M(Condition, BinaryOperation) \ M(Div, BinaryOperation) \ + M(DivZeroCheck, Instruction) \ M(DoubleConstant, Constant) \ M(Equal, Condition) \ M(Exit, Instruction) \ @@ -1723,6 +1724,33 @@ class HDiv : public HBinaryOperation { DISALLOW_COPY_AND_ASSIGN(HDiv); }; +class HDivZeroCheck : public HExpression<1> { + public: + HDivZeroCheck(HInstruction* value, uint32_t dex_pc) + : HExpression(value->GetType(), SideEffects::None()), dex_pc_(dex_pc) { + SetRawInputAt(0, value); + } + + bool CanBeMoved() const OVERRIDE { return true; } + + bool InstructionDataEquals(HInstruction* other) const OVERRIDE { + UNUSED(other); + return true; + } + + bool NeedsEnvironment() const OVERRIDE { return true; } + bool CanThrow() const OVERRIDE { return true; } + + uint32_t GetDexPc() const { return dex_pc_; } + + DECLARE_INSTRUCTION(DivZeroCheck); + + private: + const uint32_t dex_pc_; + + DISALLOW_COPY_AND_ASSIGN(HDivZeroCheck); +}; + // The value of a parameter in this method. Its location depends on // the calling convention. class HParameterValue : public HExpression<0> { diff --git a/compiler/optimizing/prepare_for_register_allocation.cc b/compiler/optimizing/prepare_for_register_allocation.cc index c4db840f33..7186dbe85e 100644 --- a/compiler/optimizing/prepare_for_register_allocation.cc +++ b/compiler/optimizing/prepare_for_register_allocation.cc @@ -34,6 +34,10 @@ void PrepareForRegisterAllocation::VisitNullCheck(HNullCheck* check) { check->ReplaceWith(check->InputAt(0)); } +void PrepareForRegisterAllocation::VisitDivZeroCheck(HDivZeroCheck* check) { + check->ReplaceWith(check->InputAt(0)); +} + void PrepareForRegisterAllocation::VisitBoundsCheck(HBoundsCheck* check) { check->ReplaceWith(check->InputAt(0)); } diff --git a/compiler/optimizing/prepare_for_register_allocation.h b/compiler/optimizing/prepare_for_register_allocation.h index 3e63ecb4f4..0fdb65ffe0 100644 --- a/compiler/optimizing/prepare_for_register_allocation.h +++ b/compiler/optimizing/prepare_for_register_allocation.h @@ -34,6 +34,7 @@ class PrepareForRegisterAllocation : public HGraphDelegateVisitor { private: virtual void VisitNullCheck(HNullCheck* check) OVERRIDE; + virtual void VisitDivZeroCheck(HDivZeroCheck* check) OVERRIDE; virtual void VisitBoundsCheck(HBoundsCheck* check) OVERRIDE; virtual void VisitClinitCheck(HClinitCheck* check) OVERRIDE; virtual void VisitCondition(HCondition* condition) OVERRIDE; diff --git a/compiler/optimizing/register_allocator.cc b/compiler/optimizing/register_allocator.cc index 2a9c88506d..0745f9c5da 100644 --- a/compiler/optimizing/register_allocator.cc +++ b/compiler/optimizing/register_allocator.cc @@ -257,7 +257,13 @@ void RegisterAllocator::ProcessInstruction(HInstruction* instruction) { // // The backwards walking ensures the ranges are ordered on increasing start positions. Location output = locations->Out(); - if (output.IsRegister() || output.IsFpuRegister()) { + if (output.IsUnallocated() && output.GetPolicy() == Location::kSameAsFirstInput) { + Location first = locations->InAt(0); + if (first.IsRegister() || first.IsFpuRegister()) { + current->SetFrom(position + 1); + current->SetRegister(first.reg()); + } + } else if (output.IsRegister() || output.IsFpuRegister()) { // Shift the interval's start by one to account for the blocked register. current->SetFrom(position + 1); current->SetRegister(output.reg()); @@ -1172,7 +1178,11 @@ void RegisterAllocator::Resolve() { if (location.IsUnallocated()) { if (location.GetPolicy() == Location::kSameAsFirstInput) { - locations->SetInAt(0, source); + if (locations->InAt(0).IsUnallocated()) { + locations->SetInAt(0, source); + } else { + DCHECK(locations->InAt(0).Equals(source)); + } } locations->SetOut(source); } else { diff --git a/compiler/optimizing/register_allocator_test.cc b/compiler/optimizing/register_allocator_test.cc index 6845deacb9..9b1a121fbe 100644 --- a/compiler/optimizing/register_allocator_test.cc +++ b/compiler/optimizing/register_allocator_test.cc @@ -696,4 +696,45 @@ TEST(RegisterAllocatorTest, SameAsFirstInputHint) { } } +static HGraph* BuildDiv(ArenaAllocator* allocator, + HInstruction** div) { + HGraph* graph = new (allocator) HGraph(allocator); + HBasicBlock* entry = new (allocator) HBasicBlock(graph); + graph->AddBlock(entry); + graph->SetEntryBlock(entry); + HInstruction* first = new (allocator) HParameterValue(0, Primitive::kPrimInt); + HInstruction* second = new (allocator) HParameterValue(0, Primitive::kPrimInt); + entry->AddInstruction(first); + entry->AddInstruction(second); + + HBasicBlock* block = new (allocator) HBasicBlock(graph); + graph->AddBlock(block); + entry->AddSuccessor(block); + + *div = new (allocator) HDiv(Primitive::kPrimInt, first, second); + block->AddInstruction(*div); + + block->AddInstruction(new (allocator) HExit()); + return graph; +} + +TEST(RegisterAllocatorTest, ExpectedExactInRegisterAndSameOutputHint) { + ArenaPool pool; + ArenaAllocator allocator(&pool); + HInstruction *div; + + { + HGraph* graph = BuildDiv(&allocator, &div); + x86::CodeGeneratorX86 codegen(graph); + SsaLivenessAnalysis liveness(*graph, &codegen); + liveness.Analyze(); + + RegisterAllocator register_allocator(&allocator, &codegen, liveness); + register_allocator.AllocateRegisters(); + + // div on x86 requires its first input in eax and the output be the same as the first input. + ASSERT_EQ(div->GetLiveInterval()->GetRegister(), 0); + } +} + } // namespace art diff --git a/test/417-optimizing-arith-div/src/Main.java b/test/417-optimizing-arith-div/src/Main.java index 535cafb113..5825d24dda 100644 --- a/test/417-optimizing-arith-div/src/Main.java +++ b/test/417-optimizing-arith-div/src/Main.java @@ -18,6 +18,12 @@ // it does compile the method. public class Main { + public static void expectEquals(int expected, int result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + public static void expectEquals(float expected, float result) { if (expected != result) { throw new Error("Expected: " + expected + ", found: " + result); @@ -60,15 +66,51 @@ public class Main { } } + public static void expectDivisionByZero(int value) { + try { + $opt$Div(value, 0); + throw new Error("Expected RuntimeException when dividing by 0"); + } catch (java.lang.RuntimeException e) { + } + try { + $opt$DivZero(value); + throw new Error("Expected RuntimeException when dividing by 0"); + } catch (java.lang.RuntimeException e) { + } + } public static void main(String[] args) { div(); } public static void div() { + divInt(); divFloat(); divDouble(); } + private static void divInt() { + expectEquals(2, $opt$DivLit(6)); + expectEquals(2, $opt$Div(6, 3)); + expectEquals(6, $opt$Div(6, 1)); + expectEquals(-2, $opt$Div(6, -3)); + expectEquals(1, $opt$Div(4, 3)); + expectEquals(-1, $opt$Div(4, -3)); + expectEquals(5, $opt$Div(23, 4)); + expectEquals(-5, $opt$Div(-23, 4)); + + expectEquals(-Integer.MAX_VALUE, $opt$Div(Integer.MAX_VALUE, -1)); + expectEquals(Integer.MIN_VALUE, $opt$Div(Integer.MIN_VALUE, -1)); // overflow + expectEquals(-1073741824, $opt$Div(Integer.MIN_VALUE, 2)); + + expectEquals(0, $opt$Div(0, Integer.MAX_VALUE)); + expectEquals(0, $opt$Div(0, Integer.MIN_VALUE)); + + expectDivisionByZero(0); + expectDivisionByZero(1); + expectDivisionByZero(Integer.MAX_VALUE); + expectDivisionByZero(Integer.MIN_VALUE); + } + private static void divFloat() { expectApproxEquals(1.6666666F, $opt$Div(5F, 3F)); expectApproxEquals(0F, $opt$Div(0F, 3F)); @@ -127,6 +169,19 @@ public class Main { expectEquals(Float.NEGATIVE_INFINITY, $opt$Div(-Float.MAX_VALUE, Float.MIN_VALUE)); } + static int $opt$Div(int a, int b) { + return a / b; + } + + static int $opt$DivZero(int a) { + return a / 0; + } + + // Division by literals != 0 should not generate checks. + static int $opt$DivLit(int a) { + return a / 3; + } + static float $opt$Div(float a, float b) { return a / b; } |