/* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "code_generator_arm.h" #include "entrypoints/quick/quick_entrypoints.h" #include "gc/accounting/card_table.h" #include "mirror/array-inl.h" #include "mirror/art_method.h" #include "mirror/class.h" #include "thread.h" #include "utils/assembler.h" #include "utils/arm/assembler_arm.h" #include "utils/arm/managed_register_arm.h" #include "utils/stack_checks.h" namespace art { namespace arm { static DRegister FromLowSToD(SRegister reg) { DCHECK_EQ(reg % 2, 0); return static_cast(reg / 2); } static constexpr bool kExplicitStackOverflowCheck = false; static constexpr int kNumberOfPushedRegistersAtEntry = 1 + 2; // LR, R6, R7 static constexpr int kCurrentMethodStackOffset = 0; static constexpr Register kRuntimeParameterCoreRegisters[] = { R0, R1, R2 }; static constexpr size_t kRuntimeParameterCoreRegistersLength = arraysize(kRuntimeParameterCoreRegisters); static constexpr SRegister kRuntimeParameterFpuRegisters[] = { }; static constexpr size_t kRuntimeParameterFpuRegistersLength = 0; class InvokeRuntimeCallingConvention : public CallingConvention { public: InvokeRuntimeCallingConvention() : CallingConvention(kRuntimeParameterCoreRegisters, kRuntimeParameterCoreRegistersLength, kRuntimeParameterFpuRegisters, kRuntimeParameterFpuRegistersLength) {} private: DISALLOW_COPY_AND_ASSIGN(InvokeRuntimeCallingConvention); }; #define __ reinterpret_cast(codegen->GetAssembler())-> #define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kArmWordSize, x).Int32Value() class SlowPathCodeARM : public SlowPathCode { public: SlowPathCodeARM() : entry_label_(), exit_label_() {} Label* GetEntryLabel() { return &entry_label_; } Label* GetExitLabel() { return &exit_label_; } private: Label entry_label_; Label exit_label_; DISALLOW_COPY_AND_ASSIGN(SlowPathCodeARM); }; class NullCheckSlowPathARM : public SlowPathCodeARM { public: explicit NullCheckSlowPathARM(HNullCheck* instruction) : instruction_(instruction) {} virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorARM* arm_codegen = down_cast(codegen); __ Bind(GetEntryLabel()); arm_codegen->InvokeRuntime( QUICK_ENTRY_POINT(pThrowNullPointer), instruction_, instruction_->GetDexPc()); } private: HNullCheck* const instruction_; DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathARM); }; class StackOverflowCheckSlowPathARM : public SlowPathCodeARM { public: StackOverflowCheckSlowPathARM() {} virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { __ Bind(GetEntryLabel()); __ LoadFromOffset(kLoadWord, PC, TR, QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pThrowStackOverflow).Int32Value()); } private: DISALLOW_COPY_AND_ASSIGN(StackOverflowCheckSlowPathARM); }; class SuspendCheckSlowPathARM : public SlowPathCodeARM { public: explicit SuspendCheckSlowPathARM(HSuspendCheck* instruction, HBasicBlock* successor) : instruction_(instruction), successor_(successor) {} virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorARM* arm_codegen = down_cast(codegen); __ Bind(GetEntryLabel()); codegen->SaveLiveRegisters(instruction_->GetLocations()); arm_codegen->InvokeRuntime( QUICK_ENTRY_POINT(pTestSuspend), instruction_, instruction_->GetDexPc()); codegen->RestoreLiveRegisters(instruction_->GetLocations()); if (successor_ == nullptr) { __ b(GetReturnLabel()); } else { __ b(arm_codegen->GetLabelOf(successor_)); } } Label* GetReturnLabel() { DCHECK(successor_ == nullptr); return &return_label_; } private: HSuspendCheck* const instruction_; // If not null, the block to branch to after the suspend check. HBasicBlock* const successor_; // If `successor_` is null, the label to branch to after the suspend check. Label return_label_; DISALLOW_COPY_AND_ASSIGN(SuspendCheckSlowPathARM); }; class BoundsCheckSlowPathARM : public SlowPathCodeARM { public: BoundsCheckSlowPathARM(HBoundsCheck* instruction, Location index_location, Location length_location) : instruction_(instruction), index_location_(index_location), length_location_(length_location) {} virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorARM* arm_codegen = down_cast(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_); arm_codegen->InvokeRuntime( QUICK_ENTRY_POINT(pThrowArrayBounds), instruction_, instruction_->GetDexPc()); } private: HBoundsCheck* const instruction_; const Location index_location_; const Location length_location_; 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(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); }; class LoadStringSlowPathARM : public SlowPathCodeARM { public: explicit LoadStringSlowPathARM(HLoadString* instruction) : instruction_(instruction) {} virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { LocationSummary* locations = instruction_->GetLocations(); DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg())); CodeGeneratorARM* arm_codegen = down_cast(codegen); __ Bind(GetEntryLabel()); codegen->SaveLiveRegisters(locations); InvokeRuntimeCallingConvention calling_convention; arm_codegen->LoadCurrentMethod(calling_convention.GetRegisterAt(0)); __ LoadImmediate(calling_convention.GetRegisterAt(1), instruction_->GetStringIndex()); arm_codegen->InvokeRuntime( QUICK_ENTRY_POINT(pResolveString), instruction_, instruction_->GetDexPc()); arm_codegen->Move32(locations->Out(), Location::RegisterLocation(R0)); codegen->RestoreLiveRegisters(locations); __ b(GetExitLabel()); } private: HLoadString* const instruction_; DISALLOW_COPY_AND_ASSIGN(LoadStringSlowPathARM); }; #undef __ #undef __ #define __ reinterpret_cast(GetAssembler())-> inline Condition ARMCondition(IfCondition cond) { switch (cond) { case kCondEQ: return EQ; case kCondNE: return NE; case kCondLT: return LT; case kCondLE: return LE; case kCondGT: return GT; case kCondGE: return GE; default: LOG(FATAL) << "Unknown if condition"; } return EQ; // Unreachable. } inline Condition ARMOppositeCondition(IfCondition cond) { switch (cond) { case kCondEQ: return NE; case kCondNE: return EQ; case kCondLT: return GE; case kCondLE: return GT; case kCondGT: return LE; case kCondGE: return LT; default: LOG(FATAL) << "Unknown if condition"; } return EQ; // Unreachable. } void CodeGeneratorARM::DumpCoreRegister(std::ostream& stream, int reg) const { stream << ArmManagedRegister::FromCoreRegister(Register(reg)); } void CodeGeneratorARM::DumpFloatingPointRegister(std::ostream& stream, int reg) const { stream << ArmManagedRegister::FromSRegister(SRegister(reg)); } size_t CodeGeneratorARM::SaveCoreRegister(size_t stack_index, uint32_t reg_id) { __ StoreToOffset(kStoreWord, static_cast(reg_id), SP, stack_index); return kArmWordSize; } size_t CodeGeneratorARM::RestoreCoreRegister(size_t stack_index, uint32_t reg_id) { __ LoadFromOffset(kLoadWord, static_cast(reg_id), SP, stack_index); return kArmWordSize; } CodeGeneratorARM::CodeGeneratorARM(HGraph* graph) : CodeGenerator(graph, kNumberOfCoreRegisters, kNumberOfSRegisters, kNumberOfRegisterPairs), block_labels_(graph->GetArena(), 0), location_builder_(graph, this), instruction_visitor_(graph, this), move_resolver_(graph->GetArena(), this), assembler_(true) {} size_t CodeGeneratorARM::FrameEntrySpillSize() const { return kNumberOfPushedRegistersAtEntry * kArmWordSize; } Location CodeGeneratorARM::AllocateFreeRegister(Primitive::Type type) const { switch (type) { case Primitive::kPrimLong: { size_t reg = FindFreeEntry(blocked_register_pairs_, kNumberOfRegisterPairs); ArmManagedRegister pair = ArmManagedRegister::FromRegisterPair(static_cast(reg)); DCHECK(!blocked_core_registers_[pair.AsRegisterPairLow()]); DCHECK(!blocked_core_registers_[pair.AsRegisterPairHigh()]); blocked_core_registers_[pair.AsRegisterPairLow()] = true; blocked_core_registers_[pair.AsRegisterPairHigh()] = true; UpdateBlockedPairRegisters(); return Location::RegisterPairLocation(pair.AsRegisterPairLow(), pair.AsRegisterPairHigh()); } case Primitive::kPrimByte: case Primitive::kPrimBoolean: case Primitive::kPrimChar: case Primitive::kPrimShort: case Primitive::kPrimInt: case Primitive::kPrimNot: { int reg = FindFreeEntry(blocked_core_registers_, kNumberOfCoreRegisters); // Block all register pairs that contain `reg`. for (int i = 0; i < kNumberOfRegisterPairs; i++) { ArmManagedRegister current = ArmManagedRegister::FromRegisterPair(static_cast(i)); if (current.AsRegisterPairLow() == reg || current.AsRegisterPairHigh() == reg) { blocked_register_pairs_[i] = true; } } return Location::RegisterLocation(reg); } case Primitive::kPrimFloat: { int reg = FindFreeEntry(blocked_fpu_registers_, kNumberOfSRegisters); return Location::FpuRegisterLocation(reg); } case Primitive::kPrimDouble: { int reg = FindTwoFreeConsecutiveAlignedEntries(blocked_fpu_registers_, kNumberOfSRegisters); DCHECK_EQ(reg % 2, 0); return Location::FpuRegisterPairLocation(reg, reg + 1); } case Primitive::kPrimVoid: LOG(FATAL) << "Unreachable type " << type; } return Location(); } void CodeGeneratorARM::SetupBlockedRegisters() const { // Don't allocate the dalvik style register pair passing. blocked_register_pairs_[R1_R2] = true; // Stack register, LR and PC are always reserved. blocked_core_registers_[SP] = true; blocked_core_registers_[LR] = true; blocked_core_registers_[PC] = true; // Reserve R4 for suspend check. blocked_core_registers_[R4] = true; // Reserve thread register. blocked_core_registers_[TR] = true; // Reserve temp register. blocked_core_registers_[IP] = true; // TODO: We currently don't use Quick's callee saved registers. // We always save and restore R6 and R7 to make sure we can use three // register pairs for long operations. blocked_core_registers_[R5] = true; blocked_core_registers_[R8] = true; blocked_core_registers_[R10] = true; blocked_core_registers_[R11] = true; blocked_fpu_registers_[S16] = true; blocked_fpu_registers_[S17] = true; blocked_fpu_registers_[S18] = true; blocked_fpu_registers_[S19] = true; blocked_fpu_registers_[S20] = true; blocked_fpu_registers_[S21] = true; blocked_fpu_registers_[S22] = true; blocked_fpu_registers_[S23] = true; blocked_fpu_registers_[S24] = true; blocked_fpu_registers_[S25] = true; blocked_fpu_registers_[S26] = true; blocked_fpu_registers_[S27] = true; blocked_fpu_registers_[S28] = true; blocked_fpu_registers_[S29] = true; blocked_fpu_registers_[S30] = true; blocked_fpu_registers_[S31] = true; UpdateBlockedPairRegisters(); } void CodeGeneratorARM::UpdateBlockedPairRegisters() const { for (int i = 0; i < kNumberOfRegisterPairs; i++) { ArmManagedRegister current = ArmManagedRegister::FromRegisterPair(static_cast(i)); if (blocked_core_registers_[current.AsRegisterPairLow()] || blocked_core_registers_[current.AsRegisterPairHigh()]) { blocked_register_pairs_[i] = true; } } } InstructionCodeGeneratorARM::InstructionCodeGeneratorARM(HGraph* graph, CodeGeneratorARM* codegen) : HGraphVisitor(graph), assembler_(codegen->GetAssembler()), codegen_(codegen) {} void CodeGeneratorARM::GenerateFrameEntry() { bool skip_overflow_check = IsLeafMethod() && !FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kArm); if (!skip_overflow_check) { if (kExplicitStackOverflowCheck) { SlowPathCodeARM* slow_path = new (GetGraph()->GetArena()) StackOverflowCheckSlowPathARM(); AddSlowPath(slow_path); __ LoadFromOffset(kLoadWord, IP, TR, Thread::StackEndOffset().Int32Value()); __ cmp(SP, ShifterOperand(IP)); __ b(slow_path->GetEntryLabel(), CC); } else { __ AddConstant(IP, SP, -static_cast(GetStackOverflowReservedBytes(kArm))); __ LoadFromOffset(kLoadWord, IP, IP, 0); RecordPcInfo(nullptr, 0); } } core_spill_mask_ |= (1 << LR | 1 << R6 | 1 << R7); __ PushList(1 << LR | 1 << R6 | 1 << R7); // The return PC has already been pushed on the stack. __ AddConstant(SP, -(GetFrameSize() - kNumberOfPushedRegistersAtEntry * kArmWordSize)); __ StoreToOffset(kStoreWord, R0, SP, 0); } void CodeGeneratorARM::GenerateFrameExit() { __ AddConstant(SP, GetFrameSize() - kNumberOfPushedRegistersAtEntry * kArmWordSize); __ PopList(1 << PC | 1 << R6 | 1 << R7); } void CodeGeneratorARM::Bind(HBasicBlock* block) { __ Bind(GetLabelOf(block)); } Location CodeGeneratorARM::GetStackLocation(HLoadLocal* load) const { switch (load->GetType()) { case Primitive::kPrimLong: case Primitive::kPrimDouble: return Location::DoubleStackSlot(GetStackSlot(load->GetLocal())); break; case Primitive::kPrimInt: case Primitive::kPrimNot: case Primitive::kPrimFloat: return Location::StackSlot(GetStackSlot(load->GetLocal())); case Primitive::kPrimBoolean: case Primitive::kPrimByte: case Primitive::kPrimChar: case Primitive::kPrimShort: case Primitive::kPrimVoid: LOG(FATAL) << "Unexpected type " << load->GetType(); } LOG(FATAL) << "Unreachable"; return Location(); } Location InvokeDexCallingConventionVisitor::GetNextLocation(Primitive::Type type) { switch (type) { case Primitive::kPrimBoolean: case Primitive::kPrimByte: case Primitive::kPrimChar: case Primitive::kPrimShort: case Primitive::kPrimInt: case Primitive::kPrimNot: { uint32_t index = gp_index_++; uint32_t stack_index = stack_index_++; if (index < calling_convention.GetNumberOfRegisters()) { return Location::RegisterLocation(calling_convention.GetRegisterAt(index)); } else { return Location::StackSlot(calling_convention.GetStackOffsetOf(stack_index)); } } case Primitive::kPrimLong: { uint32_t index = gp_index_; uint32_t stack_index = stack_index_; gp_index_ += 2; stack_index_ += 2; if (index + 1 < calling_convention.GetNumberOfRegisters()) { ArmManagedRegister pair = ArmManagedRegister::FromRegisterPair( calling_convention.GetRegisterPairAt(index)); return Location::RegisterPairLocation(pair.AsRegisterPairLow(), pair.AsRegisterPairHigh()); } else if (index + 1 == calling_convention.GetNumberOfRegisters()) { return Location::QuickParameter(index, stack_index); } else { return Location::DoubleStackSlot(calling_convention.GetStackOffsetOf(stack_index)); } } case Primitive::kPrimFloat: { uint32_t stack_index = stack_index_++; if (float_index_ % 2 == 0) { float_index_ = std::max(double_index_, float_index_); } if (float_index_ < calling_convention.GetNumberOfFpuRegisters()) { return Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(float_index_++)); } else { return Location::StackSlot(calling_convention.GetStackOffsetOf(stack_index)); } } case Primitive::kPrimDouble: { double_index_ = std::max(double_index_, RoundUp(float_index_, 2)); uint32_t stack_index = stack_index_; stack_index_ += 2; if (double_index_ + 1 < calling_convention.GetNumberOfFpuRegisters()) { uint32_t index = double_index_; double_index_ += 2; return Location::FpuRegisterPairLocation( calling_convention.GetFpuRegisterAt(index), calling_convention.GetFpuRegisterAt(index + 1)); } else { return Location::DoubleStackSlot(calling_convention.GetStackOffsetOf(stack_index)); } } case Primitive::kPrimVoid: LOG(FATAL) << "Unexpected parameter type " << type; break; } return Location(); } Location InvokeDexCallingConventionVisitor::GetReturnLocation(Primitive::Type type) { switch (type) { case Primitive::kPrimBoolean: case Primitive::kPrimByte: case Primitive::kPrimChar: case Primitive::kPrimShort: case Primitive::kPrimInt: case Primitive::kPrimNot: { return Location::RegisterLocation(R0); } case Primitive::kPrimFloat: { return Location::FpuRegisterLocation(S0); } case Primitive::kPrimLong: { return Location::RegisterPairLocation(R0, R1); } case Primitive::kPrimDouble: { return Location::FpuRegisterPairLocation(S0, S1); } case Primitive::kPrimVoid: return Location(); } UNREACHABLE(); return Location(); } void CodeGeneratorARM::Move32(Location destination, Location source) { if (source.Equals(destination)) { return; } if (destination.IsRegister()) { if (source.IsRegister()) { __ Mov(destination.As(), source.As()); } else if (source.IsFpuRegister()) { __ vmovrs(destination.As(), source.As()); } else { __ LoadFromOffset(kLoadWord, destination.As(), SP, source.GetStackIndex()); } } else if (destination.IsFpuRegister()) { if (source.IsRegister()) { __ vmovsr(destination.As(), source.As()); } else if (source.IsFpuRegister()) { __ vmovs(destination.As(), source.As()); } else { __ LoadSFromOffset(destination.As(), SP, source.GetStackIndex()); } } else { DCHECK(destination.IsStackSlot()); if (source.IsRegister()) { __ StoreToOffset(kStoreWord, source.As(), SP, destination.GetStackIndex()); } else if (source.IsFpuRegister()) { __ StoreSToOffset(source.As(), SP, destination.GetStackIndex()); } else { DCHECK(source.IsStackSlot()); __ LoadFromOffset(kLoadWord, IP, SP, source.GetStackIndex()); __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex()); } } } void CodeGeneratorARM::Move64(Location destination, Location source) { if (source.Equals(destination)) { return; } if (destination.IsRegisterPair()) { if (source.IsRegisterPair()) { __ Mov(destination.AsRegisterPairLow(), source.AsRegisterPairLow()); __ Mov(destination.AsRegisterPairHigh(), source.AsRegisterPairHigh()); } else if (source.IsFpuRegister()) { UNIMPLEMENTED(FATAL); } else if (source.IsQuickParameter()) { uint16_t register_index = source.GetQuickParameterRegisterIndex(); uint16_t stack_index = source.GetQuickParameterStackIndex(); InvokeDexCallingConvention calling_convention; __ Mov(destination.AsRegisterPairLow(), calling_convention.GetRegisterAt(register_index)); __ LoadFromOffset(kLoadWord, destination.AsRegisterPairHigh(), SP, calling_convention.GetStackOffsetOf(stack_index + 1) + GetFrameSize()); } else { DCHECK(source.IsDoubleStackSlot()); if (destination.AsRegisterPairLow() == R1) { DCHECK_EQ(destination.AsRegisterPairHigh(), R2); __ LoadFromOffset(kLoadWord, R1, SP, source.GetStackIndex()); __ LoadFromOffset(kLoadWord, R2, SP, source.GetHighStackIndex(kArmWordSize)); } else { __ LoadFromOffset(kLoadWordPair, destination.AsRegisterPairLow(), SP, source.GetStackIndex()); } } } else if (destination.IsFpuRegisterPair()) { if (source.IsDoubleStackSlot()) { __ LoadDFromOffset(FromLowSToD(destination.AsFpuRegisterPairLow()), SP, source.GetStackIndex()); } else { UNIMPLEMENTED(FATAL); } } else if (destination.IsQuickParameter()) { InvokeDexCallingConvention calling_convention; uint16_t register_index = destination.GetQuickParameterRegisterIndex(); uint16_t stack_index = destination.GetQuickParameterStackIndex(); if (source.IsRegisterPair()) { __ Mov(calling_convention.GetRegisterAt(register_index), source.AsRegisterPairLow()); __ StoreToOffset(kStoreWord, source.AsRegisterPairHigh(), SP, calling_convention.GetStackOffsetOf(stack_index + 1)); } else if (source.IsFpuRegister()) { UNIMPLEMENTED(FATAL); } else { DCHECK(source.IsDoubleStackSlot()); __ LoadFromOffset( kLoadWord, calling_convention.GetRegisterAt(register_index), SP, source.GetStackIndex()); __ LoadFromOffset(kLoadWord, R0, SP, source.GetHighStackIndex(kArmWordSize)); __ StoreToOffset(kStoreWord, R0, SP, calling_convention.GetStackOffsetOf(stack_index + 1)); } } else { DCHECK(destination.IsDoubleStackSlot()); if (source.IsRegisterPair()) { if (source.AsRegisterPairLow() == R1) { DCHECK_EQ(source.AsRegisterPairHigh(), R2); __ StoreToOffset(kStoreWord, R1, SP, destination.GetStackIndex()); __ StoreToOffset(kStoreWord, R2, SP, destination.GetHighStackIndex(kArmWordSize)); } else { __ StoreToOffset(kStoreWordPair, source.AsRegisterPairLow(), SP, destination.GetStackIndex()); } } else if (source.IsQuickParameter()) { InvokeDexCallingConvention calling_convention; uint16_t register_index = source.GetQuickParameterRegisterIndex(); uint16_t stack_index = source.GetQuickParameterStackIndex(); __ StoreToOffset(kStoreWord, calling_convention.GetRegisterAt(register_index), SP, destination.GetStackIndex()); __ LoadFromOffset(kLoadWord, R0, SP, calling_convention.GetStackOffsetOf(stack_index + 1) + GetFrameSize()); __ StoreToOffset(kStoreWord, R0, SP, destination.GetHighStackIndex(kArmWordSize)); } else if (source.IsFpuRegisterPair()) { __ StoreDToOffset(FromLowSToD(source.AsFpuRegisterPairLow()), SP, destination.GetStackIndex()); } else { DCHECK(source.IsDoubleStackSlot()); __ LoadFromOffset(kLoadWord, IP, SP, source.GetStackIndex()); __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex()); __ LoadFromOffset(kLoadWord, IP, SP, source.GetHighStackIndex(kArmWordSize)); __ StoreToOffset(kStoreWord, IP, SP, destination.GetHighStackIndex(kArmWordSize)); } } } void CodeGeneratorARM::Move(HInstruction* instruction, Location location, HInstruction* move_for) { LocationSummary* locations = instruction->GetLocations(); if (locations != nullptr && locations->Out().Equals(location)) { return; } if (instruction->IsIntConstant()) { int32_t value = instruction->AsIntConstant()->GetValue(); if (location.IsRegister()) { __ LoadImmediate(location.As(), value); } else { DCHECK(location.IsStackSlot()); __ LoadImmediate(IP, value); __ StoreToOffset(kStoreWord, IP, SP, location.GetStackIndex()); } } else if (instruction->IsLongConstant()) { int64_t value = instruction->AsLongConstant()->GetValue(); if (location.IsRegisterPair()) { __ LoadImmediate(location.AsRegisterPairLow(), Low32Bits(value)); __ LoadImmediate(location.AsRegisterPairHigh(), High32Bits(value)); } else { DCHECK(location.IsDoubleStackSlot()); __ LoadImmediate(IP, Low32Bits(value)); __ StoreToOffset(kStoreWord, IP, SP, location.GetStackIndex()); __ LoadImmediate(IP, High32Bits(value)); __ StoreToOffset(kStoreWord, IP, SP, location.GetHighStackIndex(kArmWordSize)); } } else if (instruction->IsLoadLocal()) { uint32_t stack_slot = GetStackSlot(instruction->AsLoadLocal()->GetLocal()); switch (instruction->GetType()) { case Primitive::kPrimBoolean: case Primitive::kPrimByte: case Primitive::kPrimChar: case Primitive::kPrimShort: case Primitive::kPrimInt: case Primitive::kPrimNot: case Primitive::kPrimFloat: Move32(location, Location::StackSlot(stack_slot)); break; case Primitive::kPrimLong: case Primitive::kPrimDouble: Move64(location, Location::DoubleStackSlot(stack_slot)); break; default: LOG(FATAL) << "Unexpected type " << instruction->GetType(); } } else { DCHECK((instruction->GetNext() == move_for) || instruction->GetNext()->IsTemporary()); switch (instruction->GetType()) { case Primitive::kPrimBoolean: case Primitive::kPrimByte: case Primitive::kPrimChar: case Primitive::kPrimShort: case Primitive::kPrimNot: case Primitive::kPrimInt: case Primitive::kPrimFloat: Move32(location, locations->Out()); break; case Primitive::kPrimLong: case Primitive::kPrimDouble: Move64(location, locations->Out()); break; default: LOG(FATAL) << "Unexpected type " << instruction->GetType(); } } } 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); } void InstructionCodeGeneratorARM::VisitGoto(HGoto* got) { HBasicBlock* successor = got->GetSuccessor(); DCHECK(!successor->IsExitBlock()); HBasicBlock* block = got->GetBlock(); HInstruction* previous = got->GetPrevious(); HLoopInformation* info = block->GetLoopInformation(); if (info != nullptr && info->IsBackEdge(block) && info->HasSuspendCheck()) { codegen_->ClearSpillSlotsFromLoopPhisInStackMap(info->GetSuspendCheck()); GenerateSuspendCheck(info->GetSuspendCheck(), successor); return; } if (block->IsEntryBlock() && (previous != nullptr) && previous->IsSuspendCheck()) { GenerateSuspendCheck(previous->AsSuspendCheck(), nullptr); } if (!codegen_->GoesToNextBlock(got->GetBlock(), successor)) { __ b(codegen_->GetLabelOf(successor)); } } void LocationsBuilderARM::VisitExit(HExit* exit) { exit->SetLocations(nullptr); } void InstructionCodeGeneratorARM::VisitExit(HExit* exit) { if (kIsDebugBuild) { __ Comment("Unreachable"); __ bkpt(0); } } void LocationsBuilderARM::VisitIf(HIf* if_instr) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(if_instr, LocationSummary::kNoCall); HInstruction* cond = if_instr->InputAt(0); if (!cond->IsCondition() || cond->AsCondition()->NeedsMaterialization()) { locations->SetInAt(0, Location::RequiresRegister()); } } void InstructionCodeGeneratorARM::VisitIf(HIf* if_instr) { HInstruction* cond = if_instr->InputAt(0); if (cond->IsIntConstant()) { // Constant condition, statically compared against 1. int32_t cond_value = cond->AsIntConstant()->GetValue(); if (cond_value == 1) { if (!codegen_->GoesToNextBlock(if_instr->GetBlock(), if_instr->IfTrueSuccessor())) { __ b(codegen_->GetLabelOf(if_instr->IfTrueSuccessor())); } return; } else { DCHECK_EQ(cond_value, 0); } } else { if (!cond->IsCondition() || cond->AsCondition()->NeedsMaterialization()) { // Condition has been materialized, compare the output to 0 DCHECK(if_instr->GetLocations()->InAt(0).IsRegister()); __ cmp(if_instr->GetLocations()->InAt(0).As(), ShifterOperand(0)); __ b(codegen_->GetLabelOf(if_instr->IfTrueSuccessor()), NE); } else { // Condition has not been materialized, use its inputs as the // comparison and its condition as the branch condition. LocationSummary* locations = cond->GetLocations(); if (locations->InAt(1).IsRegister()) { __ cmp(locations->InAt(0).As(), ShifterOperand(locations->InAt(1).As())); } else { DCHECK(locations->InAt(1).IsConstant()); int32_t value = locations->InAt(1).GetConstant()->AsIntConstant()->GetValue(); ShifterOperand operand; if (ShifterOperand::CanHoldArm(value, &operand)) { __ cmp(locations->InAt(0).As(), ShifterOperand(value)); } else { Register temp = IP; __ LoadImmediate(temp, value); __ cmp(locations->InAt(0).As(), ShifterOperand(temp)); } } __ b(codegen_->GetLabelOf(if_instr->IfTrueSuccessor()), ARMCondition(cond->AsCondition()->GetCondition())); } } if (!codegen_->GoesToNextBlock(if_instr->GetBlock(), if_instr->IfFalseSuccessor())) { __ b(codegen_->GetLabelOf(if_instr->IfFalseSuccessor())); } } void LocationsBuilderARM::VisitCondition(HCondition* comp) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(comp, LocationSummary::kNoCall); locations->SetInAt(0, Location::RequiresRegister()); locations->SetInAt(1, Location::RegisterOrConstant(comp->InputAt(1))); if (comp->NeedsMaterialization()) { locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); } } void InstructionCodeGeneratorARM::VisitCondition(HCondition* comp) { if (!comp->NeedsMaterialization()) return; LocationSummary* locations = comp->GetLocations(); if (locations->InAt(1).IsRegister()) { __ cmp(locations->InAt(0).As(), ShifterOperand(locations->InAt(1).As())); } else { DCHECK(locations->InAt(1).IsConstant()); int32_t value = locations->InAt(1).GetConstant()->AsIntConstant()->GetValue(); ShifterOperand operand; if (ShifterOperand::CanHoldArm(value, &operand)) { __ cmp(locations->InAt(0).As(), ShifterOperand(value)); } else { Register temp = IP; __ LoadImmediate(temp, value); __ cmp(locations->InAt(0).As(), ShifterOperand(temp)); } } __ it(ARMCondition(comp->GetCondition()), kItElse); __ mov(locations->Out().As(), ShifterOperand(1), ARMCondition(comp->GetCondition())); __ mov(locations->Out().As(), ShifterOperand(0), ARMOppositeCondition(comp->GetCondition())); } void LocationsBuilderARM::VisitEqual(HEqual* comp) { VisitCondition(comp); } void InstructionCodeGeneratorARM::VisitEqual(HEqual* comp) { VisitCondition(comp); } void LocationsBuilderARM::VisitNotEqual(HNotEqual* comp) { VisitCondition(comp); } void InstructionCodeGeneratorARM::VisitNotEqual(HNotEqual* comp) { VisitCondition(comp); } void LocationsBuilderARM::VisitLessThan(HLessThan* comp) { VisitCondition(comp); } void InstructionCodeGeneratorARM::VisitLessThan(HLessThan* comp) { VisitCondition(comp); } void LocationsBuilderARM::VisitLessThanOrEqual(HLessThanOrEqual* comp) { VisitCondition(comp); } void InstructionCodeGeneratorARM::VisitLessThanOrEqual(HLessThanOrEqual* comp) { VisitCondition(comp); } void LocationsBuilderARM::VisitGreaterThan(HGreaterThan* comp) { VisitCondition(comp); } void InstructionCodeGeneratorARM::VisitGreaterThan(HGreaterThan* comp) { VisitCondition(comp); } void LocationsBuilderARM::VisitGreaterThanOrEqual(HGreaterThanOrEqual* comp) { VisitCondition(comp); } void InstructionCodeGeneratorARM::VisitGreaterThanOrEqual(HGreaterThanOrEqual* comp) { VisitCondition(comp); } void LocationsBuilderARM::VisitLocal(HLocal* local) { local->SetLocations(nullptr); } void InstructionCodeGeneratorARM::VisitLocal(HLocal* local) { DCHECK_EQ(local->GetBlock(), GetGraph()->GetEntryBlock()); } void LocationsBuilderARM::VisitLoadLocal(HLoadLocal* load) { load->SetLocations(nullptr); } void InstructionCodeGeneratorARM::VisitLoadLocal(HLoadLocal* load) { // Nothing to do, this is driven by the code generator. } void LocationsBuilderARM::VisitStoreLocal(HStoreLocal* store) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(store, LocationSummary::kNoCall); switch (store->InputAt(1)->GetType()) { case Primitive::kPrimBoolean: case Primitive::kPrimByte: case Primitive::kPrimChar: case Primitive::kPrimShort: case Primitive::kPrimInt: case Primitive::kPrimNot: case Primitive::kPrimFloat: locations->SetInAt(1, Location::StackSlot(codegen_->GetStackSlot(store->GetLocal()))); break; case Primitive::kPrimLong: case Primitive::kPrimDouble: locations->SetInAt(1, Location::DoubleStackSlot(codegen_->GetStackSlot(store->GetLocal()))); break; default: LOG(FATAL) << "Unexpected local type " << store->InputAt(1)->GetType(); } } void InstructionCodeGeneratorARM::VisitStoreLocal(HStoreLocal* store) { } void LocationsBuilderARM::VisitIntConstant(HIntConstant* constant) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall); locations->SetOut(Location::ConstantLocation(constant)); } void InstructionCodeGeneratorARM::VisitIntConstant(HIntConstant* constant) { // Will be generated at use site. } void LocationsBuilderARM::VisitLongConstant(HLongConstant* constant) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall); locations->SetOut(Location::ConstantLocation(constant)); } void InstructionCodeGeneratorARM::VisitLongConstant(HLongConstant* constant) { // Will be generated at use site. } void LocationsBuilderARM::VisitFloatConstant(HFloatConstant* constant) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall); locations->SetOut(Location::ConstantLocation(constant)); } void InstructionCodeGeneratorARM::VisitFloatConstant(HFloatConstant* constant) { // Will be generated at use site. } void LocationsBuilderARM::VisitDoubleConstant(HDoubleConstant* constant) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall); locations->SetOut(Location::ConstantLocation(constant)); } void InstructionCodeGeneratorARM::VisitDoubleConstant(HDoubleConstant* constant) { // Will be generated at use site. } void LocationsBuilderARM::VisitReturnVoid(HReturnVoid* ret) { ret->SetLocations(nullptr); } void InstructionCodeGeneratorARM::VisitReturnVoid(HReturnVoid* ret) { codegen_->GenerateFrameExit(); } void LocationsBuilderARM::VisitReturn(HReturn* ret) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(ret, LocationSummary::kNoCall); locations->SetInAt(0, parameter_visitor_.GetReturnLocation(ret->InputAt(0)->GetType())); } void InstructionCodeGeneratorARM::VisitReturn(HReturn* ret) { codegen_->GenerateFrameExit(); } void LocationsBuilderARM::VisitInvokeStatic(HInvokeStatic* invoke) { HandleInvoke(invoke); } void CodeGeneratorARM::LoadCurrentMethod(Register reg) { __ LoadFromOffset(kLoadWord, reg, SP, kCurrentMethodStackOffset); } void InstructionCodeGeneratorARM::VisitInvokeStatic(HInvokeStatic* invoke) { Register temp = invoke->GetLocations()->GetTemp(0).As(); // TODO: Implement all kinds of calls: // 1) boot -> boot // 2) app -> boot // 3) app -> app // // Currently we implement the app -> app logic, which looks up in the resolve cache. // temp = method; codegen_->LoadCurrentMethod(temp); // temp = temp->dex_cache_resolved_methods_; __ LoadFromOffset( kLoadWord, temp, temp, mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value()); // 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()); // LR() __ blx(LR); codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); DCHECK(!codegen_->IsLeafMethod()); } void LocationsBuilderARM::VisitInvokeVirtual(HInvokeVirtual* invoke) { HandleInvoke(invoke); } void LocationsBuilderARM::HandleInvoke(HInvoke* invoke) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(invoke, LocationSummary::kCall); locations->AddTemp(Location::RegisterLocation(R0)); InvokeDexCallingConventionVisitor calling_convention_visitor; for (size_t i = 0; i < invoke->InputCount(); i++) { HInstruction* input = invoke->InputAt(i); locations->SetInAt(i, calling_convention_visitor.GetNextLocation(input->GetType())); } locations->SetOut(calling_convention_visitor.GetReturnLocation(invoke->GetType())); } void InstructionCodeGeneratorARM::VisitInvokeVirtual(HInvokeVirtual* invoke) { Register temp = invoke->GetLocations()->GetTemp(0).As(); uint32_t method_offset = mirror::Class::EmbeddedVTableOffset().Uint32Value() + invoke->GetVTableIndex() * sizeof(mirror::Class::VTableEntry); LocationSummary* locations = invoke->GetLocations(); Location receiver = locations->InAt(0); uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); // temp = object->GetClass(); if (receiver.IsStackSlot()) { __ LoadFromOffset(kLoadWord, temp, SP, receiver.GetStackIndex()); __ LoadFromOffset(kLoadWord, temp, temp, class_offset); } else { __ LoadFromOffset(kLoadWord, temp, receiver.As(), class_offset); } // temp = temp->GetMethodAt(method_offset); uint32_t entry_point = mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value(); __ LoadFromOffset(kLoadWord, temp, temp, method_offset); // LR = temp->GetEntryPoint(); __ LoadFromOffset(kLoadWord, LR, temp, entry_point); // LR(); __ blx(LR); DCHECK(!codegen_->IsLeafMethod()); codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); } void LocationsBuilderARM::VisitNeg(HNeg* neg) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(neg, LocationSummary::kNoCall); switch (neg->GetResultType()) { case Primitive::kPrimInt: case Primitive::kPrimLong: { bool output_overlaps = (neg->GetResultType() == Primitive::kPrimLong); locations->SetInAt(0, Location::RequiresRegister()); locations->SetOut(Location::RequiresRegister(), output_overlaps); break; } case Primitive::kPrimFloat: case Primitive::kPrimDouble: LOG(FATAL) << "Not yet implemented neg type " << neg->GetResultType(); break; default: LOG(FATAL) << "Unexpected neg type " << neg->GetResultType(); } } void InstructionCodeGeneratorARM::VisitNeg(HNeg* neg) { LocationSummary* locations = neg->GetLocations(); Location out = locations->Out(); Location in = locations->InAt(0); switch (neg->GetResultType()) { case Primitive::kPrimInt: DCHECK(in.IsRegister()); __ rsb(out.As(), in.As(), ShifterOperand(0)); break; case Primitive::kPrimLong: DCHECK(in.IsRegisterPair()); // out.lo = 0 - in.lo (and update the carry/borrow (C) flag) __ rsbs(out.AsRegisterPairLow(), in.AsRegisterPairLow(), ShifterOperand(0)); // We cannot emit an RSC (Reverse Subtract with Carry) // instruction here, as it does not exist in the Thumb-2 // instruction set. We use the following approach // using SBC and SUB instead. // // out.hi = -C __ sbc(out.AsRegisterPairHigh(), out.AsRegisterPairHigh(), ShifterOperand(out.AsRegisterPairHigh())); // out.hi = out.hi - in.hi __ sub(out.AsRegisterPairHigh(), out.AsRegisterPairHigh(), ShifterOperand(in.AsRegisterPairHigh())); break; case Primitive::kPrimFloat: case Primitive::kPrimDouble: LOG(FATAL) << "Not yet implemented neg type " << neg->GetResultType(); break; default: LOG(FATAL) << "Unexpected neg type " << neg->GetResultType(); } } void LocationsBuilderARM::VisitAdd(HAdd* add) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(add, LocationSummary::kNoCall); switch (add->GetResultType()) { case Primitive::kPrimInt: case Primitive::kPrimLong: { bool output_overlaps = (add->GetResultType() == Primitive::kPrimLong); locations->SetInAt(0, Location::RequiresRegister()); locations->SetInAt(1, Location::RegisterOrConstant(add->InputAt(1))); locations->SetOut(Location::RequiresRegister(), output_overlaps); break; } case Primitive::kPrimFloat: case Primitive::kPrimDouble: { locations->SetInAt(0, Location::RequiresFpuRegister()); locations->SetInAt(1, Location::RequiresFpuRegister()); locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); break; } default: LOG(FATAL) << "Unexpected add type " << add->GetResultType(); } } void InstructionCodeGeneratorARM::VisitAdd(HAdd* add) { LocationSummary* locations = add->GetLocations(); Location out = locations->Out(); Location first = locations->InAt(0); Location second = locations->InAt(1); switch (add->GetResultType()) { case Primitive::kPrimInt: if (second.IsRegister()) { __ add(out.As(), first.As(), ShifterOperand(second.As())); } else { __ AddConstant(out.As(), first.As(), second.GetConstant()->AsIntConstant()->GetValue()); } break; case Primitive::kPrimLong: __ adds(out.AsRegisterPairLow(), first.AsRegisterPairLow(), ShifterOperand(second.AsRegisterPairLow())); __ adc(out.AsRegisterPairHigh(), first.AsRegisterPairHigh(), ShifterOperand(second.AsRegisterPairHigh())); break; case Primitive::kPrimFloat: __ vadds(out.As(), first.As(), second.As()); break; case Primitive::kPrimDouble: __ vaddd(FromLowSToD(out.AsFpuRegisterPairLow()), FromLowSToD(first.AsFpuRegisterPairLow()), FromLowSToD(second.AsFpuRegisterPairLow())); break; default: LOG(FATAL) << "Unexpected add type " << add->GetResultType(); } } void LocationsBuilderARM::VisitSub(HSub* sub) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(sub, LocationSummary::kNoCall); switch (sub->GetResultType()) { case Primitive::kPrimInt: case Primitive::kPrimLong: { bool output_overlaps = (sub->GetResultType() == Primitive::kPrimLong); locations->SetInAt(0, Location::RequiresRegister()); locations->SetInAt(1, Location::RegisterOrConstant(sub->InputAt(1))); locations->SetOut(Location::RequiresRegister(), output_overlaps); break; } case Primitive::kPrimFloat: case Primitive::kPrimDouble: { locations->SetInAt(0, Location::RequiresFpuRegister()); locations->SetInAt(1, Location::RequiresFpuRegister()); locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); break; } default: LOG(FATAL) << "Unexpected sub type " << sub->GetResultType(); } } void InstructionCodeGeneratorARM::VisitSub(HSub* sub) { LocationSummary* locations = sub->GetLocations(); Location out = locations->Out(); Location first = locations->InAt(0); Location second = locations->InAt(1); switch (sub->GetResultType()) { case Primitive::kPrimInt: { if (second.IsRegister()) { __ sub(out.As(), first.As(), ShifterOperand(second.As())); } else { __ AddConstant(out.As(), first.As(), -second.GetConstant()->AsIntConstant()->GetValue()); } break; } case Primitive::kPrimLong: { __ subs(out.AsRegisterPairLow(), first.AsRegisterPairLow(), ShifterOperand(second.AsRegisterPairLow())); __ sbc(out.AsRegisterPairHigh(), first.AsRegisterPairHigh(), ShifterOperand(second.AsRegisterPairHigh())); break; } case Primitive::kPrimFloat: { __ vsubs(out.As(), first.As(), second.As()); break; } case Primitive::kPrimDouble: { __ vsubd(FromLowSToD(out.AsFpuRegisterPairLow()), FromLowSToD(first.AsFpuRegisterPairLow()), FromLowSToD(second.AsFpuRegisterPairLow())); break; } default: LOG(FATAL) << "Unexpected sub type " << sub->GetResultType(); } } void LocationsBuilderARM::VisitMul(HMul* mul) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(mul, LocationSummary::kNoCall); switch (mul->GetResultType()) { case Primitive::kPrimInt: case Primitive::kPrimLong: { locations->SetInAt(0, Location::RequiresRegister()); locations->SetInAt(1, Location::RequiresRegister()); locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); break; } case Primitive::kPrimFloat: case Primitive::kPrimDouble: { locations->SetInAt(0, Location::RequiresFpuRegister()); locations->SetInAt(1, Location::RequiresFpuRegister()); locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); break; } default: LOG(FATAL) << "Unexpected mul type " << mul->GetResultType(); } } void InstructionCodeGeneratorARM::VisitMul(HMul* mul) { LocationSummary* locations = mul->GetLocations(); Location out = locations->Out(); Location first = locations->InAt(0); Location second = locations->InAt(1); switch (mul->GetResultType()) { case Primitive::kPrimInt: { __ mul(out.As(), first.As(), second.As()); break; } case Primitive::kPrimLong: { Register out_hi = out.AsRegisterPairHigh(); Register out_lo = out.AsRegisterPairLow(); Register in1_hi = first.AsRegisterPairHigh(); Register in1_lo = first.AsRegisterPairLow(); Register in2_hi = second.AsRegisterPairHigh(); Register in2_lo = second.AsRegisterPairLow(); // Extra checks to protect caused by the existence of R1_R2. // The algorithm is wrong if out.hi is either in1.lo or in2.lo: // (e.g. in1=r0_r1, in2=r2_r3 and out=r1_r2); DCHECK_NE(out_hi, in1_lo); DCHECK_NE(out_hi, in2_lo); // input: in1 - 64 bits, in2 - 64 bits // output: out // formula: out.hi : out.lo = (in1.lo * in2.hi + in1.hi * in2.lo)* 2^32 + in1.lo * in2.lo // parts: out.hi = in1.lo * in2.hi + in1.hi * in2.lo + (in1.lo * in2.lo)[63:32] // parts: out.lo = (in1.lo * in2.lo)[31:0] // IP <- in1.lo * in2.hi __ mul(IP, in1_lo, in2_hi); // out.hi <- in1.lo * in2.hi + in1.hi * in2.lo __ mla(out_hi, in1_hi, in2_lo, IP); // out.lo <- (in1.lo * in2.lo)[31:0]; __ umull(out_lo, IP, in1_lo, in2_lo); // out.hi <- in2.hi * in1.lo + in2.lo * in1.hi + (in1.lo * in2.lo)[63:32] __ add(out_hi, out_hi, ShifterOperand(IP)); break; } case Primitive::kPrimFloat: { __ vmuls(out.As(), first.As(), second.As()); break; } case Primitive::kPrimDouble: { __ vmuld(FromLowSToD(out.AsFpuRegisterPairLow()), FromLowSToD(first.AsFpuRegisterPairLow()), FromLowSToD(second.AsFpuRegisterPairLow())); break; } default: LOG(FATAL) << "Unexpected mul type " << mul->GetResultType(); } } void LocationsBuilderARM::VisitDiv(HDiv* div) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(div, LocationSummary::kNoCall); switch (div->GetResultType()) { case Primitive::kPrimInt: case Primitive::kPrimLong: { LOG(FATAL) << "Not implemented div type" << div->GetResultType(); break; } case Primitive::kPrimFloat: case Primitive::kPrimDouble: { locations->SetInAt(0, Location::RequiresFpuRegister()); locations->SetInAt(1, Location::RequiresFpuRegister()); locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); break; } default: LOG(FATAL) << "Unexpected div type " << div->GetResultType(); } } void InstructionCodeGeneratorARM::VisitDiv(HDiv* div) { LocationSummary* locations = div->GetLocations(); Location out = locations->Out(); Location first = locations->InAt(0); Location second = locations->InAt(1); switch (div->GetResultType()) { case Primitive::kPrimInt: case Primitive::kPrimLong: { LOG(FATAL) << "Not implemented div type" << div->GetResultType(); break; } case Primitive::kPrimFloat: { __ vdivs(out.As(), first.As(), second.As()); break; } case Primitive::kPrimDouble: { __ vdivd(FromLowSToD(out.AsFpuRegisterPairLow()), FromLowSToD(first.AsFpuRegisterPairLow()), FromLowSToD(second.AsFpuRegisterPairLow())); break; } default: LOG(FATAL) << "Unexpected div type " << div->GetResultType(); } } void LocationsBuilderARM::VisitNewInstance(HNewInstance* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall); InvokeRuntimeCallingConvention calling_convention; locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0))); locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(1))); locations->SetOut(Location::RegisterLocation(R0)); } void InstructionCodeGeneratorARM::VisitNewInstance(HNewInstance* instruction) { InvokeRuntimeCallingConvention calling_convention; codegen_->LoadCurrentMethod(calling_convention.GetRegisterAt(1)); __ LoadImmediate(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex()); codegen_->InvokeRuntime( QUICK_ENTRY_POINT(pAllocObjectWithAccessCheck), instruction, instruction->GetDexPc()); } void LocationsBuilderARM::VisitNewArray(HNewArray* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall); InvokeRuntimeCallingConvention calling_convention; locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0))); locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(1))); locations->SetOut(Location::RegisterLocation(R0)); locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(2))); } void InstructionCodeGeneratorARM::VisitNewArray(HNewArray* instruction) { InvokeRuntimeCallingConvention calling_convention; codegen_->LoadCurrentMethod(calling_convention.GetRegisterAt(1)); __ LoadImmediate(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex()); codegen_->InvokeRuntime( QUICK_ENTRY_POINT(pAllocArrayWithAccessCheck), instruction, instruction->GetDexPc()); } void LocationsBuilderARM::VisitParameterValue(HParameterValue* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); Location location = parameter_visitor_.GetNextLocation(instruction->GetType()); if (location.IsStackSlot()) { location = Location::StackSlot(location.GetStackIndex() + codegen_->GetFrameSize()); } else if (location.IsDoubleStackSlot()) { location = Location::DoubleStackSlot(location.GetStackIndex() + codegen_->GetFrameSize()); } locations->SetOut(location); } void InstructionCodeGeneratorARM::VisitParameterValue(HParameterValue* instruction) { // Nothing to do, the parameter is already at its location. } void LocationsBuilderARM::VisitNot(HNot* not_) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(not_, LocationSummary::kNoCall); locations->SetInAt(0, Location::RequiresRegister()); locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); } void InstructionCodeGeneratorARM::VisitNot(HNot* not_) { LocationSummary* locations = not_->GetLocations(); Location out = locations->Out(); Location in = locations->InAt(0); switch (not_->InputAt(0)->GetType()) { case Primitive::kPrimBoolean: __ eor(out.As(), in.As(), ShifterOperand(1)); break; case Primitive::kPrimInt: __ mvn(out.As(), ShifterOperand(in.As())); break; case Primitive::kPrimLong: __ mvn(out.AsRegisterPairLow(), ShifterOperand(in.AsRegisterPairLow())); __ mvn(out.AsRegisterPairHigh(), ShifterOperand(in.AsRegisterPairHigh())); break; default: LOG(FATAL) << "Unimplemented type for not operation " << not_->GetResultType(); } } void LocationsBuilderARM::VisitCompare(HCompare* compare) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(compare, LocationSummary::kNoCall); locations->SetInAt(0, Location::RequiresRegister()); locations->SetInAt(1, Location::RequiresRegister()); locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); } void InstructionCodeGeneratorARM::VisitCompare(HCompare* compare) { Label greater, done; LocationSummary* locations = compare->GetLocations(); switch (compare->InputAt(0)->GetType()) { case Primitive::kPrimLong: { Register output = locations->Out().As(); Location left = locations->InAt(0); Location right = locations->InAt(1); Label less, greater, done; __ cmp(left.AsRegisterPairHigh(), ShifterOperand(right.AsRegisterPairHigh())); // Signed compare. __ b(&less, LT); __ b(&greater, GT); // Do LoadImmediate before any `cmp`, as LoadImmediate might affect // the status flags. __ LoadImmediate(output, 0); __ cmp(left.AsRegisterPairLow(), ShifterOperand(right.AsRegisterPairLow())); // Unsigned compare. __ b(&done, EQ); __ b(&less, CC); __ Bind(&greater); __ LoadImmediate(output, 1); __ b(&done); __ Bind(&less); __ LoadImmediate(output, -1); __ Bind(&done); break; } default: LOG(FATAL) << "Unimplemented compare type " << compare->InputAt(0)->GetType(); } } void LocationsBuilderARM::VisitPhi(HPhi* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); for (size_t i = 0, e = instruction->InputCount(); i < e; ++i) { locations->SetInAt(i, Location::Any()); } locations->SetOut(Location::Any()); } void InstructionCodeGeneratorARM::VisitPhi(HPhi* instruction) { LOG(FATAL) << "Unreachable"; } void LocationsBuilderARM::VisitInstanceFieldSet(HInstanceFieldSet* 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::VisitInstanceFieldSet(HInstanceFieldSet* instruction) { LocationSummary* locations = instruction->GetLocations(); Register obj = locations->InAt(0).As(); 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(); __ StoreToOffset(kStoreByte, value, obj, offset); break; } case Primitive::kPrimShort: case Primitive::kPrimChar: { Register value = locations->InAt(1).As(); __ StoreToOffset(kStoreHalfword, value, obj, offset); break; } case Primitive::kPrimInt: case Primitive::kPrimNot: { Register value = locations->InAt(1).As(); __ StoreToOffset(kStoreWord, value, obj, offset); if (field_type == Primitive::kPrimNot) { Register temp = locations->GetTemp(0).As(); Register card = locations->GetTemp(1).As(); codegen_->MarkGCCard(temp, card, obj, value); } break; } case Primitive::kPrimLong: { Location value = locations->InAt(1); __ StoreToOffset(kStoreWordPair, value.AsRegisterPairLow(), obj, 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(); } } void LocationsBuilderARM::VisitInstanceFieldGet(HInstanceFieldGet* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); locations->SetInAt(0, Location::RequiresRegister()); locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); } void InstructionCodeGeneratorARM::VisitInstanceFieldGet(HInstanceFieldGet* instruction) { LocationSummary* locations = instruction->GetLocations(); Register obj = locations->InAt(0).As(); uint32_t offset = instruction->GetFieldOffset().Uint32Value(); switch (instruction->GetType()) { case Primitive::kPrimBoolean: { Register out = locations->Out().As(); __ LoadFromOffset(kLoadUnsignedByte, out, obj, offset); break; } case Primitive::kPrimByte: { Register out = locations->Out().As(); __ LoadFromOffset(kLoadSignedByte, out, obj, offset); break; } case Primitive::kPrimShort: { Register out = locations->Out().As(); __ LoadFromOffset(kLoadSignedHalfword, out, obj, offset); break; } case Primitive::kPrimChar: { Register out = locations->Out().As(); __ LoadFromOffset(kLoadUnsignedHalfword, out, obj, offset); break; } case Primitive::kPrimInt: case Primitive::kPrimNot: { Register out = locations->Out().As(); __ LoadFromOffset(kLoadWord, out, obj, offset); break; } case Primitive::kPrimLong: { // TODO: support volatile. Location out = locations->Out(); __ LoadFromOffset(kLoadWordPair, out.AsRegisterPairLow(), obj, 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::VisitNullCheck(HNullCheck* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); locations->SetInAt(0, Location::RequiresRegister()); if (instruction->HasUses()) { locations->SetOut(Location::SameAsFirstInput()); } } void InstructionCodeGeneratorARM::VisitNullCheck(HNullCheck* instruction) { SlowPathCodeARM* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathARM(instruction); codegen_->AddSlowPath(slow_path); LocationSummary* locations = instruction->GetLocations(); Location obj = locations->InAt(0); if (obj.IsRegister()) { __ cmp(obj.As(), ShifterOperand(0)); __ b(slow_path->GetEntryLabel(), EQ); } else { DCHECK(obj.IsConstant()) << obj; DCHECK_EQ(obj.GetConstant()->AsIntConstant()->GetValue(), 0); __ b(slow_path->GetEntryLabel()); } } void LocationsBuilderARM::VisitArrayGet(HArrayGet* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); locations->SetInAt(0, Location::RequiresRegister()); locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1))); locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); } void InstructionCodeGeneratorARM::VisitArrayGet(HArrayGet* instruction) { LocationSummary* locations = instruction->GetLocations(); Register obj = locations->InAt(0).As(); 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().As(); 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.As())); __ 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().As(); 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.As())); __ 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().As(); 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.As(), 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().As(); 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.As(), LSL, TIMES_2)); __ LoadFromOffset(kLoadUnsignedHalfword, out, IP, data_offset); } break; } case Primitive::kPrimInt: case Primitive::kPrimNot: { DCHECK_EQ(sizeof(mirror::HeapReference), sizeof(int32_t)); uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value(); Register out = locations->Out().As(); 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.As(), LSL, TIMES_4)); __ LoadFromOffset(kLoadWord, out, IP, data_offset); } break; } case Primitive::kPrimLong: { uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value(); Location out = locations->Out(); 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.As(), LSL, TIMES_8)); __ LoadFromOffset(kLoadWordPair, out.AsRegisterPairLow(), IP, data_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::VisitArraySet(HArraySet* instruction) { Primitive::Type value_type = instruction->GetComponentType(); bool is_object = value_type == Primitive::kPrimNot; LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary( instruction, is_object ? LocationSummary::kCall : LocationSummary::kNoCall); if (is_object) { InvokeRuntimeCallingConvention calling_convention; locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2))); } else { locations->SetInAt(0, Location::RequiresRegister()); locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1))); locations->SetInAt(2, Location::RequiresRegister()); } } void InstructionCodeGeneratorARM::VisitArraySet(HArraySet* instruction) { LocationSummary* locations = instruction->GetLocations(); Register obj = locations->InAt(0).As(); Location index = locations->InAt(1); Primitive::Type value_type = instruction->GetComponentType(); 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).As(); 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.As())); __ 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).As(); 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.As(), 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).As(); 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.As(), LSL, TIMES_4)); __ StoreToOffset(kStoreWord, value, IP, data_offset); } break; } case Primitive::kPrimNot: { codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pAputObject), instruction, instruction->GetDexPc()); break; } case Primitive::kPrimLong: { uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value(); Location value = locations->InAt(2); 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.As(), LSL, TIMES_8)); __ StoreToOffset(kStoreWordPair, value.AsRegisterPairLow(), IP, data_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::VisitArrayLength(HArrayLength* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); locations->SetInAt(0, Location::RequiresRegister()); locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); } void InstructionCodeGeneratorARM::VisitArrayLength(HArrayLength* instruction) { LocationSummary* locations = instruction->GetLocations(); uint32_t offset = mirror::Array::LengthOffset().Uint32Value(); Register obj = locations->InAt(0).As(); Register out = locations->Out().As(); __ LoadFromOffset(kLoadWord, out, obj, offset); } void LocationsBuilderARM::VisitBoundsCheck(HBoundsCheck* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); locations->SetInAt(0, Location::RequiresRegister()); locations->SetInAt(1, Location::RequiresRegister()); if (instruction->HasUses()) { locations->SetOut(Location::SameAsFirstInput()); } } void InstructionCodeGeneratorARM::VisitBoundsCheck(HBoundsCheck* instruction) { LocationSummary* locations = instruction->GetLocations(); SlowPathCodeARM* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathARM( instruction, locations->InAt(0), locations->InAt(1)); codegen_->AddSlowPath(slow_path); Register index = locations->InAt(0).As(); Register length = locations->InAt(1).As(); __ 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().Int32Value()); __ Lsr(temp, object, gc::accounting::CardTable::kCardShift); __ strb(card, Address(card, temp)); __ Bind(&is_null); } void LocationsBuilderARM::VisitTemporary(HTemporary* temp) { temp->SetLocations(nullptr); } void InstructionCodeGeneratorARM::VisitTemporary(HTemporary* temp) { // Nothing to do, this is driven by the code generator. } void LocationsBuilderARM::VisitParallelMove(HParallelMove* instruction) { LOG(FATAL) << "Unreachable"; } void InstructionCodeGeneratorARM::VisitParallelMove(HParallelMove* instruction) { codegen_->GetMoveResolver()->EmitNativeCode(instruction); } void LocationsBuilderARM::VisitSuspendCheck(HSuspendCheck* instruction) { new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnSlowPath); } void InstructionCodeGeneratorARM::VisitSuspendCheck(HSuspendCheck* instruction) { HBasicBlock* block = instruction->GetBlock(); if (block->GetLoopInformation() != nullptr) { DCHECK(block->GetLoopInformation()->GetSuspendCheck() == instruction); // The back edge will generate the suspend check. return; } if (block->IsEntryBlock() && instruction->GetNext()->IsGoto()) { // The goto will generate the suspend check. return; } GenerateSuspendCheck(instruction, nullptr); } void InstructionCodeGeneratorARM::GenerateSuspendCheck(HSuspendCheck* instruction, HBasicBlock* successor) { SuspendCheckSlowPathARM* slow_path = new (GetGraph()->GetArena()) SuspendCheckSlowPathARM(instruction, successor); codegen_->AddSlowPath(slow_path); __ subs(R4, R4, ShifterOperand(1)); if (successor == nullptr) { __ b(slow_path->GetEntryLabel(), EQ); __ Bind(slow_path->GetReturnLabel()); } else { __ b(codegen_->GetLabelOf(successor), NE); __ b(slow_path->GetEntryLabel()); } } ArmAssembler* ParallelMoveResolverARM::GetAssembler() const { return codegen_->GetAssembler(); } void ParallelMoveResolverARM::EmitMove(size_t index) { MoveOperands* move = moves_.Get(index); Location source = move->GetSource(); Location destination = move->GetDestination(); if (source.IsRegister()) { if (destination.IsRegister()) { __ Mov(destination.As(), source.As()); } else { DCHECK(destination.IsStackSlot()); __ StoreToOffset(kStoreWord, source.As(), SP, destination.GetStackIndex()); } } else if (source.IsStackSlot()) { if (destination.IsRegister()) { __ LoadFromOffset(kLoadWord, destination.As(), SP, source.GetStackIndex()); } else { DCHECK(destination.IsStackSlot()); __ LoadFromOffset(kLoadWord, IP, SP, source.GetStackIndex()); __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex()); } } else { DCHECK(source.IsConstant()); DCHECK(source.GetConstant()->IsIntConstant()); int32_t value = source.GetConstant()->AsIntConstant()->GetValue(); if (destination.IsRegister()) { __ LoadImmediate(destination.As(), value); } else { DCHECK(destination.IsStackSlot()); __ LoadImmediate(IP, value); __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex()); } } } void ParallelMoveResolverARM::Exchange(Register reg, int mem) { __ Mov(IP, reg); __ LoadFromOffset(kLoadWord, reg, SP, mem); __ StoreToOffset(kStoreWord, IP, SP, mem); } void ParallelMoveResolverARM::Exchange(int mem1, int mem2) { ScratchRegisterScope ensure_scratch(this, IP, R0, codegen_->GetNumberOfCoreRegisters()); int stack_offset = ensure_scratch.IsSpilled() ? kArmWordSize : 0; __ LoadFromOffset(kLoadWord, static_cast(ensure_scratch.GetRegister()), SP, mem1 + stack_offset); __ LoadFromOffset(kLoadWord, IP, SP, mem2 + stack_offset); __ StoreToOffset(kStoreWord, static_cast(ensure_scratch.GetRegister()), SP, mem2 + stack_offset); __ StoreToOffset(kStoreWord, IP, SP, mem1 + stack_offset); } void ParallelMoveResolverARM::EmitSwap(size_t index) { MoveOperands* move = moves_.Get(index); Location source = move->GetSource(); Location destination = move->GetDestination(); if (source.IsRegister() && destination.IsRegister()) { DCHECK_NE(source.As(), IP); DCHECK_NE(destination.As(), IP); __ Mov(IP, source.As()); __ Mov(source.As(), destination.As()); __ Mov(destination.As(), IP); } else if (source.IsRegister() && destination.IsStackSlot()) { Exchange(source.As(), destination.GetStackIndex()); } else if (source.IsStackSlot() && destination.IsRegister()) { Exchange(destination.As(), source.GetStackIndex()); } else if (source.IsStackSlot() && destination.IsStackSlot()) { Exchange(source.GetStackIndex(), destination.GetStackIndex()); } else { LOG(FATAL) << "Unimplemented"; } } void ParallelMoveResolverARM::SpillScratch(int reg) { __ Push(static_cast(reg)); } void ParallelMoveResolverARM::RestoreScratch(int reg) { __ Pop(static_cast(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(); 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(); __ 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(); uint32_t offset = instruction->GetFieldOffset().Uint32Value(); switch (instruction->GetType()) { case Primitive::kPrimBoolean: { Register out = locations->Out().As(); __ LoadFromOffset(kLoadUnsignedByte, out, cls, offset); break; } case Primitive::kPrimByte: { Register out = locations->Out().As(); __ LoadFromOffset(kLoadSignedByte, out, cls, offset); break; } case Primitive::kPrimShort: { Register out = locations->Out().As(); __ LoadFromOffset(kLoadSignedHalfword, out, cls, offset); break; } case Primitive::kPrimChar: { Register out = locations->Out().As(); __ LoadFromOffset(kLoadUnsignedHalfword, out, cls, offset); break; } case Primitive::kPrimInt: case Primitive::kPrimNot: { Register out = locations->Out().As(); __ LoadFromOffset(kLoadWord, out, cls, offset); break; } case Primitive::kPrimLong: { // TODO: support volatile. Location out = locations->Out(); __ LoadFromOffset(kLoadWordPair, out.AsRegisterPairLow(), 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(); 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(); __ StoreToOffset(kStoreByte, value, cls, offset); break; } case Primitive::kPrimShort: case Primitive::kPrimChar: { Register value = locations->InAt(1).As(); __ StoreToOffset(kStoreHalfword, value, cls, offset); break; } case Primitive::kPrimInt: case Primitive::kPrimNot: { Register value = locations->InAt(1).As(); __ StoreToOffset(kStoreWord, value, cls, offset); if (field_type == Primitive::kPrimNot) { Register temp = locations->GetTemp(0).As(); Register card = locations->GetTemp(1).As(); codegen_->MarkGCCard(temp, card, cls, value); } break; } case Primitive::kPrimLong: { Location value = locations->InAt(1); __ StoreToOffset(kStoreWordPair, value.AsRegisterPairLow(), 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(); } } void LocationsBuilderARM::VisitLoadString(HLoadString* load) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, LocationSummary::kCallOnSlowPath); locations->SetOut(Location::RequiresRegister()); } void InstructionCodeGeneratorARM::VisitLoadString(HLoadString* load) { SlowPathCodeARM* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathARM(load); codegen_->AddSlowPath(slow_path); Register out = load->GetLocations()->Out().As(); codegen_->LoadCurrentMethod(out); __ LoadFromOffset( kLoadWord, out, out, mirror::ArtMethod::DexCacheStringsOffset().Int32Value()); __ LoadFromOffset(kLoadWord, out, out, CodeGenerator::GetCacheOffset(load->GetStringIndex())); __ cmp(out, ShifterOperand(0)); __ b(slow_path->GetEntryLabel(), EQ); __ Bind(slow_path->GetExitLabel()); } } // namespace arm } // namespace art