diff options
author | Nicolas Geoffray <ngeoffray@google.com> | 2014-07-23 16:04:16 +0100 |
---|---|---|
committer | Nicolas Geoffray <ngeoffray@google.com> | 2014-07-28 15:44:28 +0100 |
commit | 3c7bb98698f77af10372cf31824d3bb115d9bf0f (patch) | |
tree | 1cd4cc18babfbb16ab908f23929fa88d7678f06b /compiler | |
parent | 98cc1e552c2ccbe5d51bc81d49e79119280f5416 (diff) | |
download | art-3c7bb98698f77af10372cf31824d3bb115d9bf0f.zip art-3c7bb98698f77af10372cf31824d3bb115d9bf0f.tar.gz art-3c7bb98698f77af10372cf31824d3bb115d9bf0f.tar.bz2 |
Implement array get and array put in optimizing.
Also fix a couple of assembler/disassembler issues.
Change-Id: I705c8572988c1a9c4df3172b304678529636d5f6
Diffstat (limited to 'compiler')
-rw-r--r-- | compiler/optimizing/builder.cc | 53 | ||||
-rw-r--r-- | compiler/optimizing/builder.h | 4 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_arm.cc | 329 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_arm.h | 5 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_x86.cc | 335 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_x86.h | 5 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_x86_64.cc | 325 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_x86_64.h | 5 | ||||
-rw-r--r-- | compiler/optimizing/nodes.h | 78 | ||||
-rw-r--r-- | compiler/utils/arm/assembler_thumb2.cc | 4 | ||||
-rw-r--r-- | compiler/utils/assembler_thumb_test.cc | 6 | ||||
-rw-r--r-- | compiler/utils/assembler_thumb_test_expected.cc.inc | 4 |
12 files changed, 1047 insertions, 106 deletions
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc index 1f0b361..43e6b83 100644 --- a/compiler/optimizing/builder.cc +++ b/compiler/optimizing/builder.cc @@ -425,6 +425,41 @@ bool HGraphBuilder::BuildFieldAccess(const Instruction& instruction, return true; } +void HGraphBuilder::BuildArrayAccess(const Instruction& instruction, + uint32_t dex_offset, + bool is_put, + Primitive::Type anticipated_type) { + uint8_t source_or_dest_reg = instruction.VRegA_23x(); + uint8_t array_reg = instruction.VRegB_23x(); + uint8_t index_reg = instruction.VRegC_23x(); + + DCHECK(IsTypeSupported(anticipated_type)); + + // We need one temporary for the null check, one for the index, and one for the length. + Temporaries temps(graph_, 3); + + HInstruction* object = LoadLocal(array_reg, Primitive::kPrimNot); + object = new (arena_) HNullCheck(object, dex_offset); + current_block_->AddInstruction(object); + temps.Add(object); + + HInstruction* length = new (arena_) HArrayLength(object); + current_block_->AddInstruction(length); + temps.Add(length); + HInstruction* index = LoadLocal(index_reg, Primitive::kPrimInt); + index = new (arena_) HBoundsCheck(index, length, dex_offset); + current_block_->AddInstruction(index); + temps.Add(index); + if (is_put) { + HInstruction* value = LoadLocal(source_or_dest_reg, anticipated_type); + // TODO: Insert a type check node if the type is Object. + current_block_->AddInstruction(new (arena_) HArraySet(object, index, value, dex_offset)); + } else { + current_block_->AddInstruction(new (arena_) HArrayGet(object, index, anticipated_type)); + UpdateLocal(source_or_dest_reg, current_block_->GetLastInstruction()); + } +} + bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, int32_t dex_offset) { if (current_block_ == nullptr) { return true; // Dead code @@ -697,6 +732,24 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, int32_ break; } +#define ARRAY_XX(kind, anticipated_type) \ + case Instruction::AGET##kind: { \ + BuildArrayAccess(instruction, dex_offset, false, anticipated_type); \ + break; \ + } \ + case Instruction::APUT##kind: { \ + BuildArrayAccess(instruction, dex_offset, true, anticipated_type); \ + break; \ + } + + ARRAY_XX(, Primitive::kPrimInt); + ARRAY_XX(_WIDE, Primitive::kPrimLong); + ARRAY_XX(_OBJECT, Primitive::kPrimNot); + ARRAY_XX(_BOOLEAN, Primitive::kPrimBoolean); + ARRAY_XX(_BYTE, Primitive::kPrimByte); + ARRAY_XX(_CHAR, Primitive::kPrimChar); + ARRAY_XX(_SHORT, Primitive::kPrimShort); + default: return false; } diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h index f94b8e8..170c427 100644 --- a/compiler/optimizing/builder.h +++ b/compiler/optimizing/builder.h @@ -93,6 +93,10 @@ class HGraphBuilder : public ValueObject { void BuildReturn(const Instruction& instruction, Primitive::Type type); bool BuildFieldAccess(const Instruction& instruction, uint32_t dex_offset, bool is_get); + void BuildArrayAccess(const Instruction& instruction, + uint32_t dex_offset, + bool is_get, + Primitive::Type anticipated_type); // Builds an invocation node and returns whether the instruction is supported. bool BuildInvoke(const Instruction& instruction, diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index 90ec6cf..eccc970 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -39,6 +39,24 @@ static constexpr bool kExplicitStackOverflowCheck = false; static constexpr int kNumberOfPushedRegistersAtEntry = 1 + 2; // LR, R6, R7 static constexpr int kCurrentMethodStackOffset = 0; +static Location ArmCoreLocation(Register reg) { + return Location::RegisterLocation(ArmManagedRegister::FromCoreRegister(reg)); +} + +static constexpr Register kRuntimeParameterCoreRegisters[] = { R0, R1, R2 }; +static constexpr size_t kRuntimeParameterCoreRegistersLength = + arraysize(kRuntimeParameterCoreRegisters); + +class InvokeRuntimeCallingConvention : public CallingConvention<Register> { + public: + InvokeRuntimeCallingConvention() + : CallingConvention(kRuntimeParameterCoreRegisters, + kRuntimeParameterCoreRegistersLength) {} + + private: + DISALLOW_COPY_AND_ASSIGN(InvokeRuntimeCallingConvention); +}; + #define __ reinterpret_cast<ArmAssembler*>(codegen->GetAssembler())-> class NullCheckSlowPathARM : public SlowPathCode { @@ -72,6 +90,33 @@ class StackOverflowCheckSlowPathARM : public SlowPathCode { DISALLOW_COPY_AND_ASSIGN(StackOverflowCheckSlowPathARM); }; +class BoundsCheckSlowPathARM : public SlowPathCode { + public: + explicit BoundsCheckSlowPathARM(uint32_t dex_pc, + Location index_location, + Location length_location) + : dex_pc_(dex_pc), index_location_(index_location), length_location_(length_location) {} + + virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + CodeGeneratorARM* arm_codegen = reinterpret_cast<CodeGeneratorARM*>(codegen); + __ Bind(GetEntryLabel()); + InvokeRuntimeCallingConvention calling_convention; + arm_codegen->Move32(ArmCoreLocation(calling_convention.GetRegisterAt(0)), index_location_); + arm_codegen->Move32(ArmCoreLocation(calling_convention.GetRegisterAt(1)), length_location_); + int32_t offset = QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pThrowArrayBounds).Int32Value(); + __ ldr(LR, Address(TR, offset)); + __ blx(LR); + codegen->RecordPcInfo(dex_pc_); + } + + private: + const uint32_t dex_pc_; + const Location index_location_; + const Location length_location_; + + DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathARM); +}; + #undef __ #define __ reinterpret_cast<ArmAssembler*>(GetAssembler())-> @@ -214,10 +259,6 @@ size_t CodeGeneratorARM::GetNumberOfRegisters() const { return kNumberOfRegIds; } -static Location ArmCoreLocation(Register reg) { - return Location::RegisterLocation(ArmManagedRegister::FromCoreRegister(reg)); -} - InstructionCodeGeneratorARM::InstructionCodeGeneratorARM(HGraph* graph, CodeGeneratorARM* codegen) : HGraphVisitor(graph), assembler_(codegen->GetAssembler()), @@ -943,20 +984,6 @@ void InstructionCodeGeneratorARM::VisitSub(HSub* sub) { } } -static constexpr Register kRuntimeParameterCoreRegisters[] = { R0, R1 }; -static constexpr size_t kRuntimeParameterCoreRegistersLength = - arraysize(kRuntimeParameterCoreRegisters); - -class InvokeRuntimeCallingConvention : public CallingConvention<Register> { - public: - InvokeRuntimeCallingConvention() - : CallingConvention(kRuntimeParameterCoreRegisters, - kRuntimeParameterCoreRegistersLength) {} - - private: - DISALLOW_COPY_AND_ASSIGN(InvokeRuntimeCallingConvention); -}; - void LocationsBuilderARM::VisitNewInstance(HNewInstance* instruction) { codegen_->MarkNotLeaf(); LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); @@ -1099,24 +1126,15 @@ void InstructionCodeGeneratorARM::VisitInstanceFieldSet(HInstanceFieldSet* instr break; } - case Primitive::kPrimInt: { - Register value = locations->InAt(1).AsArm().AsCoreRegister(); - __ StoreToOffset(kStoreWord, value, obj, offset); - break; - } - + case Primitive::kPrimInt: case Primitive::kPrimNot: { Register value = locations->InAt(1).AsArm().AsCoreRegister(); __ StoreToOffset(kStoreWord, value, obj, offset); - - Register temp = locations->GetTemp(0).AsArm().AsCoreRegister(); - Register card = locations->GetTemp(1).AsArm().AsCoreRegister(); - Label is_null; - __ CompareAndBranchIfZero(value, &is_null); - __ LoadFromOffset(kLoadWord, card, TR, Thread::CardTableOffset<kArmWordSize>().Int32Value()); - __ Lsr(temp, obj, gc::accounting::CardTable::kCardShift); - __ strb(card, Address(card, temp)); - __ Bind(&is_null); + if (field_type == Primitive::kPrimNot) { + Register temp = locations->GetTemp(0).AsArm().AsCoreRegister(); + Register card = locations->GetTemp(1).AsArm().AsCoreRegister(); + codegen_->MarkGCCard(temp, card, obj, value); + } break; } @@ -1218,6 +1236,251 @@ void InstructionCodeGeneratorARM::VisitNullCheck(HNullCheck* instruction) { __ b(slow_path->GetEntryLabel(), EQ); } +void LocationsBuilderARM::VisitArrayGet(HArrayGet* instruction) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1))); + locations->SetOut(Location::RequiresRegister()); + instruction->SetLocations(locations); +} + +void InstructionCodeGeneratorARM::VisitArrayGet(HArrayGet* instruction) { + LocationSummary* locations = instruction->GetLocations(); + Register obj = locations->InAt(0).AsArm().AsCoreRegister(); + Location index = locations->InAt(1); + + switch (instruction->GetType()) { + case Primitive::kPrimBoolean: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value(); + Register out = locations->Out().AsArm().AsCoreRegister(); + if (index.IsConstant()) { + size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset; + __ LoadFromOffset(kLoadUnsignedByte, out, obj, offset); + } else { + __ add(IP, obj, ShifterOperand(index.AsArm().AsCoreRegister())); + __ LoadFromOffset(kLoadUnsignedByte, out, IP, data_offset); + } + break; + } + + case Primitive::kPrimByte: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(int8_t)).Uint32Value(); + Register out = locations->Out().AsArm().AsCoreRegister(); + if (index.IsConstant()) { + size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset; + __ LoadFromOffset(kLoadSignedByte, out, obj, offset); + } else { + __ add(IP, obj, ShifterOperand(index.AsArm().AsCoreRegister())); + __ LoadFromOffset(kLoadSignedByte, out, IP, data_offset); + } + break; + } + + case Primitive::kPrimShort: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(int16_t)).Uint32Value(); + Register out = locations->Out().AsArm().AsCoreRegister(); + if (index.IsConstant()) { + size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset; + __ LoadFromOffset(kLoadSignedHalfword, out, obj, offset); + } else { + __ add(IP, obj, ShifterOperand(index.AsArm().AsCoreRegister(), LSL, TIMES_2)); + __ LoadFromOffset(kLoadSignedHalfword, out, IP, data_offset); + } + break; + } + + case Primitive::kPrimChar: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value(); + Register out = locations->Out().AsArm().AsCoreRegister(); + if (index.IsConstant()) { + size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset; + __ LoadFromOffset(kLoadUnsignedHalfword, out, obj, offset); + } else { + __ add(IP, obj, ShifterOperand(index.AsArm().AsCoreRegister(), LSL, TIMES_2)); + __ LoadFromOffset(kLoadUnsignedHalfword, out, IP, data_offset); + } + break; + } + + case Primitive::kPrimInt: + case Primitive::kPrimNot: { + DCHECK_EQ(sizeof(mirror::HeapReference<mirror::Object>), sizeof(int32_t)); + uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value(); + Register out = locations->Out().AsArm().AsCoreRegister(); + if (index.IsConstant()) { + size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; + __ LoadFromOffset(kLoadWord, out, obj, offset); + } else { + __ add(IP, obj, ShifterOperand(index.AsArm().AsCoreRegister(), LSL, TIMES_4)); + __ LoadFromOffset(kLoadWord, out, IP, data_offset); + } + break; + } + + case Primitive::kPrimLong: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value(); + ArmManagedRegister out = locations->Out().AsArm(); + if (index.IsConstant()) { + size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; + __ LoadFromOffset(kLoadWordPair, out.AsRegisterPairLow(), obj, offset); + } else { + __ add(IP, obj, ShifterOperand(index.AsArm().AsCoreRegister(), LSL, TIMES_8)); + __ LoadFromOffset(kLoadWordPair, out.AsRegisterPairLow(), IP, data_offset); + } + break; + } + + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + LOG(FATAL) << "Unimplemented register type " << instruction->GetType(); + + case Primitive::kPrimVoid: + LOG(FATAL) << "Unreachable type " << instruction->GetType(); + } +} + +void LocationsBuilderARM::VisitArraySet(HArraySet* instruction) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); + Primitive::Type value_type = instruction->InputAt(2)->GetType(); + if (value_type == Primitive::kPrimNot) { + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, ArmCoreLocation(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, ArmCoreLocation(calling_convention.GetRegisterAt(1))); + locations->SetInAt(2, ArmCoreLocation(calling_convention.GetRegisterAt(2))); + codegen_->MarkNotLeaf(); + } else { + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1))); + locations->SetInAt(2, Location::RequiresRegister()); + } + instruction->SetLocations(locations); +} + +void InstructionCodeGeneratorARM::VisitArraySet(HArraySet* instruction) { + LocationSummary* locations = instruction->GetLocations(); + Register obj = locations->InAt(0).AsArm().AsCoreRegister(); + Location index = locations->InAt(1); + Primitive::Type value_type = instruction->InputAt(2)->GetType(); + + switch (value_type) { + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value(); + Register value = locations->InAt(2).AsArm().AsCoreRegister(); + if (index.IsConstant()) { + size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset; + __ StoreToOffset(kStoreByte, value, obj, offset); + } else { + __ add(IP, obj, ShifterOperand(index.AsArm().AsCoreRegister())); + __ StoreToOffset(kStoreByte, value, IP, data_offset); + } + break; + } + + case Primitive::kPrimShort: + case Primitive::kPrimChar: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value(); + Register value = locations->InAt(2).AsArm().AsCoreRegister(); + if (index.IsConstant()) { + size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset; + __ StoreToOffset(kStoreHalfword, value, obj, offset); + } else { + __ add(IP, obj, ShifterOperand(index.AsArm().AsCoreRegister(), LSL, TIMES_2)); + __ StoreToOffset(kStoreHalfword, value, IP, data_offset); + } + break; + } + + case Primitive::kPrimInt: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value(); + Register value = locations->InAt(2).AsArm().AsCoreRegister(); + if (index.IsConstant()) { + size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; + __ StoreToOffset(kStoreWord, value, obj, offset); + } else { + __ add(IP, obj, ShifterOperand(index.AsArm().AsCoreRegister(), LSL, TIMES_4)); + __ StoreToOffset(kStoreWord, value, IP, data_offset); + } + break; + } + + case Primitive::kPrimNot: { + int32_t offset = QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pAputObject).Int32Value(); + __ ldr(LR, Address(TR, offset)); + __ blx(LR); + codegen_->RecordPcInfo(instruction->GetDexPc()); + DCHECK(!codegen_->IsLeafMethod()); + break; + } + + case Primitive::kPrimLong: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value(); + ArmManagedRegister value = locations->InAt(2).AsArm(); + if (index.IsConstant()) { + size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; + __ StoreToOffset(kStoreWordPair, value.AsRegisterPairLow(), obj, offset); + } else { + __ add(IP, obj, ShifterOperand(index.AsArm().AsCoreRegister(), LSL, TIMES_8)); + __ StoreToOffset(kStoreWordPair, value.AsRegisterPairLow(), IP, data_offset); + } + break; + } + + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + LOG(FATAL) << "Unimplemented register type " << instruction->GetType(); + + case Primitive::kPrimVoid: + LOG(FATAL) << "Unreachable type " << instruction->GetType(); + } +} + +void LocationsBuilderARM::VisitArrayLength(HArrayLength* instruction) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister()); + instruction->SetLocations(locations); +} + +void InstructionCodeGeneratorARM::VisitArrayLength(HArrayLength* instruction) { + LocationSummary* locations = instruction->GetLocations(); + uint32_t offset = mirror::Array::LengthOffset().Uint32Value(); + Register obj = locations->InAt(0).AsArm().AsCoreRegister(); + Register out = locations->Out().AsArm().AsCoreRegister(); + __ LoadFromOffset(kLoadWord, out, obj, offset); +} + +void LocationsBuilderARM::VisitBoundsCheck(HBoundsCheck* instruction) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + // TODO: Have a normalization phase that makes this instruction never used. + locations->SetOut(Location::SameAsFirstInput()); + instruction->SetLocations(locations); +} + +void InstructionCodeGeneratorARM::VisitBoundsCheck(HBoundsCheck* instruction) { + LocationSummary* locations = instruction->GetLocations(); + SlowPathCode* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathARM( + instruction->GetDexPc(), locations->InAt(0), locations->InAt(1)); + codegen_->AddSlowPath(slow_path); + + Register index = locations->InAt(0).AsArm().AsCoreRegister(); + Register length = locations->InAt(1).AsArm().AsCoreRegister(); + + __ cmp(index, ShifterOperand(length)); + __ b(slow_path->GetEntryLabel(), CS); +} + +void CodeGeneratorARM::MarkGCCard(Register temp, Register card, Register object, Register value) { + Label is_null; + __ CompareAndBranchIfZero(value, &is_null); + __ LoadFromOffset(kLoadWord, card, TR, Thread::CardTableOffset<kArmWordSize>().Int32Value()); + __ Lsr(temp, object, gc::accounting::CardTable::kCardShift); + __ strb(card, Address(card, temp)); + __ Bind(&is_null); +} + void LocationsBuilderARM::VisitTemporary(HTemporary* temp) { temp->SetLocations(nullptr); } diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h index 85ab22b..610625c 100644 --- a/compiler/optimizing/code_generator_arm.h +++ b/compiler/optimizing/code_generator_arm.h @@ -175,12 +175,15 @@ class CodeGeneratorARM : public CodeGenerator { return InstructionSet::kThumb2; } - private: // Helper method to move a 32bits value between two locations. void Move32(Location destination, Location source); // Helper method to move a 64bits value between two locations. void Move64(Location destination, Location source); + // Emit a write barrier. + void MarkGCCard(Register temp, Register card, Register object, Register value); + + private: LocationsBuilderARM location_builder_; InstructionCodeGeneratorARM instruction_visitor_; ParallelMoveResolverARM move_resolver_; diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 251a2ad..2264638 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -39,6 +39,24 @@ static constexpr bool kExplicitStackOverflowCheck = false; static constexpr int kNumberOfPushedRegistersAtEntry = 1; static constexpr int kCurrentMethodStackOffset = 0; +static Location X86CpuLocation(Register reg) { + return Location::RegisterLocation(X86ManagedRegister::FromCpuRegister(reg)); +} + +static constexpr Register kRuntimeParameterCoreRegisters[] = { EAX, ECX, EDX }; +static constexpr size_t kRuntimeParameterCoreRegistersLength = + arraysize(kRuntimeParameterCoreRegisters); + +class InvokeRuntimeCallingConvention : public CallingConvention<Register> { + public: + InvokeRuntimeCallingConvention() + : CallingConvention(kRuntimeParameterCoreRegisters, + kRuntimeParameterCoreRegistersLength) {} + + private: + DISALLOW_COPY_AND_ASSIGN(InvokeRuntimeCallingConvention); +}; + #define __ reinterpret_cast<X86Assembler*>(codegen->GetAssembler())-> class NullCheckSlowPathX86 : public SlowPathCode { @@ -71,6 +89,31 @@ class StackOverflowCheckSlowPathX86 : public SlowPathCode { DISALLOW_COPY_AND_ASSIGN(StackOverflowCheckSlowPathX86); }; +class BoundsCheckSlowPathX86 : public SlowPathCode { + public: + explicit BoundsCheckSlowPathX86(uint32_t dex_pc, + Location index_location, + Location length_location) + : dex_pc_(dex_pc), index_location_(index_location), length_location_(length_location) {} + + virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + CodeGeneratorX86* x86_codegen = reinterpret_cast<CodeGeneratorX86*>(codegen); + __ Bind(GetEntryLabel()); + InvokeRuntimeCallingConvention calling_convention; + x86_codegen->Move32(X86CpuLocation(calling_convention.GetRegisterAt(0)), index_location_); + x86_codegen->Move32(X86CpuLocation(calling_convention.GetRegisterAt(1)), length_location_); + __ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pThrowArrayBounds))); + codegen->RecordPcInfo(dex_pc_); + } + + private: + const uint32_t dex_pc_; + const Location index_location_; + const Location length_location_; + + DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathX86); +}; + #undef __ #define __ reinterpret_cast<X86Assembler*>(GetAssembler())-> @@ -188,10 +231,6 @@ size_t CodeGeneratorX86::GetNumberOfRegisters() const { return kNumberOfRegIds; } -static Location X86CpuLocation(Register reg) { - return Location::RegisterLocation(X86ManagedRegister::FromCpuRegister(reg)); -} - InstructionCodeGeneratorX86::InstructionCodeGeneratorX86(HGraph* graph, CodeGeneratorX86* codegen) : HGraphVisitor(graph), assembler_(codegen->GetAssembler()), @@ -260,20 +299,6 @@ Location CodeGeneratorX86::GetStackLocation(HLoadLocal* load) const { return Location(); } -static constexpr Register kRuntimeParameterCoreRegisters[] = { EAX, ECX, EDX }; -static constexpr size_t kRuntimeParameterCoreRegistersLength = - arraysize(kRuntimeParameterCoreRegisters); - -class InvokeRuntimeCallingConvention : public CallingConvention<Register> { - public: - InvokeRuntimeCallingConvention() - : CallingConvention(kRuntimeParameterCoreRegisters, - kRuntimeParameterCoreRegistersLength) {} - - private: - DISALLOW_COPY_AND_ASSIGN(InvokeRuntimeCallingConvention); -}; - Location InvokeDexCallingConventionVisitor::GetNextLocation(Primitive::Type type) { switch (type) { case Primitive::kPrimBoolean: @@ -1048,7 +1073,7 @@ void LocationsBuilderX86::VisitInstanceFieldSet(HInstanceFieldSet* instruction) locations->SetInAt(1, Location::RequiresRegister()); } // Temporary registers for the write barrier. - if (instruction->InputAt(1)->GetType() == Primitive::kPrimNot) { + if (field_type == Primitive::kPrimNot) { locations->AddTemp(Location::RequiresRegister()); // Ensure the card is in a byte register. locations->AddTemp(X86CpuLocation(ECX)); @@ -1077,25 +1102,16 @@ void InstructionCodeGeneratorX86::VisitInstanceFieldSet(HInstanceFieldSet* instr break; } - case Primitive::kPrimInt: { - Register value = locations->InAt(1).AsX86().AsCpuRegister(); - __ movl(Address(obj, offset), value); - break; - } - + case Primitive::kPrimInt: case Primitive::kPrimNot: { Register value = locations->InAt(1).AsX86().AsCpuRegister(); __ movl(Address(obj, offset), value); - Label is_null; - Register temp = locations->GetTemp(0).AsX86().AsCpuRegister(); - Register card = locations->GetTemp(1).AsX86().AsCpuRegister(); - __ testl(value, value); - __ j(kEqual, &is_null); - __ fs()->movl(card, Address::Absolute(Thread::CardTableOffset<kX86WordSize>().Int32Value())); - __ movl(temp, obj); - __ shrl(temp, Immediate(gc::accounting::CardTable::kCardShift)); - __ movb(Address(temp, card, TIMES_1, 0), locations->GetTemp(1).AsX86().AsByteRegister()); - __ Bind(&is_null); + + if (field_type == Primitive::kPrimNot) { + Register temp = locations->GetTemp(0).AsX86().AsCpuRegister(); + Register card = locations->GetTemp(1).AsX86().AsCpuRegister(); + codegen_->MarkGCCard(temp, card, obj, value); + } break; } @@ -1115,6 +1131,18 @@ void InstructionCodeGeneratorX86::VisitInstanceFieldSet(HInstanceFieldSet* instr } } +void CodeGeneratorX86::MarkGCCard(Register temp, Register card, Register object, Register value) { + Label is_null; + __ testl(value, value); + __ j(kEqual, &is_null); + __ fs()->movl(card, Address::Absolute(Thread::CardTableOffset<kX86WordSize>().Int32Value())); + __ movl(temp, object); + __ shrl(temp, Immediate(gc::accounting::CardTable::kCardShift)); + __ movb(Address(temp, card, TIMES_1, 0), + X86ManagedRegister::FromCpuRegister(card).AsByteRegister()); + __ Bind(&is_null); +} + void LocationsBuilderX86::VisitInstanceFieldGet(HInstanceFieldGet* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); locations->SetInAt(0, Location::RequiresRegister()); @@ -1202,6 +1230,243 @@ void InstructionCodeGeneratorX86::VisitNullCheck(HNullCheck* instruction) { __ j(kEqual, slow_path->GetEntryLabel()); } +void LocationsBuilderX86::VisitArrayGet(HArrayGet* instruction) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1))); + locations->SetOut(Location::RequiresRegister()); + instruction->SetLocations(locations); +} + +void InstructionCodeGeneratorX86::VisitArrayGet(HArrayGet* instruction) { + LocationSummary* locations = instruction->GetLocations(); + Register obj = locations->InAt(0).AsX86().AsCpuRegister(); + Location index = locations->InAt(1); + + switch (instruction->GetType()) { + case Primitive::kPrimBoolean: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value(); + Register out = locations->Out().AsX86().AsCpuRegister(); + if (index.IsConstant()) { + __ movzxb(out, Address(obj, + (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset)); + } else { + __ movzxb(out, Address(obj, index.AsX86().AsCpuRegister(), TIMES_1, data_offset)); + } + break; + } + + case Primitive::kPrimByte: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(int8_t)).Uint32Value(); + Register out = locations->Out().AsX86().AsCpuRegister(); + if (index.IsConstant()) { + __ movsxb(out, Address(obj, + (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset)); + } else { + __ movsxb(out, Address(obj, index.AsX86().AsCpuRegister(), TIMES_1, data_offset)); + } + break; + } + + case Primitive::kPrimShort: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(int16_t)).Uint32Value(); + Register out = locations->Out().AsX86().AsCpuRegister(); + if (index.IsConstant()) { + __ movsxw(out, Address(obj, + (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset)); + } else { + __ movsxw(out, Address(obj, index.AsX86().AsCpuRegister(), TIMES_2, data_offset)); + } + break; + } + + case Primitive::kPrimChar: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value(); + Register out = locations->Out().AsX86().AsCpuRegister(); + if (index.IsConstant()) { + __ movzxw(out, Address(obj, + (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset)); + } else { + __ movzxw(out, Address(obj, index.AsX86().AsCpuRegister(), TIMES_2, data_offset)); + } + break; + } + + case Primitive::kPrimInt: + case Primitive::kPrimNot: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value(); + Register out = locations->Out().AsX86().AsCpuRegister(); + if (index.IsConstant()) { + __ movl(out, Address(obj, + (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset)); + } else { + __ movl(out, Address(obj, index.AsX86().AsCpuRegister(), TIMES_4, data_offset)); + } + break; + } + + case Primitive::kPrimLong: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value(); + X86ManagedRegister out = locations->Out().AsX86(); + if (index.IsConstant()) { + size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; + __ movl(out.AsRegisterPairLow(), Address(obj, offset)); + __ movl(out.AsRegisterPairHigh(), Address(obj, offset + kX86WordSize)); + } else { + __ movl(out.AsRegisterPairLow(), + Address(obj, index.AsX86().AsCpuRegister(), TIMES_8, data_offset)); + __ movl(out.AsRegisterPairHigh(), + Address(obj, index.AsX86().AsCpuRegister(), TIMES_8, data_offset + kX86WordSize)); + } + break; + } + + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + LOG(FATAL) << "Unimplemented register type " << instruction->GetType(); + + case Primitive::kPrimVoid: + LOG(FATAL) << "Unreachable type " << instruction->GetType(); + } +} + +void LocationsBuilderX86::VisitArraySet(HArraySet* instruction) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); + Primitive::Type value_type = instruction->InputAt(2)->GetType(); + if (value_type == Primitive::kPrimNot) { + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, X86CpuLocation(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, X86CpuLocation(calling_convention.GetRegisterAt(1))); + locations->SetInAt(2, X86CpuLocation(calling_convention.GetRegisterAt(2))); + codegen_->MarkNotLeaf(); + } else { + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1))); + if (value_type == Primitive::kPrimBoolean || value_type == Primitive::kPrimByte) { + // Ensure the value is in a byte register. + locations->SetInAt(2, X86CpuLocation(EAX)); + } else { + locations->SetInAt(2, Location::RequiresRegister()); + } + } + + instruction->SetLocations(locations); +} + +void InstructionCodeGeneratorX86::VisitArraySet(HArraySet* instruction) { + LocationSummary* locations = instruction->GetLocations(); + Register obj = locations->InAt(0).AsX86().AsCpuRegister(); + Location index = locations->InAt(1); + Primitive::Type value_type = instruction->InputAt(2)->GetType(); + + switch (value_type) { + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value(); + ByteRegister value = locations->InAt(2).AsX86().AsByteRegister(); + if (index.IsConstant()) { + size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset; + __ movb(Address(obj, offset), value); + } else { + __ movb(Address(obj, index.AsX86().AsCpuRegister(), TIMES_1, data_offset), value); + } + break; + } + + case Primitive::kPrimShort: + case Primitive::kPrimChar: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value(); + Register value = locations->InAt(2).AsX86().AsCpuRegister(); + if (index.IsConstant()) { + size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset; + __ movw(Address(obj, offset), value); + } else { + __ movw(Address(obj, index.AsX86().AsCpuRegister(), TIMES_2, data_offset), value); + } + break; + } + + case Primitive::kPrimInt: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value(); + Register value = locations->InAt(2).AsX86().AsCpuRegister(); + if (index.IsConstant()) { + size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; + __ movl(Address(obj, offset), value); + } else { + __ movl(Address(obj, index.AsX86().AsCpuRegister(), TIMES_4, data_offset), value); + } + break; + } + + case Primitive::kPrimNot: { + DCHECK(!codegen_->IsLeafMethod()); + __ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pAputObject))); + codegen_->RecordPcInfo(instruction->GetDexPc()); + break; + } + + case Primitive::kPrimLong: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value(); + X86ManagedRegister value = locations->InAt(2).AsX86(); + if (index.IsConstant()) { + size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; + __ movl(Address(obj, offset), value.AsRegisterPairLow()); + __ movl(Address(obj, offset + kX86WordSize), value.AsRegisterPairHigh()); + } else { + __ movl(Address(obj, index.AsX86().AsCpuRegister(), TIMES_8, data_offset), + value.AsRegisterPairLow()); + __ movl(Address(obj, index.AsX86().AsCpuRegister(), TIMES_8, data_offset + kX86WordSize), + value.AsRegisterPairHigh()); + } + break; + } + + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + LOG(FATAL) << "Unimplemented register type " << instruction->GetType(); + + case Primitive::kPrimVoid: + LOG(FATAL) << "Unreachable type " << instruction->GetType(); + } +} + +void LocationsBuilderX86::VisitArrayLength(HArrayLength* instruction) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister()); + instruction->SetLocations(locations); +} + +void InstructionCodeGeneratorX86::VisitArrayLength(HArrayLength* instruction) { + LocationSummary* locations = instruction->GetLocations(); + uint32_t offset = mirror::Array::LengthOffset().Uint32Value(); + Register obj = locations->InAt(0).AsX86().AsCpuRegister(); + Register out = locations->Out().AsX86().AsCpuRegister(); + __ movl(out, Address(obj, offset)); +} + +void LocationsBuilderX86::VisitBoundsCheck(HBoundsCheck* instruction) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + // TODO: Have a normalization phase that makes this instruction never used. + locations->SetOut(Location::SameAsFirstInput()); + instruction->SetLocations(locations); +} + +void InstructionCodeGeneratorX86::VisitBoundsCheck(HBoundsCheck* instruction) { + LocationSummary* locations = instruction->GetLocations(); + SlowPathCode* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathX86( + instruction->GetDexPc(), locations->InAt(0), locations->InAt(1)); + codegen_->AddSlowPath(slow_path); + + Register index = locations->InAt(0).AsX86().AsCpuRegister(); + Register length = locations->InAt(1).AsX86().AsCpuRegister(); + + __ cmpl(index, length); + __ j(kAboveEqual, slow_path->GetEntryLabel()); +} + void LocationsBuilderX86::VisitTemporary(HTemporary* temp) { temp->SetLocations(nullptr); } diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h index b7c2ad8..7c50204 100644 --- a/compiler/optimizing/code_generator_x86.h +++ b/compiler/optimizing/code_generator_x86.h @@ -177,12 +177,15 @@ class CodeGeneratorX86 : public CodeGenerator { return InstructionSet::kX86; } - private: // Helper method to move a 32bits value between two locations. void Move32(Location destination, Location source); // Helper method to move a 64bits value between two locations. void Move64(Location destination, Location source); + // Emit a write barrier. + void MarkGCCard(Register temp, Register card, Register object, Register value); + + private: LocationsBuilderX86 location_builder_; InstructionCodeGeneratorX86 instruction_visitor_; ParallelMoveResolverX86 move_resolver_; diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index 641e8e1..2ff2a17 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -43,6 +43,23 @@ static constexpr Register TMP = R11; static constexpr int kNumberOfPushedRegistersAtEntry = 1; static constexpr int kCurrentMethodStackOffset = 0; +static Location X86_64CpuLocation(Register reg) { + return Location::RegisterLocation(X86_64ManagedRegister::FromCpuRegister(reg)); +} + +static constexpr Register kRuntimeParameterCoreRegisters[] = { RDI, RSI, RDX }; +static constexpr size_t kRuntimeParameterCoreRegistersLength = + arraysize(kRuntimeParameterCoreRegisters); + +class InvokeRuntimeCallingConvention : public CallingConvention<Register> { + public: + InvokeRuntimeCallingConvention() + : CallingConvention(kRuntimeParameterCoreRegisters, + kRuntimeParameterCoreRegistersLength) {} + + private: + DISALLOW_COPY_AND_ASSIGN(InvokeRuntimeCallingConvention); +}; #define __ reinterpret_cast<X86_64Assembler*>(codegen->GetAssembler())-> @@ -78,6 +95,32 @@ class StackOverflowCheckSlowPathX86_64 : public SlowPathCode { DISALLOW_COPY_AND_ASSIGN(StackOverflowCheckSlowPathX86_64); }; +class BoundsCheckSlowPathX86_64 : public SlowPathCode { + public: + explicit BoundsCheckSlowPathX86_64(uint32_t dex_pc, + Location index_location, + Location length_location) + : dex_pc_(dex_pc), index_location_(index_location), length_location_(length_location) {} + + virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + CodeGeneratorX86_64* x64_codegen = reinterpret_cast<CodeGeneratorX86_64*>(codegen); + __ Bind(GetEntryLabel()); + InvokeRuntimeCallingConvention calling_convention; + x64_codegen->Move(X86_64CpuLocation(calling_convention.GetRegisterAt(0)), index_location_); + x64_codegen->Move(X86_64CpuLocation(calling_convention.GetRegisterAt(1)), length_location_); + __ gs()->call(Address::Absolute( + QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pThrowArrayBounds), true)); + codegen->RecordPcInfo(dex_pc_); + } + + private: + const uint32_t dex_pc_; + const Location index_location_; + const Location length_location_; + + DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathX86_64); +}; + #undef __ #define __ reinterpret_cast<X86_64Assembler*>(GetAssembler())-> @@ -103,10 +146,6 @@ void CodeGeneratorX86_64::DumpFloatingPointRegister(std::ostream& stream, int re stream << X86_64ManagedRegister::FromXmmRegister(FloatRegister(reg)); } -static Location X86_64CpuLocation(Register reg) { - return Location::RegisterLocation(X86_64ManagedRegister::FromCpuRegister(reg)); -} - CodeGeneratorX86_64::CodeGeneratorX86_64(HGraph* graph) : CodeGenerator(graph, kNumberOfRegIds), location_builder_(graph, this), @@ -117,7 +156,8 @@ size_t CodeGeneratorX86_64::FrameEntrySpillSize() const { return kNumberOfPushedRegistersAtEntry * kX86_64WordSize; } -InstructionCodeGeneratorX86_64::InstructionCodeGeneratorX86_64(HGraph* graph, CodeGeneratorX86_64* codegen) +InstructionCodeGeneratorX86_64::InstructionCodeGeneratorX86_64(HGraph* graph, + CodeGeneratorX86_64* codegen) : HGraphVisitor(graph), assembler_(codegen->GetAssembler()), codegen_(codegen) {} @@ -603,20 +643,6 @@ void InstructionCodeGeneratorX86_64::VisitReturn(HReturn* ret) { __ ret(); } -static constexpr Register kRuntimeParameterCoreRegisters[] = { RDI, RSI, RDX }; -static constexpr size_t kRuntimeParameterCoreRegistersLength = - arraysize(kRuntimeParameterCoreRegisters); - -class InvokeRuntimeCallingConvention : public CallingConvention<Register> { - public: - InvokeRuntimeCallingConvention() - : CallingConvention(kRuntimeParameterCoreRegisters, - kRuntimeParameterCoreRegistersLength) {} - - private: - DISALLOW_COPY_AND_ASSIGN(InvokeRuntimeCallingConvention); -}; - Location InvokeDexCallingConventionVisitor::GetNextLocation(Primitive::Type type) { switch (type) { case Primitive::kPrimBoolean: @@ -944,24 +970,14 @@ void InstructionCodeGeneratorX86_64::VisitInstanceFieldSet(HInstanceFieldSet* in break; } - case Primitive::kPrimInt: { - __ movl(Address(obj, offset), value); - break; - } - + case Primitive::kPrimInt: case Primitive::kPrimNot: { __ movl(Address(obj, offset), value); - Label is_null; - CpuRegister temp = locations->GetTemp(0).AsX86_64().AsCpuRegister(); - CpuRegister card = locations->GetTemp(1).AsX86_64().AsCpuRegister(); - __ testl(value, value); - __ j(kEqual, &is_null); - __ gs()->movq(card, Address::Absolute( - Thread::CardTableOffset<kX86_64WordSize>().Int32Value(), true)); - __ movq(temp, obj); - __ shrq(temp, Immediate(gc::accounting::CardTable::kCardShift)); - __ movb(Address(temp, card, TIMES_1, 0), card); - __ Bind(&is_null); + if (field_type == Primitive::kPrimNot) { + CpuRegister temp = locations->GetTemp(0).AsX86_64().AsCpuRegister(); + CpuRegister card = locations->GetTemp(1).AsX86_64().AsCpuRegister(); + codegen_->MarkGCCard(temp, card, obj, value); + } break; } @@ -1059,6 +1075,245 @@ void InstructionCodeGeneratorX86_64::VisitNullCheck(HNullCheck* instruction) { __ j(kEqual, slow_path->GetEntryLabel()); } +void LocationsBuilderX86_64::VisitArrayGet(HArrayGet* instruction) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1))); + locations->SetOut(Location::RequiresRegister()); + instruction->SetLocations(locations); +} + +void InstructionCodeGeneratorX86_64::VisitArrayGet(HArrayGet* instruction) { + LocationSummary* locations = instruction->GetLocations(); + CpuRegister obj = locations->InAt(0).AsX86_64().AsCpuRegister(); + Location index = locations->InAt(1); + + switch (instruction->GetType()) { + case Primitive::kPrimBoolean: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value(); + CpuRegister out = locations->Out().AsX86_64().AsCpuRegister(); + if (index.IsConstant()) { + __ movzxb(out, Address(obj, + (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset)); + } else { + __ movzxb(out, Address(obj, index.AsX86_64().AsCpuRegister(), TIMES_1, data_offset)); + } + break; + } + + case Primitive::kPrimByte: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(int8_t)).Uint32Value(); + CpuRegister out = locations->Out().AsX86_64().AsCpuRegister(); + if (index.IsConstant()) { + __ movsxb(out, Address(obj, + (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset)); + } else { + __ movsxb(out, Address(obj, index.AsX86_64().AsCpuRegister(), TIMES_1, data_offset)); + } + break; + } + + case Primitive::kPrimShort: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(int16_t)).Uint32Value(); + CpuRegister out = locations->Out().AsX86_64().AsCpuRegister(); + if (index.IsConstant()) { + __ movsxw(out, Address(obj, + (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset)); + } else { + __ movsxw(out, Address(obj, index.AsX86_64().AsCpuRegister(), TIMES_2, data_offset)); + } + break; + } + + case Primitive::kPrimChar: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value(); + CpuRegister out = locations->Out().AsX86_64().AsCpuRegister(); + if (index.IsConstant()) { + __ movzxw(out, Address(obj, + (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset)); + } else { + __ movzxw(out, Address(obj, index.AsX86_64().AsCpuRegister(), TIMES_2, data_offset)); + } + break; + } + + case Primitive::kPrimInt: + case Primitive::kPrimNot: { + DCHECK_EQ(sizeof(mirror::HeapReference<mirror::Object>), sizeof(int32_t)); + uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value(); + CpuRegister out = locations->Out().AsX86_64().AsCpuRegister(); + if (index.IsConstant()) { + __ movl(out, Address(obj, + (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset)); + } else { + __ movl(out, Address(obj, index.AsX86_64().AsCpuRegister(), TIMES_4, data_offset)); + } + break; + } + + case Primitive::kPrimLong: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value(); + CpuRegister out = locations->Out().AsX86_64().AsCpuRegister(); + if (index.IsConstant()) { + __ movq(out, Address(obj, + (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset)); + } else { + __ movq(out, Address(obj, index.AsX86_64().AsCpuRegister(), TIMES_8, data_offset)); + } + break; + } + + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + LOG(FATAL) << "Unimplemented register type " << instruction->GetType(); + + case Primitive::kPrimVoid: + LOG(FATAL) << "Unreachable type " << instruction->GetType(); + } +} + +void LocationsBuilderX86_64::VisitArraySet(HArraySet* instruction) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); + Primitive::Type value_type = instruction->InputAt(2)->GetType(); + if (value_type == Primitive::kPrimNot) { + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, X86_64CpuLocation(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, X86_64CpuLocation(calling_convention.GetRegisterAt(1))); + locations->SetInAt(2, X86_64CpuLocation(calling_convention.GetRegisterAt(2))); + codegen_->MarkNotLeaf(); + } else { + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1))); + locations->SetInAt(2, Location::RequiresRegister()); + } + instruction->SetLocations(locations); +} + +void InstructionCodeGeneratorX86_64::VisitArraySet(HArraySet* instruction) { + LocationSummary* locations = instruction->GetLocations(); + CpuRegister obj = locations->InAt(0).AsX86_64().AsCpuRegister(); + Location index = locations->InAt(1); + Primitive::Type value_type = instruction->InputAt(2)->GetType(); + + switch (value_type) { + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value(); + CpuRegister value = locations->InAt(2).AsX86_64().AsCpuRegister(); + if (index.IsConstant()) { + size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset; + __ movb(Address(obj, offset), value); + } else { + __ movb(Address(obj, index.AsX86_64().AsCpuRegister(), TIMES_1, data_offset), value); + } + break; + } + + case Primitive::kPrimShort: + case Primitive::kPrimChar: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value(); + CpuRegister value = locations->InAt(2).AsX86_64().AsCpuRegister(); + if (index.IsConstant()) { + size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset; + __ movw(Address(obj, offset), value); + } else { + __ movw(Address(obj, index.AsX86_64().AsCpuRegister(), TIMES_2, data_offset), value); + } + break; + } + + case Primitive::kPrimInt: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value(); + CpuRegister value = locations->InAt(2).AsX86_64().AsCpuRegister(); + if (index.IsConstant()) { + size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; + __ movl(Address(obj, offset), value); + } else { + __ movl(Address(obj, index.AsX86_64().AsCpuRegister(), TIMES_4, data_offset), value); + } + break; + } + + case Primitive::kPrimNot: { + __ gs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pAputObject), true)); + DCHECK(!codegen_->IsLeafMethod()); + codegen_->RecordPcInfo(instruction->GetDexPc()); + break; + } + + case Primitive::kPrimLong: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value(); + CpuRegister value = locations->InAt(2).AsX86_64().AsCpuRegister(); + if (index.IsConstant()) { + size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; + __ movq(Address(obj, offset), value); + } else { + __ movq(Address(obj, index.AsX86_64().AsCpuRegister(), TIMES_8, data_offset), value); + } + break; + } + + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + LOG(FATAL) << "Unimplemented register type " << instruction->GetType(); + + case Primitive::kPrimVoid: + LOG(FATAL) << "Unreachable type " << instruction->GetType(); + } +} + +void LocationsBuilderX86_64::VisitArrayLength(HArrayLength* instruction) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister()); + instruction->SetLocations(locations); +} + +void InstructionCodeGeneratorX86_64::VisitArrayLength(HArrayLength* instruction) { + LocationSummary* locations = instruction->GetLocations(); + uint32_t offset = mirror::Array::LengthOffset().Uint32Value(); + CpuRegister obj = locations->InAt(0).AsX86_64().AsCpuRegister(); + CpuRegister out = locations->Out().AsX86_64().AsCpuRegister(); + __ movl(out, Address(obj, offset)); +} + +void LocationsBuilderX86_64::VisitBoundsCheck(HBoundsCheck* instruction) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + // TODO: Have a normalization phase that makes this instruction never used. + locations->SetOut(Location::SameAsFirstInput()); + instruction->SetLocations(locations); +} + +void InstructionCodeGeneratorX86_64::VisitBoundsCheck(HBoundsCheck* instruction) { + LocationSummary* locations = instruction->GetLocations(); + SlowPathCode* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathX86_64( + instruction->GetDexPc(), locations->InAt(0), locations->InAt(1)); + codegen_->AddSlowPath(slow_path); + + CpuRegister index = locations->InAt(0).AsX86_64().AsCpuRegister(); + CpuRegister length = locations->InAt(1).AsX86_64().AsCpuRegister(); + + __ cmpl(index, length); + __ j(kAboveEqual, slow_path->GetEntryLabel()); +} + +void CodeGeneratorX86_64::MarkGCCard(CpuRegister temp, + CpuRegister card, + CpuRegister object, + CpuRegister value) { + Label is_null; + __ testl(value, value); + __ j(kEqual, &is_null); + __ gs()->movq(card, Address::Absolute( + Thread::CardTableOffset<kX86_64WordSize>().Int32Value(), true)); + __ movq(temp, object); + __ shrq(temp, Immediate(gc::accounting::CardTable::kCardShift)); + __ movb(Address(temp, card, TIMES_1, 0), card); + __ Bind(&is_null); +} + void LocationsBuilderX86_64::VisitTemporary(HTemporary* temp) { temp->SetLocations(nullptr); } diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h index a20ca3f..44552ea 100644 --- a/compiler/optimizing/code_generator_x86_64.h +++ b/compiler/optimizing/code_generator_x86_64.h @@ -176,10 +176,13 @@ class CodeGeneratorX86_64 : public CodeGenerator { return InstructionSet::kX86_64; } - private: + // Emit a write barrier. + void MarkGCCard(CpuRegister temp, CpuRegister card, CpuRegister object, CpuRegister value); + // Helper method to move a value between two locations. void Move(Location destination, Location source); + private: LocationsBuilderX86_64 location_builder_; InstructionCodeGeneratorX86_64 instruction_visitor_; ParallelMoveResolverX86_64 move_resolver_; diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 61a6f6b..cb3dd0f 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -437,6 +437,10 @@ class HBasicBlock : public ArenaObject { M(Compare) \ M(InstanceFieldGet) \ M(InstanceFieldSet) \ + M(ArrayGet) \ + M(ArraySet) \ + M(ArrayLength) \ + M(BoundsCheck) \ M(NullCheck) \ M(Temporary) \ @@ -1367,6 +1371,80 @@ class HInstanceFieldSet : public HTemplateInstruction<2> { DISALLOW_COPY_AND_ASSIGN(HInstanceFieldSet); }; +class HArrayGet : public HExpression<2> { + public: + HArrayGet(HInstruction* array, HInstruction* index, Primitive::Type type) + : HExpression(type) { + SetRawInputAt(0, array); + SetRawInputAt(1, index); + } + + DECLARE_INSTRUCTION(ArrayGet); + + private: + DISALLOW_COPY_AND_ASSIGN(HArrayGet); +}; + +class HArraySet : public HTemplateInstruction<3> { + public: + HArraySet(HInstruction* array, + HInstruction* index, + HInstruction* value, + uint32_t dex_pc) : dex_pc_(dex_pc) { + SetRawInputAt(0, array); + SetRawInputAt(1, index); + SetRawInputAt(2, value); + } + + virtual bool NeedsEnvironment() const { + // We currently always call a runtime method to catch array store + // exceptions. + return InputAt(2)->GetType() == Primitive::kPrimNot; + } + + uint32_t GetDexPc() const { return dex_pc_; } + + DECLARE_INSTRUCTION(ArraySet); + + private: + const uint32_t dex_pc_; + + DISALLOW_COPY_AND_ASSIGN(HArraySet); +}; + +class HArrayLength : public HExpression<1> { + public: + explicit HArrayLength(HInstruction* array) : HExpression(Primitive::kPrimInt) { + SetRawInputAt(0, array); + } + + DECLARE_INSTRUCTION(ArrayLength); + + private: + DISALLOW_COPY_AND_ASSIGN(HArrayLength); +}; + +class HBoundsCheck : public HExpression<2> { + public: + HBoundsCheck(HInstruction* index, HInstruction* length, uint32_t dex_pc) + : HExpression(index->GetType()), dex_pc_(dex_pc) { + DCHECK(index->GetType() == Primitive::kPrimInt); + SetRawInputAt(0, index); + SetRawInputAt(1, length); + } + + virtual bool NeedsEnvironment() const { return true; } + + uint32_t GetDexPc() const { return dex_pc_; } + + DECLARE_INSTRUCTION(BoundsCheck); + + private: + const uint32_t dex_pc_; + + DISALLOW_COPY_AND_ASSIGN(HBoundsCheck); +}; + /** * Some DEX instructions are folded into multiple HInstructions that need * to stay live until the last HInstruction. This class diff --git a/compiler/utils/arm/assembler_thumb2.cc b/compiler/utils/arm/assembler_thumb2.cc index 78ff31a..4904428 100644 --- a/compiler/utils/arm/assembler_thumb2.cc +++ b/compiler/utils/arm/assembler_thumb2.cc @@ -659,6 +659,10 @@ bool Thumb2Assembler::Is32BitDataProcessing(Condition cond, if (so.IsImmediate()) { return true; } + + if (!can_contain_high_register) { + return true; + } } if (so.IsRegister() && IsHighRegister(so.GetRegister()) && !can_contain_high_register) { diff --git a/compiler/utils/assembler_thumb_test.cc b/compiler/utils/assembler_thumb_test.cc index 3312f8f..891a287 100644 --- a/compiler/utils/assembler_thumb_test.cc +++ b/compiler/utils/assembler_thumb_test.cc @@ -312,6 +312,9 @@ TEST(Thumb2AssemblerTest, DataProcessingRegister) { __ movs(R0, ShifterOperand(R1)); __ mvns(R0, ShifterOperand(R1)); + // 32 bit variants. + __ add(R12, R1, ShifterOperand(R0)); + size_t cs = __ CodeSize(); std::vector<uint8_t> managed_code(cs); MemoryRegion code(&managed_code[0], managed_code.size()); @@ -868,6 +871,9 @@ TEST(Thumb2AssemblerTest, StoreToOffset) { __ StoreToOffset(kStoreWord, R2, R4, 12); // Simple __ StoreToOffset(kStoreWord, R2, R4, 0x2000); // Offset too big. + __ StoreToOffset(kStoreWord, R0, R12, 12); + __ StoreToOffset(kStoreHalfword, R0, R12, 12); + __ StoreToOffset(kStoreByte, R2, R12, 12); size_t cs = __ CodeSize(); std::vector<uint8_t> managed_code(cs); diff --git a/compiler/utils/assembler_thumb_test_expected.cc.inc b/compiler/utils/assembler_thumb_test_expected.cc.inc index 18035f3..3f2641c 100644 --- a/compiler/utils/assembler_thumb_test_expected.cc.inc +++ b/compiler/utils/assembler_thumb_test_expected.cc.inc @@ -43,6 +43,7 @@ const char* DataProcessingRegisterResults[] = { " 3e: 42c8 cmn r0, r1\n", " 40: 0008 movs r0, r1\n", " 42: 43c8 mvns r0, r1\n", + " 44: eb01 0c00 add.w ip, r1, r0\n", nullptr }; const char* DataProcessingImmediateResults[] = { @@ -355,6 +356,9 @@ const char* StoreToOffsetResults[] = { " 2: f44f 5c00 mov.w ip, #8192 ; 0x2000\n", " 6: 44a4 add ip, r4\n", " 8: f8cc 2000 str.w r2, [ip]\n", + " c: f8cc 000c str.w r0, [ip, #12]\n", + " 10: f8ac 000c strh.w r0, [ip, #12]\n", + " 14: f88c 200c strb.w r2, [ip, #12]\n", nullptr }; const char* IfThenResults[] = { |