diff options
author | Nicolas Geoffray <ngeoffray@google.com> | 2014-10-22 16:07:05 +0100 |
---|---|---|
committer | Nicolas Geoffray <ngeoffray@google.com> | 2014-10-29 13:01:48 +0000 |
commit | 19a19cffd197a28ae4c9c3e59eff6352fd392241 (patch) | |
tree | 265b971afd0e33afc8986317aea2f5a6fe817aec /compiler | |
parent | 7c049c1f34220b0dc1a7f68f3b30f388bae7bdb9 (diff) | |
download | art-19a19cffd197a28ae4c9c3e59eff6352fd392241.zip art-19a19cffd197a28ae4c9c3e59eff6352fd392241.tar.gz art-19a19cffd197a28ae4c9c3e59eff6352fd392241.tar.bz2 |
Add support for static fields in optimizing compiler.
Change-Id: Id2f010589e2bd6faf42c05bb33abf6816ebe9fa9
Diffstat (limited to 'compiler')
23 files changed, 952 insertions, 79 deletions
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index eb5739f..05785a8 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -1071,7 +1071,8 @@ bool CompilerDriver::ComputeInstanceFieldInfo(uint32_t field_idx, const DexCompi bool CompilerDriver::ComputeStaticFieldInfo(uint32_t field_idx, const DexCompilationUnit* mUnit, bool is_put, MemberOffset* field_offset, uint32_t* storage_index, bool* is_referrers_class, - bool* is_volatile, bool* is_initialized) { + bool* is_volatile, bool* is_initialized, + Primitive::Type* type) { ScopedObjectAccess soa(Thread::Current()); // Try to resolve the field and compiling method's class. mirror::ArtField* resolved_field; @@ -1104,6 +1105,7 @@ bool CompilerDriver::ComputeStaticFieldInfo(uint32_t field_idx, const DexCompila *is_initialized = (*is_referrers_class) || (IsStaticFieldsClassInitialized(referrer_class, resolved_field) && CanAssumeTypeIsPresentInDexCache(*mUnit->GetDexFile(), *storage_index)); + *type = resolved_field->GetTypeAsPrimitiveType(); } else { // Conservative defaults. *is_volatile = true; @@ -1111,6 +1113,7 @@ bool CompilerDriver::ComputeStaticFieldInfo(uint32_t field_idx, const DexCompila *storage_index = -1; *is_referrers_class = false; *is_initialized = false; + *type = Primitive::kPrimVoid; } ProcessedStaticField(result, *is_referrers_class); return result; diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index 6764aef..65a842d 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -311,7 +311,8 @@ class CompilerDriver { // field is within the referrer (which can avoid checking class initialization). bool ComputeStaticFieldInfo(uint32_t field_idx, const DexCompilationUnit* mUnit, bool is_put, MemberOffset* field_offset, uint32_t* storage_index, - bool* is_referrers_class, bool* is_volatile, bool* is_initialized) + bool* is_referrers_class, bool* is_volatile, bool* is_initialized, + Primitive::Type* type) LOCKS_EXCLUDED(Locks::mutator_lock_); // Can we fastpath a interface, super class or virtual method call? Computes method's vtable diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc index f80ebdb..e4ccd96 100644 --- a/compiler/optimizing/builder.cc +++ b/compiler/optimizing/builder.cc @@ -1,5 +1,4 @@ /* - * * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -421,9 +420,9 @@ bool HGraphBuilder::BuildInvoke(const Instruction& instruction, return true; } -bool HGraphBuilder::BuildFieldAccess(const Instruction& instruction, - uint32_t dex_offset, - bool is_put) { +bool HGraphBuilder::BuildInstanceFieldAccess(const Instruction& instruction, + uint32_t dex_offset, + bool is_put) { uint32_t source_or_dest_reg = instruction.VRegA_22c(); uint32_t obj_reg = instruction.VRegB_22c(); uint16_t field_index = instruction.VRegC_22c(); @@ -469,6 +468,67 @@ bool HGraphBuilder::BuildFieldAccess(const Instruction& instruction, return true; } + + +bool HGraphBuilder::BuildStaticFieldAccess(const Instruction& instruction, + uint32_t dex_offset, + bool is_put) { + uint32_t source_or_dest_reg = instruction.VRegA_21c(); + uint16_t field_index = instruction.VRegB_21c(); + + uint32_t storage_index; + bool is_referrers_class; + bool is_initialized; + bool is_volatile; + MemberOffset field_offset(0u); + Primitive::Type field_type; + + bool fast_path = compiler_driver_->ComputeStaticFieldInfo(field_index, + dex_compilation_unit_, + is_put, + &field_offset, + &storage_index, + &is_referrers_class, + &is_volatile, + &is_initialized, + &field_type); + if (!fast_path) { + return false; + } + + if (is_volatile) { + return false; + } + + if (!IsTypeSupported(field_type)) { + return false; + } + + HLoadClass* constant = new (arena_) HLoadClass( + storage_index, is_referrers_class, is_initialized, dex_offset); + current_block_->AddInstruction(constant); + + HInstruction* cls = constant; + if (constant->NeedsInitialization()) { + cls = new (arena_) HClinitCheck(constant, dex_offset); + current_block_->AddInstruction(cls); + } + + if (is_put) { + // We need to keep the class alive before loading the value. + Temporaries temps(graph_, 1); + temps.Add(cls); + HInstruction* value = LoadLocal(source_or_dest_reg, field_type); + DCHECK_EQ(value->GetType(), field_type); + current_block_->AddInstruction( + new (arena_) HStaticFieldSet(cls, value, field_type, field_offset)); + } else { + current_block_->AddInstruction(new (arena_) HStaticFieldGet(cls, field_type, field_offset)); + UpdateLocal(source_or_dest_reg, current_block_->GetLastInstruction()); + } + return true; +} + void HGraphBuilder::BuildArrayAccess(const Instruction& instruction, uint32_t dex_offset, bool is_put, @@ -1043,7 +1103,7 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32 case Instruction::IGET_BYTE: case Instruction::IGET_CHAR: case Instruction::IGET_SHORT: { - if (!BuildFieldAccess(instruction, dex_offset, false)) { + if (!BuildInstanceFieldAccess(instruction, dex_offset, false)) { return false; } break; @@ -1056,7 +1116,33 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32 case Instruction::IPUT_BYTE: case Instruction::IPUT_CHAR: case Instruction::IPUT_SHORT: { - if (!BuildFieldAccess(instruction, dex_offset, true)) { + if (!BuildInstanceFieldAccess(instruction, dex_offset, true)) { + return false; + } + break; + } + + case Instruction::SGET: + case Instruction::SGET_WIDE: + case Instruction::SGET_OBJECT: + case Instruction::SGET_BOOLEAN: + case Instruction::SGET_BYTE: + case Instruction::SGET_CHAR: + case Instruction::SGET_SHORT: { + if (!BuildStaticFieldAccess(instruction, dex_offset, false)) { + return false; + } + break; + } + + case Instruction::SPUT: + case Instruction::SPUT_WIDE: + case Instruction::SPUT_OBJECT: + case Instruction::SPUT_BOOLEAN: + case Instruction::SPUT_BYTE: + case Instruction::SPUT_CHAR: + case Instruction::SPUT_SHORT: { + if (!BuildStaticFieldAccess(instruction, dex_offset, true)) { return false; } break; diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h index b55ef07..eea762f 100644 --- a/compiler/optimizing/builder.h +++ b/compiler/optimizing/builder.h @@ -118,7 +118,12 @@ class HGraphBuilder : public ValueObject { void BuildReturn(const Instruction& instruction, Primitive::Type type); - bool BuildFieldAccess(const Instruction& instruction, uint32_t dex_offset, bool is_get); + // Builds an instance field access node and returns whether the instruction is supported. + bool BuildInstanceFieldAccess(const Instruction& instruction, uint32_t dex_offset, bool is_put); + + // Builds a static field access node and returns whether the instruction is supported. + bool BuildStaticFieldAccess(const Instruction& instruction, uint32_t dex_offset, bool is_put); + void BuildArrayAccess(const Instruction& instruction, uint32_t dex_offset, bool is_get, diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index 28ff1cf..c61e991 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -26,6 +26,9 @@ #include "gc_map_builder.h" #include "leb128.h" #include "mapping_table.h" +#include "mirror/array-inl.h" +#include "mirror/object_array-inl.h" +#include "mirror/object_reference.h" #include "ssa_liveness_analysis.h" #include "utils/assembler.h" #include "verifier/dex_gc_map.h" @@ -33,6 +36,10 @@ namespace art { +size_t CodeGenerator::GetCacheOffset(uint32_t index) { + return mirror::ObjectArray<mirror::Object>::OffsetOfElement(index).SizeValue(); +} + void CodeGenerator::CompileBaseline(CodeAllocator* allocator, bool is_leaf) { const GrowableArray<HBasicBlock*>& blocks = GetGraph()->GetBlocks(); DCHECK(blocks.Get(0) == GetGraph()->GetEntryBlock()); diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index 1638869..bf9d2c0 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -157,6 +157,11 @@ class CodeGenerator : public ArenaObject { bool* GetBlockedCoreRegisters() const { return blocked_core_registers_; } bool* GetBlockedFloatingPointRegisters() const { return blocked_fpu_registers_; } + // Helper that returns the pointer offset of an index in an object array. + // Note: this method assumes we always have the same pointer size, regardless + // of the architecture. + static size_t GetCacheOffset(uint32_t index); + protected: CodeGenerator(HGraph* graph, size_t number_of_core_registers, diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index a5d4c43..a06860a 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -60,6 +60,7 @@ class InvokeRuntimeCallingConvention : public CallingConvention<Register, SRegis }; #define __ reinterpret_cast<ArmAssembler*>(codegen->GetAssembler())-> +#define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kArmWordSize, x).Int32Value() class SlowPathCodeARM : public SlowPathCode { public: @@ -80,11 +81,10 @@ class NullCheckSlowPathARM : public SlowPathCodeARM { explicit NullCheckSlowPathARM(HNullCheck* instruction) : instruction_(instruction) {} virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); __ Bind(GetEntryLabel()); - int32_t offset = QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pThrowNullPointer).Int32Value(); - __ LoadFromOffset(kLoadWord, LR, TR, offset); - __ blx(LR); - codegen->RecordPcInfo(instruction_, instruction_->GetDexPc()); + arm_codegen->InvokeRuntime( + QUICK_ENTRY_POINT(pThrowNullPointer), instruction_, instruction_->GetDexPc()); } private: @@ -115,10 +115,8 @@ class SuspendCheckSlowPathARM : public SlowPathCodeARM { CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); __ Bind(GetEntryLabel()); codegen->SaveLiveRegisters(instruction_->GetLocations()); - int32_t offset = QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pTestSuspend).Int32Value(); - __ LoadFromOffset(kLoadWord, LR, TR, offset); - __ blx(LR); - codegen->RecordPcInfo(instruction_, instruction_->GetDexPc()); + arm_codegen->InvokeRuntime( + QUICK_ENTRY_POINT(pTestSuspend), instruction_, instruction_->GetDexPc()); codegen->RestoreLiveRegisters(instruction_->GetLocations()); if (successor_ == nullptr) { __ b(GetReturnLabel()); @@ -156,12 +154,12 @@ class BoundsCheckSlowPathARM : public SlowPathCodeARM { CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); __ Bind(GetEntryLabel()); InvokeRuntimeCallingConvention calling_convention; - arm_codegen->Move32(Location::RegisterLocation(calling_convention.GetRegisterAt(0)), index_location_); - arm_codegen->Move32(Location::RegisterLocation(calling_convention.GetRegisterAt(1)), length_location_); - int32_t offset = QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pThrowArrayBounds).Int32Value(); - __ LoadFromOffset(kLoadWord, LR, TR, offset); - __ blx(LR); - codegen->RecordPcInfo(instruction_, instruction_->GetDexPc()); + arm_codegen->Move32( + Location::RegisterLocation(calling_convention.GetRegisterAt(0)), index_location_); + arm_codegen->Move32( + Location::RegisterLocation(calling_convention.GetRegisterAt(1)), length_location_); + arm_codegen->InvokeRuntime( + QUICK_ENTRY_POINT(pThrowArrayBounds), instruction_, instruction_->GetDexPc()); } private: @@ -172,6 +170,32 @@ class BoundsCheckSlowPathARM : public SlowPathCodeARM { DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathARM); }; +class ClinitCheckSlowPathARM : public SlowPathCodeARM { + public: + explicit ClinitCheckSlowPathARM(HClinitCheck* instruction) : instruction_(instruction) {} + + virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); + __ Bind(GetEntryLabel()); + codegen->SaveLiveRegisters(instruction_->GetLocations()); + + HLoadClass* cls = instruction_->GetLoadClass(); + InvokeRuntimeCallingConvention calling_convention; + __ LoadImmediate(calling_convention.GetRegisterAt(0), cls->GetTypeIndex()); + arm_codegen->LoadCurrentMethod(calling_convention.GetRegisterAt(1)); + arm_codegen->InvokeRuntime( + QUICK_ENTRY_POINT(pInitializeStaticStorage), instruction_, instruction_->GetDexPc()); + arm_codegen->Move32(instruction_->GetLocations()->InAt(0), Location::RegisterLocation(R0)); + codegen->RestoreLiveRegisters(instruction_->GetLocations()); + __ b(GetExitLabel()); + } + + private: + HClinitCheck* const instruction_; + + DISALLOW_COPY_AND_ASSIGN(ClinitCheckSlowPathARM); +}; + #undef __ #define __ reinterpret_cast<ArmAssembler*>(GetAssembler())-> @@ -684,6 +708,18 @@ void CodeGeneratorARM::Move(HInstruction* instruction, Location location, HInstr } } +void CodeGeneratorARM::InvokeRuntime(int32_t entry_point_offset, + HInstruction* instruction, + uint32_t dex_pc) { + __ LoadFromOffset(kLoadWord, LR, TR, entry_point_offset); + __ blx(LR); + RecordPcInfo(instruction, dex_pc); + DCHECK(instruction->IsSuspendCheck() + || instruction->IsBoundsCheck() + || instruction->IsNullCheck() + || !IsLeafMethod()); +} + void LocationsBuilderARM::VisitGoto(HGoto* got) { got->SetLocations(nullptr); } @@ -971,15 +1007,12 @@ void LocationsBuilderARM::VisitInvokeStatic(HInvokeStatic* invoke) { HandleInvoke(invoke); } -void InstructionCodeGeneratorARM::LoadCurrentMethod(Register reg) { +void CodeGeneratorARM::LoadCurrentMethod(Register reg) { __ LoadFromOffset(kLoadWord, reg, SP, kCurrentMethodStackOffset); } void InstructionCodeGeneratorARM::VisitInvokeStatic(HInvokeStatic* invoke) { Register temp = invoke->GetLocations()->GetTemp(0).As<Register>(); - uint32_t heap_reference_size = sizeof(mirror::HeapReference<mirror::Object>); - size_t index_in_cache = mirror::Array::DataOffset(heap_reference_size).Int32Value() + - invoke->GetIndexInDexCache() * kArmWordSize; // TODO: Implement all kinds of calls: // 1) boot -> boot @@ -989,11 +1022,13 @@ void InstructionCodeGeneratorARM::VisitInvokeStatic(HInvokeStatic* invoke) { // Currently we implement the app -> app logic, which looks up in the resolve cache. // temp = method; - LoadCurrentMethod(temp); + codegen_->LoadCurrentMethod(temp); // temp = temp->dex_cache_resolved_methods_; - __ LoadFromOffset(kLoadWord, temp, temp, mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value()); + __ LoadFromOffset( + kLoadWord, temp, temp, mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value()); // temp = temp[index_in_cache] - __ LoadFromOffset(kLoadWord, temp, temp, index_in_cache); + __ LoadFromOffset( + kLoadWord, temp, temp, CodeGenerator::GetCacheOffset(invoke->GetIndexInDexCache())); // LR = temp[offset_of_quick_compiled_code] __ LoadFromOffset(kLoadWord, LR, temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value()); @@ -1392,15 +1427,10 @@ void LocationsBuilderARM::VisitNewInstance(HNewInstance* instruction) { void InstructionCodeGeneratorARM::VisitNewInstance(HNewInstance* instruction) { InvokeRuntimeCallingConvention calling_convention; - LoadCurrentMethod(calling_convention.GetRegisterAt(1)); + codegen_->LoadCurrentMethod(calling_convention.GetRegisterAt(1)); __ LoadImmediate(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex()); - - int32_t offset = QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pAllocObjectWithAccessCheck).Int32Value(); - __ LoadFromOffset(kLoadWord, LR, TR, offset); - __ blx(LR); - - codegen_->RecordPcInfo(instruction, instruction->GetDexPc()); - DCHECK(!codegen_->IsLeafMethod()); + codegen_->InvokeRuntime( + QUICK_ENTRY_POINT(pAllocObjectWithAccessCheck), instruction, instruction->GetDexPc()); } void LocationsBuilderARM::VisitNewArray(HNewArray* instruction) { @@ -1415,15 +1445,10 @@ void LocationsBuilderARM::VisitNewArray(HNewArray* instruction) { void InstructionCodeGeneratorARM::VisitNewArray(HNewArray* instruction) { InvokeRuntimeCallingConvention calling_convention; - LoadCurrentMethod(calling_convention.GetRegisterAt(1)); + codegen_->LoadCurrentMethod(calling_convention.GetRegisterAt(1)); __ LoadImmediate(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex()); - - int32_t offset = QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pAllocArrayWithAccessCheck).Int32Value(); - __ LoadFromOffset(kLoadWord, LR, TR, offset); - __ blx(LR); - - codegen_->RecordPcInfo(instruction, instruction->GetDexPc()); - DCHECK(!codegen_->IsLeafMethod()); + codegen_->InvokeRuntime( + QUICK_ENTRY_POINT(pAllocArrayWithAccessCheck), instruction, instruction->GetDexPc()); } void LocationsBuilderARM::VisitParameterValue(HParameterValue* instruction) { @@ -1850,11 +1875,7 @@ void InstructionCodeGeneratorARM::VisitArraySet(HArraySet* instruction) { } case Primitive::kPrimNot: { - int32_t offset = QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pAputObject).Int32Value(); - __ LoadFromOffset(kLoadWord, LR, TR, offset); - __ blx(LR); - codegen_->RecordPcInfo(instruction, instruction->GetDexPc()); - DCHECK(!codegen_->IsLeafMethod()); + codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pAputObject), instruction, instruction->GetDexPc()); break; } @@ -2065,5 +2086,177 @@ void ParallelMoveResolverARM::RestoreScratch(int reg) { __ Pop(static_cast<Register>(reg)); } +void LocationsBuilderARM::VisitLoadClass(HLoadClass* cls) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(cls, LocationSummary::kNoCall); + locations->SetOut(Location::RequiresRegister()); +} + +void InstructionCodeGeneratorARM::VisitLoadClass(HLoadClass* cls) { + Register out = cls->GetLocations()->Out().As<Register>(); + if (cls->IsReferrersClass()) { + codegen_->LoadCurrentMethod(out); + __ LoadFromOffset(kLoadWord, out, out, mirror::ArtMethod::DeclaringClassOffset().Int32Value()); + } else { + codegen_->LoadCurrentMethod(out); + __ LoadFromOffset( + kLoadWord, out, out, mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value()); + __ LoadFromOffset(kLoadWord, out, out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex())); + } +} + +void LocationsBuilderARM::VisitClinitCheck(HClinitCheck* check) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(check, LocationSummary::kCallOnSlowPath); + locations->SetInAt(0, Location::RequiresRegister()); + if (check->HasUses()) { + locations->SetOut(Location::SameAsFirstInput()); + } +} + +void InstructionCodeGeneratorARM::VisitClinitCheck(HClinitCheck* check) { + SlowPathCodeARM* slow_path = new (GetGraph()->GetArena()) ClinitCheckSlowPathARM(check); + codegen_->AddSlowPath(slow_path); + + LocationSummary* locations = check->GetLocations(); + // We remove the class as a live register, we know it's null or unused in the slow path. + RegisterSet* register_set = locations->GetLiveRegisters(); + register_set->Remove(locations->InAt(0)); + + Register class_reg = locations->InAt(0).As<Register>(); + __ cmp(class_reg, ShifterOperand(0)); + __ b(slow_path->GetEntryLabel(), EQ); + __ LoadFromOffset(kLoadWord, IP, class_reg, mirror::Class::StatusOffset().Int32Value()); + __ cmp(IP, ShifterOperand(mirror::Class::kStatusInitialized)); + __ b(slow_path->GetEntryLabel(), LT); + // Even if the initialized flag is set, we may be in a situation where caches are not synced + // properly. Therefore, we do a memory fence. + __ dmb(ISH); + __ Bind(slow_path->GetExitLabel()); +} + +void LocationsBuilderARM::VisitStaticFieldGet(HStaticFieldGet* instruction) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); +} + +void InstructionCodeGeneratorARM::VisitStaticFieldGet(HStaticFieldGet* instruction) { + LocationSummary* locations = instruction->GetLocations(); + Register cls = locations->InAt(0).As<Register>(); + uint32_t offset = instruction->GetFieldOffset().Uint32Value(); + + switch (instruction->GetType()) { + case Primitive::kPrimBoolean: { + Register out = locations->Out().As<Register>(); + __ LoadFromOffset(kLoadUnsignedByte, out, cls, offset); + break; + } + + case Primitive::kPrimByte: { + Register out = locations->Out().As<Register>(); + __ LoadFromOffset(kLoadSignedByte, out, cls, offset); + break; + } + + case Primitive::kPrimShort: { + Register out = locations->Out().As<Register>(); + __ LoadFromOffset(kLoadSignedHalfword, out, cls, offset); + break; + } + + case Primitive::kPrimChar: { + Register out = locations->Out().As<Register>(); + __ LoadFromOffset(kLoadUnsignedHalfword, out, cls, offset); + break; + } + + case Primitive::kPrimInt: + case Primitive::kPrimNot: { + Register out = locations->Out().As<Register>(); + __ LoadFromOffset(kLoadWord, out, cls, offset); + break; + } + + case Primitive::kPrimLong: { + // TODO: support volatile. + Location out = locations->Out(); + __ LoadFromOffset(kLoadWordPair, out.AsRegisterPairLow<Register>(), cls, offset); + break; + } + + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + LOG(FATAL) << "Unimplemented register type " << instruction->GetType(); + UNREACHABLE(); + case Primitive::kPrimVoid: + LOG(FATAL) << "Unreachable type " << instruction->GetType(); + UNREACHABLE(); + } +} + +void LocationsBuilderARM::VisitStaticFieldSet(HStaticFieldSet* instruction) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + bool is_object_type = instruction->GetFieldType() == Primitive::kPrimNot; + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + // Temporary registers for the write barrier. + if (is_object_type) { + locations->AddTemp(Location::RequiresRegister()); + locations->AddTemp(Location::RequiresRegister()); + } +} + +void InstructionCodeGeneratorARM::VisitStaticFieldSet(HStaticFieldSet* instruction) { + LocationSummary* locations = instruction->GetLocations(); + Register cls = locations->InAt(0).As<Register>(); + uint32_t offset = instruction->GetFieldOffset().Uint32Value(); + Primitive::Type field_type = instruction->GetFieldType(); + + switch (field_type) { + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: { + Register value = locations->InAt(1).As<Register>(); + __ StoreToOffset(kStoreByte, value, cls, offset); + break; + } + + case Primitive::kPrimShort: + case Primitive::kPrimChar: { + Register value = locations->InAt(1).As<Register>(); + __ StoreToOffset(kStoreHalfword, value, cls, offset); + break; + } + + case Primitive::kPrimInt: + case Primitive::kPrimNot: { + Register value = locations->InAt(1).As<Register>(); + __ StoreToOffset(kStoreWord, value, cls, offset); + if (field_type == Primitive::kPrimNot) { + Register temp = locations->GetTemp(0).As<Register>(); + Register card = locations->GetTemp(1).As<Register>(); + codegen_->MarkGCCard(temp, card, cls, value); + } + break; + } + + case Primitive::kPrimLong: { + Location value = locations->InAt(1); + __ StoreToOffset(kStoreWordPair, value.AsRegisterPairLow<Register>(), cls, offset); + break; + } + + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + LOG(FATAL) << "Unimplemented register type " << field_type; + UNREACHABLE(); + case Primitive::kPrimVoid: + LOG(FATAL) << "Unreachable type " << field_type; + UNREACHABLE(); + } +} + } // namespace arm } // namespace art diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h index b1c36c0..c65b426 100644 --- a/compiler/optimizing/code_generator_arm.h +++ b/compiler/optimizing/code_generator_arm.h @@ -125,7 +125,6 @@ class InstructionCodeGeneratorARM : public HGraphVisitor { #undef DECLARE_VISIT_INSTRUCTION ArmAssembler* GetAssembler() const { return assembler_; } - void LoadCurrentMethod(Register reg); private: // Generate code for the given suspend check. If not null, `successor` @@ -194,6 +193,12 @@ class CodeGeneratorARM : public CodeGenerator { // Helper method to move a 64bits value between two locations. void Move64(Location destination, Location source); + // Load current method into `reg`. + void LoadCurrentMethod(Register reg); + + // Generate code to invoke a runtime entry point. + void InvokeRuntime(int32_t offset, HInstruction* instruction, uint32_t dex_pc); + // Emit a write barrier. void MarkGCCard(Register temp, Register card, Register object, Register value); diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index f9aa44b..fe999c2 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -534,13 +534,17 @@ InstructionCodeGeneratorARM64::InstructionCodeGeneratorARM64(HGraph* graph, #define FOR_EACH_UNIMPLEMENTED_INSTRUCTION(M) \ M(ArrayGet) \ M(ArraySet) \ + M(ClinitCheck) \ M(DoubleConstant) \ M(Div) \ M(FloatConstant) \ M(Mul) \ + M(LoadClass) \ M(Neg) \ M(NewArray) \ - M(ParallelMove) + M(ParallelMove) \ + M(StaticFieldGet) \ + M(StaticFieldSet) \ #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 495ff8b..267edca 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -157,6 +157,32 @@ class SuspendCheckSlowPathX86 : public SlowPathCodeX86 { DISALLOW_COPY_AND_ASSIGN(SuspendCheckSlowPathX86); }; +class ClinitCheckSlowPathX86 : public SlowPathCodeX86 { + public: + explicit ClinitCheckSlowPathX86(HClinitCheck* instruction) : instruction_(instruction) {} + + virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + CodeGeneratorX86* x86_codegen = down_cast<CodeGeneratorX86*>(codegen); + __ Bind(GetEntryLabel()); + codegen->SaveLiveRegisters(instruction_->GetLocations()); + + HLoadClass* cls = instruction_->GetLoadClass(); + InvokeRuntimeCallingConvention calling_convention; + __ movl(calling_convention.GetRegisterAt(0), Immediate(cls->GetTypeIndex())); + x86_codegen->LoadCurrentMethod(calling_convention.GetRegisterAt(1)); + __ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pInitializeStaticStorage))); + codegen->RecordPcInfo(instruction_, instruction_->GetDexPc()); + x86_codegen->Move32(instruction_->GetLocations()->InAt(0), Location::RegisterLocation(EAX)); + codegen->RestoreLiveRegisters(instruction_->GetLocations()); + __ jmp(GetExitLabel()); + } + + private: + HClinitCheck* const instruction_; + + DISALLOW_COPY_AND_ASSIGN(ClinitCheckSlowPathX86); +}; + #undef __ #define __ reinterpret_cast<X86Assembler*>(GetAssembler())-> @@ -313,7 +339,7 @@ void CodeGeneratorX86::Bind(HBasicBlock* block) { __ Bind(GetLabelOf(block)); } -void InstructionCodeGeneratorX86::LoadCurrentMethod(Register reg) { +void CodeGeneratorX86::LoadCurrentMethod(Register reg) { __ movl(reg, Address(ESP, kCurrentMethodStackOffset)); } @@ -897,9 +923,6 @@ void LocationsBuilderX86::VisitInvokeStatic(HInvokeStatic* invoke) { void InstructionCodeGeneratorX86::VisitInvokeStatic(HInvokeStatic* invoke) { Register temp = invoke->GetLocations()->GetTemp(0).As<Register>(); - uint32_t heap_reference_size = sizeof(mirror::HeapReference<mirror::Object>); - size_t index_in_cache = mirror::Array::DataOffset(heap_reference_size).Int32Value() + - invoke->GetIndexInDexCache() * kX86WordSize; // TODO: Implement all kinds of calls: // 1) boot -> boot @@ -909,11 +932,11 @@ void InstructionCodeGeneratorX86::VisitInvokeStatic(HInvokeStatic* invoke) { // Currently we implement the app -> app logic, which looks up in the resolve cache. // temp = method; - LoadCurrentMethod(temp); + codegen_->LoadCurrentMethod(temp); // temp = temp->dex_cache_resolved_methods_; __ movl(temp, Address(temp, mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value())); // temp = temp[index_in_cache] - __ movl(temp, Address(temp, index_in_cache)); + __ movl(temp, Address(temp, CodeGenerator::GetCacheOffset(invoke->GetIndexInDexCache()))); // (temp + offset_of_quick_compiled_code)() __ call(Address(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value())); @@ -1354,7 +1377,7 @@ void LocationsBuilderX86::VisitNewInstance(HNewInstance* instruction) { void InstructionCodeGeneratorX86::VisitNewInstance(HNewInstance* instruction) { InvokeRuntimeCallingConvention calling_convention; - LoadCurrentMethod(calling_convention.GetRegisterAt(1)); + codegen_->LoadCurrentMethod(calling_convention.GetRegisterAt(1)); __ movl(calling_convention.GetRegisterAt(0), Immediate(instruction->GetTypeIndex())); __ fs()->call( @@ -1376,7 +1399,7 @@ void LocationsBuilderX86::VisitNewArray(HNewArray* instruction) { void InstructionCodeGeneratorX86::VisitNewArray(HNewArray* instruction) { InvokeRuntimeCallingConvention calling_convention; - LoadCurrentMethod(calling_convention.GetRegisterAt(1)); + codegen_->LoadCurrentMethod(calling_convention.GetRegisterAt(1)); __ movl(calling_convention.GetRegisterAt(0), Immediate(instruction->GetTypeIndex())); __ fs()->call( @@ -2111,5 +2134,187 @@ void ParallelMoveResolverX86::RestoreScratch(int reg) { __ popl(static_cast<Register>(reg)); } +void LocationsBuilderX86::VisitLoadClass(HLoadClass* cls) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(cls, LocationSummary::kNoCall); + locations->SetOut(Location::RequiresRegister()); +} + +void InstructionCodeGeneratorX86::VisitLoadClass(HLoadClass* cls) { + Register out = cls->GetLocations()->Out().As<Register>(); + if (cls->IsReferrersClass()) { + codegen_->LoadCurrentMethod(out); + __ movl(out, Address(out, mirror::ArtMethod::DeclaringClassOffset().Int32Value())); + } else { + codegen_->LoadCurrentMethod(out); + __ movl(out, Address(out, mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value())); + __ movl(out, Address(out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex()))); + } +} + +void LocationsBuilderX86::VisitClinitCheck(HClinitCheck* check) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(check, LocationSummary::kCallOnSlowPath); + locations->SetInAt(0, Location::RequiresRegister()); + if (check->HasUses()) { + locations->SetOut(Location::SameAsFirstInput()); + } +} + +void InstructionCodeGeneratorX86::VisitClinitCheck(HClinitCheck* check) { + SlowPathCodeX86* slow_path = new (GetGraph()->GetArena()) ClinitCheckSlowPathX86(check); + codegen_->AddSlowPath(slow_path); + + LocationSummary* locations = check->GetLocations(); + // We remove the class as a live register, we know it's null or unused in the slow path. + RegisterSet* register_set = locations->GetLiveRegisters(); + register_set->Remove(locations->InAt(0)); + + Register class_reg = locations->InAt(0).As<Register>(); + __ testl(class_reg, class_reg); + __ j(kEqual, slow_path->GetEntryLabel()); + __ cmpl(Address(class_reg, mirror::Class::StatusOffset().Int32Value()), + Immediate(mirror::Class::kStatusInitialized)); + __ j(kLess, slow_path->GetEntryLabel()); + __ Bind(slow_path->GetExitLabel()); + // No need for memory fence, thanks to the X86 memory model. +} + +void LocationsBuilderX86::VisitStaticFieldGet(HStaticFieldGet* instruction) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); +} + +void InstructionCodeGeneratorX86::VisitStaticFieldGet(HStaticFieldGet* instruction) { + LocationSummary* locations = instruction->GetLocations(); + Register cls = locations->InAt(0).As<Register>(); + uint32_t offset = instruction->GetFieldOffset().Uint32Value(); + + switch (instruction->GetType()) { + case Primitive::kPrimBoolean: { + Register out = locations->Out().As<Register>(); + __ movzxb(out, Address(cls, offset)); + break; + } + + case Primitive::kPrimByte: { + Register out = locations->Out().As<Register>(); + __ movsxb(out, Address(cls, offset)); + break; + } + + case Primitive::kPrimShort: { + Register out = locations->Out().As<Register>(); + __ movsxw(out, Address(cls, offset)); + break; + } + + case Primitive::kPrimChar: { + Register out = locations->Out().As<Register>(); + __ movzxw(out, Address(cls, offset)); + break; + } + + case Primitive::kPrimInt: + case Primitive::kPrimNot: { + Register out = locations->Out().As<Register>(); + __ movl(out, Address(cls, offset)); + break; + } + + case Primitive::kPrimLong: { + // TODO: support volatile. + __ movl(locations->Out().AsRegisterPairLow<Register>(), Address(cls, offset)); + __ movl(locations->Out().AsRegisterPairHigh<Register>(), Address(cls, kX86WordSize + offset)); + break; + } + + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + LOG(FATAL) << "Unimplemented register type " << instruction->GetType(); + UNREACHABLE(); + case Primitive::kPrimVoid: + LOG(FATAL) << "Unreachable type " << instruction->GetType(); + UNREACHABLE(); + } +} + +void LocationsBuilderX86::VisitStaticFieldSet(HStaticFieldSet* instruction) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + locations->SetInAt(0, Location::RequiresRegister()); + Primitive::Type field_type = instruction->GetFieldType(); + bool is_object_type = field_type == Primitive::kPrimNot; + bool is_byte_type = (field_type == Primitive::kPrimBoolean) + || (field_type == Primitive::kPrimByte); + // The register allocator does not support multiple + // inputs that die at entry with one in a specific register. + if (is_byte_type) { + // Ensure the value is in a byte register. + locations->SetInAt(1, Location::RegisterLocation(EAX)); + } else { + locations->SetInAt(1, Location::RequiresRegister()); + } + // Temporary registers for the write barrier. + if (is_object_type) { + locations->AddTemp(Location::RequiresRegister()); + // Ensure the card is in a byte register. + locations->AddTemp(Location::RegisterLocation(ECX)); + } +} + +void InstructionCodeGeneratorX86::VisitStaticFieldSet(HStaticFieldSet* instruction) { + LocationSummary* locations = instruction->GetLocations(); + Register cls = locations->InAt(0).As<Register>(); + uint32_t offset = instruction->GetFieldOffset().Uint32Value(); + Primitive::Type field_type = instruction->GetFieldType(); + + switch (field_type) { + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: { + ByteRegister value = locations->InAt(1).As<ByteRegister>(); + __ movb(Address(cls, offset), value); + break; + } + + case Primitive::kPrimShort: + case Primitive::kPrimChar: { + Register value = locations->InAt(1).As<Register>(); + __ movw(Address(cls, offset), value); + break; + } + + case Primitive::kPrimInt: + case Primitive::kPrimNot: { + Register value = locations->InAt(1).As<Register>(); + __ movl(Address(cls, offset), value); + + if (field_type == Primitive::kPrimNot) { + Register temp = locations->GetTemp(0).As<Register>(); + Register card = locations->GetTemp(1).As<Register>(); + codegen_->MarkGCCard(temp, card, cls, value); + } + break; + } + + case Primitive::kPrimLong: { + Location value = locations->InAt(1); + __ movl(Address(cls, offset), value.AsRegisterPairLow<Register>()); + __ movl(Address(cls, kX86WordSize + offset), value.AsRegisterPairHigh<Register>()); + break; + } + + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + LOG(FATAL) << "Unimplemented register type " << field_type; + UNREACHABLE(); + case Primitive::kPrimVoid: + LOG(FATAL) << "Unreachable type " << field_type; + UNREACHABLE(); + } +} + } // namespace x86 } // namespace art diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h index fff91d1..bcceaad 100644 --- a/compiler/optimizing/code_generator_x86.h +++ b/compiler/optimizing/code_generator_x86.h @@ -119,8 +119,6 @@ class InstructionCodeGeneratorX86 : public HGraphVisitor { #undef DECLARE_VISIT_INSTRUCTION - void LoadCurrentMethod(Register reg); - X86Assembler* GetAssembler() const { return assembler_; } private: @@ -193,6 +191,8 @@ class CodeGeneratorX86 : public CodeGenerator { // Emit a write barrier. void MarkGCCard(Register temp, Register card, Register object, Register value); + void LoadCurrentMethod(Register reg); + Label* GetLabelOf(HBasicBlock* block) const { return block_labels_.GetRawStorage() + block->GetBlockId(); } diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index 4d11a24..e8d34e3 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -151,8 +151,10 @@ class BoundsCheckSlowPathX86_64 : public SlowPathCodeX86_64 { CodeGeneratorX86_64* x64_codegen = down_cast<CodeGeneratorX86_64*>(codegen); __ Bind(GetEntryLabel()); InvokeRuntimeCallingConvention calling_convention; - x64_codegen->Move(Location::RegisterLocation(calling_convention.GetRegisterAt(0)), index_location_); - x64_codegen->Move(Location::RegisterLocation(calling_convention.GetRegisterAt(1)), length_location_); + x64_codegen->Move( + Location::RegisterLocation(calling_convention.GetRegisterAt(0)), index_location_); + x64_codegen->Move( + Location::RegisterLocation(calling_convention.GetRegisterAt(1)), length_location_); __ gs()->call(Address::Absolute( QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pThrowArrayBounds), true)); codegen->RecordPcInfo(instruction_, instruction_->GetDexPc()); @@ -166,6 +168,34 @@ class BoundsCheckSlowPathX86_64 : public SlowPathCodeX86_64 { DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathX86_64); }; +class ClinitCheckSlowPathX86_64 : public SlowPathCodeX86_64 { + public: + explicit ClinitCheckSlowPathX86_64(HClinitCheck* instruction) : instruction_(instruction) {} + + virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + CodeGeneratorX86_64* x64_codegen = down_cast<CodeGeneratorX86_64*>(codegen); + __ Bind(GetEntryLabel()); + codegen->SaveLiveRegisters(instruction_->GetLocations()); + + HLoadClass* cls = instruction_->GetLoadClass(); + InvokeRuntimeCallingConvention calling_convention; + __ movl(CpuRegister(calling_convention.GetRegisterAt(0)), Immediate(cls->GetTypeIndex())); + x64_codegen->LoadCurrentMethod(CpuRegister(calling_convention.GetRegisterAt(1))); + __ gs()->call(Address::Absolute( + QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pInitializeStaticStorage), true)); + + codegen->RecordPcInfo(instruction_, instruction_->GetDexPc()); + x64_codegen->Move(instruction_->GetLocations()->InAt(0), Location::RegisterLocation(RAX)); + codegen->RestoreLiveRegisters(instruction_->GetLocations()); + __ jmp(GetExitLabel()); + } + + private: + HClinitCheck* const instruction_; + + DISALLOW_COPY_AND_ASSIGN(ClinitCheckSlowPathX86_64); +}; + #undef __ #define __ reinterpret_cast<X86_64Assembler*>(GetAssembler())-> @@ -314,7 +344,7 @@ void CodeGeneratorX86_64::Bind(HBasicBlock* block) { __ Bind(GetLabelOf(block)); } -void InstructionCodeGeneratorX86_64::LoadCurrentMethod(CpuRegister reg) { +void CodeGeneratorX86_64::LoadCurrentMethod(CpuRegister reg) { __ movl(reg, Address(CpuRegister(RSP), kCurrentMethodStackOffset)); } @@ -888,10 +918,6 @@ void LocationsBuilderX86_64::VisitInvokeStatic(HInvokeStatic* invoke) { void InstructionCodeGeneratorX86_64::VisitInvokeStatic(HInvokeStatic* invoke) { CpuRegister temp = invoke->GetLocations()->GetTemp(0).As<CpuRegister>(); - uint32_t heap_reference_size = sizeof(mirror::HeapReference<mirror::Object>); - size_t index_in_cache = mirror::Array::DataOffset(heap_reference_size).SizeValue() + - invoke->GetIndexInDexCache() * heap_reference_size; - // TODO: Implement all kinds of calls: // 1) boot -> boot // 2) app -> boot @@ -900,11 +926,11 @@ void InstructionCodeGeneratorX86_64::VisitInvokeStatic(HInvokeStatic* invoke) { // Currently we implement the app -> app logic, which looks up in the resolve cache. // temp = method; - LoadCurrentMethod(temp); + codegen_->LoadCurrentMethod(temp); // temp = temp->dex_cache_resolved_methods_; __ movl(temp, Address(temp, mirror::ArtMethod::DexCacheResolvedMethodsOffset().SizeValue())); // temp = temp[index_in_cache] - __ movl(temp, Address(temp, index_in_cache)); + __ movl(temp, Address(temp, CodeGenerator::GetCacheOffset(invoke->GetIndexInDexCache()))); // (temp + offset_of_quick_compiled_code)() __ call(Address(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().SizeValue())); @@ -1279,7 +1305,7 @@ void LocationsBuilderX86_64::VisitNewInstance(HNewInstance* instruction) { void InstructionCodeGeneratorX86_64::VisitNewInstance(HNewInstance* instruction) { InvokeRuntimeCallingConvention calling_convention; - LoadCurrentMethod(CpuRegister(calling_convention.GetRegisterAt(1))); + codegen_->LoadCurrentMethod(CpuRegister(calling_convention.GetRegisterAt(1))); __ movq(CpuRegister(calling_convention.GetRegisterAt(0)), Immediate(instruction->GetTypeIndex())); __ gs()->call(Address::Absolute( @@ -1301,7 +1327,7 @@ void LocationsBuilderX86_64::VisitNewArray(HNewArray* instruction) { void InstructionCodeGeneratorX86_64::VisitNewArray(HNewArray* instruction) { InvokeRuntimeCallingConvention calling_convention; - LoadCurrentMethod(CpuRegister(calling_convention.GetRegisterAt(1))); + codegen_->LoadCurrentMethod(CpuRegister(calling_convention.GetRegisterAt(1))); __ movq(CpuRegister(calling_convention.GetRegisterAt(0)), Immediate(instruction->GetTypeIndex())); __ gs()->call(Address::Absolute( @@ -2083,5 +2109,166 @@ void ParallelMoveResolverX86_64::RestoreScratch(int reg) { __ popq(CpuRegister(reg)); } +void LocationsBuilderX86_64::VisitLoadClass(HLoadClass* cls) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(cls, LocationSummary::kNoCall); + locations->SetOut(Location::RequiresRegister()); +} + +void InstructionCodeGeneratorX86_64::VisitLoadClass(HLoadClass* cls) { + CpuRegister out = cls->GetLocations()->Out().As<CpuRegister>(); + if (cls->IsReferrersClass()) { + codegen_->LoadCurrentMethod(out); + __ movl(out, Address(out, mirror::ArtMethod::DeclaringClassOffset().Int32Value())); + } else { + codegen_->LoadCurrentMethod(out); + __ movl(out, Address(out, mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value())); + __ movl(out, Address(out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex()))); + } +} + +void LocationsBuilderX86_64::VisitClinitCheck(HClinitCheck* check) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(check, LocationSummary::kCallOnSlowPath); + locations->SetInAt(0, Location::RequiresRegister()); + if (check->HasUses()) { + locations->SetOut(Location::SameAsFirstInput()); + } +} + +void InstructionCodeGeneratorX86_64::VisitClinitCheck(HClinitCheck* check) { + SlowPathCodeX86_64* slow_path = new (GetGraph()->GetArena()) ClinitCheckSlowPathX86_64(check); + codegen_->AddSlowPath(slow_path); + + LocationSummary* locations = check->GetLocations(); + // We remove the class as a live register, we know it's null or unused in the slow path. + RegisterSet* register_set = locations->GetLiveRegisters(); + register_set->Remove(locations->InAt(0)); + + CpuRegister class_reg = locations->InAt(0).As<CpuRegister>(); + __ testl(class_reg, class_reg); + __ j(kEqual, slow_path->GetEntryLabel()); + __ cmpl(Address(class_reg, mirror::Class::StatusOffset().Int32Value()), + Immediate(mirror::Class::kStatusInitialized)); + __ j(kLess, slow_path->GetEntryLabel()); + __ Bind(slow_path->GetExitLabel()); + // No need for memory fence, thanks to the X86_64 memory model. +} + +void LocationsBuilderX86_64::VisitStaticFieldGet(HStaticFieldGet* instruction) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); +} + +void InstructionCodeGeneratorX86_64::VisitStaticFieldGet(HStaticFieldGet* instruction) { + LocationSummary* locations = instruction->GetLocations(); + CpuRegister cls = locations->InAt(0).As<CpuRegister>(); + CpuRegister out = locations->Out().As<CpuRegister>(); + size_t offset = instruction->GetFieldOffset().SizeValue(); + + switch (instruction->GetType()) { + case Primitive::kPrimBoolean: { + __ movzxb(out, Address(cls, offset)); + break; + } + + case Primitive::kPrimByte: { + __ movsxb(out, Address(cls, offset)); + break; + } + + case Primitive::kPrimShort: { + __ movsxw(out, Address(cls, offset)); + break; + } + + case Primitive::kPrimChar: { + __ movzxw(out, Address(cls, offset)); + break; + } + + case Primitive::kPrimInt: + case Primitive::kPrimNot: { + __ movl(out, Address(cls, offset)); + break; + } + + case Primitive::kPrimLong: { + __ movq(out, Address(cls, offset)); + break; + } + + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + LOG(FATAL) << "Unimplemented register type " << instruction->GetType(); + UNREACHABLE(); + case Primitive::kPrimVoid: + LOG(FATAL) << "Unreachable type " << instruction->GetType(); + UNREACHABLE(); + } +} + +void LocationsBuilderX86_64::VisitStaticFieldSet(HStaticFieldSet* instruction) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + Primitive::Type field_type = instruction->GetFieldType(); + bool is_object_type = field_type == Primitive::kPrimNot; + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + if (is_object_type) { + // Temporary registers for the write barrier. + locations->AddTemp(Location::RequiresRegister()); + locations->AddTemp(Location::RequiresRegister()); + } +} + +void InstructionCodeGeneratorX86_64::VisitStaticFieldSet(HStaticFieldSet* instruction) { + LocationSummary* locations = instruction->GetLocations(); + CpuRegister cls = locations->InAt(0).As<CpuRegister>(); + CpuRegister value = locations->InAt(1).As<CpuRegister>(); + size_t offset = instruction->GetFieldOffset().SizeValue(); + Primitive::Type field_type = instruction->GetFieldType(); + + switch (field_type) { + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: { + __ movb(Address(cls, offset), value); + break; + } + + case Primitive::kPrimShort: + case Primitive::kPrimChar: { + __ movw(Address(cls, offset), value); + break; + } + + case Primitive::kPrimInt: + case Primitive::kPrimNot: { + __ movl(Address(cls, offset), value); + if (field_type == Primitive::kPrimNot) { + CpuRegister temp = locations->GetTemp(0).As<CpuRegister>(); + CpuRegister card = locations->GetTemp(1).As<CpuRegister>(); + codegen_->MarkGCCard(temp, card, cls, value); + } + break; + } + + case Primitive::kPrimLong: { + __ movq(Address(cls, offset), value); + break; + } + + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + LOG(FATAL) << "Unimplemented register type " << field_type; + UNREACHABLE(); + case Primitive::kPrimVoid: + LOG(FATAL) << "Unreachable type " << field_type; + UNREACHABLE(); + } +} + } // namespace x86_64 } // namespace art diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h index e04a8d8..32d2702 100644 --- a/compiler/optimizing/code_generator_x86_64.h +++ b/compiler/optimizing/code_generator_x86_64.h @@ -123,8 +123,6 @@ class InstructionCodeGeneratorX86_64 : public HGraphVisitor { #undef DECLARE_VISIT_INSTRUCTION - void LoadCurrentMethod(CpuRegister reg); - X86_64Assembler* GetAssembler() const { return assembler_; } private: @@ -192,6 +190,8 @@ class CodeGeneratorX86_64 : public CodeGenerator { // Helper method to move a value between two locations. void Move(Location destination, Location source); + void LoadCurrentMethod(CpuRegister reg); + Label* GetLabelOf(HBasicBlock* block) const { return block_labels_.GetRawStorage() + block->GetBlockId(); } diff --git a/compiler/optimizing/locations.h b/compiler/optimizing/locations.h index de8c78d..94aded6 100644 --- a/compiler/optimizing/locations.h +++ b/compiler/optimizing/locations.h @@ -360,6 +360,15 @@ class RegisterSet : public ValueObject { } } + void Remove(Location loc) { + if (loc.IsRegister()) { + core_registers_ &= ~(1 << loc.reg()); + } else { + DCHECK(loc.IsFpuRegister()); + floating_point_registers_ &= ~(1 << loc.reg()); + } + } + bool ContainsCoreRegister(uint32_t id) { return Contains(core_registers_, id); } diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index f530708..86c36b8 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -472,6 +472,7 @@ class HBasicBlock : public ArenaObject { M(ArrayLength, Instruction) \ M(ArraySet, Instruction) \ M(BoundsCheck, Instruction) \ + M(ClinitCheck, Instruction) \ M(Compare, BinaryOperation) \ M(Condition, BinaryOperation) \ M(Div, BinaryOperation) \ @@ -488,6 +489,7 @@ class HBasicBlock : public ArenaObject { M(IntConstant, Constant) \ M(InvokeStatic, Invoke) \ M(InvokeVirtual, Invoke) \ + M(LoadClass, Instruction) \ M(LessThan, Condition) \ M(LessThanOrEqual, Condition) \ M(LoadLocal, Instruction) \ @@ -505,6 +507,8 @@ class HBasicBlock : public ArenaObject { M(Phi, Instruction) \ M(Return, Instruction) \ M(ReturnVoid, Instruction) \ + M(StaticFieldGet, Instruction) \ + M(StaticFieldSet, Instruction) \ M(StoreLocal, Instruction) \ M(Sub, BinaryOperation) \ M(SuspendCheck, Instruction) \ @@ -2017,6 +2021,132 @@ class HSuspendCheck : public HTemplateInstruction<0> { DISALLOW_COPY_AND_ASSIGN(HSuspendCheck); }; +// TODO: Make this class handle the case the load is null (dex cache +// is null). +/** + * Instruction to load a Class object. + */ +class HLoadClass : public HExpression<0> { + public: + HLoadClass(uint16_t type_index, + bool is_referrers_class, + bool is_initialized, + uint32_t dex_pc) + : HExpression(Primitive::kPrimNot, SideEffects::None()), + type_index_(type_index), + is_referrers_class_(is_referrers_class), + is_initialized_(is_initialized), + dex_pc_(dex_pc) {} + + bool InstructionDataEquals(HInstruction* other) const OVERRIDE { + return other->AsLoadClass()->type_index_ == type_index_; + } + + size_t ComputeHashCode() const OVERRIDE { return type_index_; } + + uint32_t GetDexPc() const { return dex_pc_; } + uint16_t GetTypeIndex() const { return type_index_; } + + bool NeedsInitialization() const { + return !is_initialized_ && !is_referrers_class_; + } + + bool IsReferrersClass() const { return is_referrers_class_; } + + DECLARE_INSTRUCTION(LoadClass); + + private: + const uint16_t type_index_; + const bool is_referrers_class_; + const bool is_initialized_; + const uint32_t dex_pc_; + + DISALLOW_COPY_AND_ASSIGN(HLoadClass); +}; + +// TODO: Pass this check to HInvokeStatic nodes. +/** + * Performs an initialization check on its Class object input. + */ +class HClinitCheck : public HExpression<1> { + public: + explicit HClinitCheck(HLoadClass* constant, uint32_t dex_pc) + : HExpression(Primitive::kPrimNot, SideEffects::All()), + dex_pc_(dex_pc) { + SetRawInputAt(0, constant); + } + + bool NeedsEnvironment() const OVERRIDE { + // May call runtime to initialize the class. + return true; + } + + uint32_t GetDexPc() const { return dex_pc_; } + + HLoadClass* GetLoadClass() const { return InputAt(0)->AsLoadClass(); } + + DECLARE_INSTRUCTION(ClinitCheck); + + private: + const uint32_t dex_pc_; + + DISALLOW_COPY_AND_ASSIGN(HClinitCheck); +}; + +class HStaticFieldGet : public HExpression<1> { + public: + HStaticFieldGet(HInstruction* cls, + Primitive::Type field_type, + MemberOffset field_offset) + : HExpression(field_type, SideEffects::DependsOnSomething()), + field_info_(field_offset, field_type) { + SetRawInputAt(0, cls); + } + + bool CanBeMoved() const OVERRIDE { return true; } + bool InstructionDataEquals(HInstruction* other) const OVERRIDE { + size_t other_offset = other->AsStaticFieldGet()->GetFieldOffset().SizeValue(); + return other_offset == GetFieldOffset().SizeValue(); + } + + size_t ComputeHashCode() const OVERRIDE { + return (HInstruction::ComputeHashCode() << 7) | GetFieldOffset().SizeValue(); + } + + MemberOffset GetFieldOffset() const { return field_info_.GetFieldOffset(); } + Primitive::Type GetFieldType() const { return field_info_.GetFieldType(); } + + DECLARE_INSTRUCTION(StaticFieldGet); + + private: + const FieldInfo field_info_; + + DISALLOW_COPY_AND_ASSIGN(HStaticFieldGet); +}; + +class HStaticFieldSet : public HTemplateInstruction<2> { + public: + HStaticFieldSet(HInstruction* cls, + HInstruction* value, + Primitive::Type field_type, + MemberOffset field_offset) + : HTemplateInstruction(SideEffects::ChangesSomething()), + field_info_(field_offset, field_type) { + SetRawInputAt(0, cls); + SetRawInputAt(1, value); + } + + MemberOffset GetFieldOffset() const { return field_info_.GetFieldOffset(); } + Primitive::Type GetFieldType() const { return field_info_.GetFieldType(); } + + DECLARE_INSTRUCTION(StaticFieldSet); + + private: + const FieldInfo field_info_; + + DISALLOW_COPY_AND_ASSIGN(HStaticFieldSet); +}; + class MoveOperands : public ArenaObject { public: MoveOperands(Location source, Location destination, HInstruction* instruction) diff --git a/compiler/optimizing/prepare_for_register_allocation.cc b/compiler/optimizing/prepare_for_register_allocation.cc index a81dc1b..2387141 100644 --- a/compiler/optimizing/prepare_for_register_allocation.cc +++ b/compiler/optimizing/prepare_for_register_allocation.cc @@ -37,6 +37,10 @@ void PrepareForRegisterAllocation::VisitBoundsCheck(HBoundsCheck* check) { check->ReplaceWith(check->InputAt(0)); } +void PrepareForRegisterAllocation::VisitClinitCheck(HClinitCheck* check) { + check->ReplaceWith(check->InputAt(0)); +} + void PrepareForRegisterAllocation::VisitCondition(HCondition* condition) { bool needs_materialization = false; if (!condition->HasOnlyOneUse()) { diff --git a/compiler/optimizing/prepare_for_register_allocation.h b/compiler/optimizing/prepare_for_register_allocation.h index e86a39b..3e63ecb 100644 --- a/compiler/optimizing/prepare_for_register_allocation.h +++ b/compiler/optimizing/prepare_for_register_allocation.h @@ -35,6 +35,7 @@ class PrepareForRegisterAllocation : public HGraphDelegateVisitor { private: virtual void VisitNullCheck(HNullCheck* check) OVERRIDE; virtual void VisitBoundsCheck(HBoundsCheck* check) OVERRIDE; + virtual void VisitClinitCheck(HClinitCheck* check) OVERRIDE; virtual void VisitCondition(HCondition* condition) OVERRIDE; DISALLOW_COPY_AND_ASSIGN(PrepareForRegisterAllocation); diff --git a/compiler/utils/arm/assembler_arm.h b/compiler/utils/arm/assembler_arm.h index 14d48b7..c1ed6a2 100644 --- a/compiler/utils/arm/assembler_arm.h +++ b/compiler/utils/arm/assembler_arm.h @@ -523,6 +523,9 @@ class ArmAssembler : public Assembler { virtual void blx(Register rm, Condition cond = AL) = 0; virtual void bx(Register rm, Condition cond = AL) = 0; + // Memory barriers. + virtual void dmb(DmbOptions flavor) = 0; + void Pad(uint32_t bytes); // Macros. diff --git a/compiler/utils/arm/assembler_arm32.cc b/compiler/utils/arm/assembler_arm32.cc index 3d46617..d262b6a 100644 --- a/compiler/utils/arm/assembler_arm32.cc +++ b/compiler/utils/arm/assembler_arm32.cc @@ -1471,9 +1471,14 @@ void Arm32Assembler::StoreDToOffset(DRegister reg, void Arm32Assembler::MemoryBarrier(ManagedRegister mscratch) { CHECK_EQ(mscratch.AsArm().AsCoreRegister(), R12); + dmb(SY); +} + + +void Arm32Assembler::dmb(DmbOptions flavor) { #if ANDROID_SMP != 0 int32_t encoding = 0xf57ff05f; // dmb - Emit(encoding); + Emit(encoding | flavor); #endif } diff --git a/compiler/utils/arm/assembler_arm32.h b/compiler/utils/arm/assembler_arm32.h index c89fd04..cfc300b 100644 --- a/compiler/utils/arm/assembler_arm32.h +++ b/compiler/utils/arm/assembler_arm32.h @@ -228,6 +228,8 @@ class Arm32Assembler FINAL : public ArmAssembler { void CompareAndBranchIfZero(Register r, Label* label) OVERRIDE; void CompareAndBranchIfNonZero(Register r, Label* label) OVERRIDE; + // Memory barriers. + void dmb(DmbOptions flavor) OVERRIDE; // Macros. // Add signed constant value to rd. May clobber IP. diff --git a/compiler/utils/arm/assembler_thumb2.cc b/compiler/utils/arm/assembler_thumb2.cc index 37478c4..633f55b 100644 --- a/compiler/utils/arm/assembler_thumb2.cc +++ b/compiler/utils/arm/assembler_thumb2.cc @@ -2521,9 +2521,14 @@ void Thumb2Assembler::StoreDToOffset(DRegister reg, void Thumb2Assembler::MemoryBarrier(ManagedRegister mscratch) { CHECK_EQ(mscratch.AsArm().AsCoreRegister(), R12); + dmb(SY); +} + + +void Thumb2Assembler::dmb(DmbOptions flavor) { #if ANDROID_SMP != 0 - int32_t encoding = 0xf3bf8f5f; // dmb in T1 encoding. - Emit32(encoding); + int32_t encoding = 0xf3bf8f50; // dmb in T1 encoding. + Emit32(encoding | flavor); #endif } diff --git a/compiler/utils/arm/assembler_thumb2.h b/compiler/utils/arm/assembler_thumb2.h index 9e7d394..b26173f 100644 --- a/compiler/utils/arm/assembler_thumb2.h +++ b/compiler/utils/arm/assembler_thumb2.h @@ -259,6 +259,9 @@ class Thumb2Assembler FINAL : public ArmAssembler { void CompareAndBranchIfZero(Register r, Label* label) OVERRIDE; void CompareAndBranchIfNonZero(Register r, Label* label) OVERRIDE; + // Memory barriers. + void dmb(DmbOptions flavor) OVERRIDE; + // Macros. // Add signed constant value to rd. May clobber IP. void AddConstant(Register rd, int32_t value, Condition cond = AL) OVERRIDE; diff --git a/compiler/utils/arm/constants_arm.h b/compiler/utils/arm/constants_arm.h index 092c891..702e03a 100644 --- a/compiler/utils/arm/constants_arm.h +++ b/compiler/utils/arm/constants_arm.h @@ -47,6 +47,16 @@ namespace arm { #error "Exactly one of VFPv3_D16 or VFPv3_D32 can be defined at a time." #endif +// 4 bits option for the dmb instruction. +// Order and values follows those of the ARM Architecture Reference Manual. +enum DmbOptions { + SY = 0xf, + ST = 0xe, + ISH = 0xb, + ISHST = 0xa, + NSH = 0x7, + NSHST = 0x6 +}; enum ScaleFactor { TIMES_1 = 0, |