diff options
Diffstat (limited to 'compiler/optimizing')
22 files changed, 1939 insertions, 215 deletions
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc index f5941291e7..43e6b830e9 100644 --- a/compiler/optimizing/builder.cc +++ b/compiler/optimizing/builder.cc @@ -34,6 +34,37 @@ namespace art { +/** + * Helper class to add HTemporary instructions. This class is used when + * converting a DEX instruction to multiple HInstruction, and where those + * instructions do not die at the following instruction, but instead spans + * multiple instructions. + */ +class Temporaries : public ValueObject { + public: + Temporaries(HGraph* graph, size_t count) : graph_(graph), count_(count), index_(0) { + graph_->UpdateNumberOfTemporaries(count_); + } + + void Add(HInstruction* instruction) { + // We currently only support vreg size temps. + DCHECK(instruction->GetType() != Primitive::kPrimLong + && instruction->GetType() != Primitive::kPrimDouble); + HInstruction* temp = new (graph_->GetArena()) HTemporary(index_++); + instruction->GetBlock()->AddInstruction(temp); + DCHECK(temp->GetPrevious() == instruction); + } + + private: + HGraph* const graph_; + + // The total number of temporaries that will be used. + const size_t count_; + + // Current index in the temporary stack, updated by `Add`. + size_t index_; +}; + static bool IsTypeSupported(Primitive::Type type) { return type != Primitive::kPrimFloat && type != Primitive::kPrimDouble; } @@ -308,9 +339,13 @@ bool HGraphBuilder::BuildInvoke(const Instruction& instruction, arena_, number_of_arguments, return_type, dex_offset, method_idx); size_t start_index = 0; + Temporaries temps(graph_, is_instance_call ? 1 : 0); if (is_instance_call) { HInstruction* arg = LoadLocal(is_range ? register_index : args[0], Primitive::kPrimNot); - invoke->SetArgumentAt(0, arg); + HNullCheck* null_check = new (arena_) HNullCheck(arg, dex_offset); + current_block_->AddInstruction(null_check); + temps.Add(null_check); + invoke->SetArgumentAt(0, null_check); start_index = 1; } @@ -343,37 +378,6 @@ bool HGraphBuilder::BuildInvoke(const Instruction& instruction, return true; } -/** - * Helper class to add HTemporary instructions. This class is used when - * converting a DEX instruction to multiple HInstruction, and where those - * instructions do not die at the following instruction, but instead spans - * multiple instructions. - */ -class Temporaries : public ValueObject { - public: - Temporaries(HGraph* graph, size_t count) : graph_(graph), count_(count), index_(0) { - graph_->UpdateNumberOfTemporaries(count_); - } - - void Add(HInstruction* instruction) { - // We currently only support vreg size temps. - DCHECK(instruction->GetType() != Primitive::kPrimLong - && instruction->GetType() != Primitive::kPrimDouble); - HInstruction* temp = new (graph_->GetArena()) HTemporary(index_++); - instruction->GetBlock()->AddInstruction(temp); - DCHECK(temp->GetPrevious() == instruction); - } - - private: - HGraph* const graph_; - - // The total number of temporaries that will be used. - const size_t count_; - - // Current index in the temporary stack, updated by `Add`. - size_t index_; -}; - bool HGraphBuilder::BuildFieldAccess(const Instruction& instruction, uint32_t dex_offset, bool is_put) { @@ -421,6 +425,41 @@ bool HGraphBuilder::BuildFieldAccess(const Instruction& instruction, return true; } +void HGraphBuilder::BuildArrayAccess(const Instruction& instruction, + uint32_t dex_offset, + bool is_put, + Primitive::Type anticipated_type) { + uint8_t source_or_dest_reg = instruction.VRegA_23x(); + uint8_t array_reg = instruction.VRegB_23x(); + uint8_t index_reg = instruction.VRegC_23x(); + + DCHECK(IsTypeSupported(anticipated_type)); + + // We need one temporary for the null check, one for the index, and one for the length. + Temporaries temps(graph_, 3); + + HInstruction* object = LoadLocal(array_reg, Primitive::kPrimNot); + object = new (arena_) HNullCheck(object, dex_offset); + current_block_->AddInstruction(object); + temps.Add(object); + + HInstruction* length = new (arena_) HArrayLength(object); + current_block_->AddInstruction(length); + temps.Add(length); + HInstruction* index = LoadLocal(index_reg, Primitive::kPrimInt); + index = new (arena_) HBoundsCheck(index, length, dex_offset); + current_block_->AddInstruction(index); + temps.Add(index); + if (is_put) { + HInstruction* value = LoadLocal(source_or_dest_reg, anticipated_type); + // TODO: Insert a type check node if the type is Object. + current_block_->AddInstruction(new (arena_) HArraySet(object, index, value, dex_offset)); + } else { + current_block_->AddInstruction(new (arena_) HArrayGet(object, index, anticipated_type)); + UpdateLocal(source_or_dest_reg, current_block_->GetLastInstruction()); + } +} + bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, int32_t dex_offset) { if (current_block_ == nullptr) { return true; // Dead code @@ -693,6 +732,24 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, int32_ break; } +#define ARRAY_XX(kind, anticipated_type) \ + case Instruction::AGET##kind: { \ + BuildArrayAccess(instruction, dex_offset, false, anticipated_type); \ + break; \ + } \ + case Instruction::APUT##kind: { \ + BuildArrayAccess(instruction, dex_offset, true, anticipated_type); \ + break; \ + } + + ARRAY_XX(, Primitive::kPrimInt); + ARRAY_XX(_WIDE, Primitive::kPrimLong); + ARRAY_XX(_OBJECT, Primitive::kPrimNot); + ARRAY_XX(_BOOLEAN, Primitive::kPrimBoolean); + ARRAY_XX(_BYTE, Primitive::kPrimByte); + ARRAY_XX(_CHAR, Primitive::kPrimChar); + ARRAY_XX(_SHORT, Primitive::kPrimShort); + default: return false; } diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h index f94b8e810a..170c42761a 100644 --- a/compiler/optimizing/builder.h +++ b/compiler/optimizing/builder.h @@ -93,6 +93,10 @@ class HGraphBuilder : public ValueObject { void BuildReturn(const Instruction& instruction, Primitive::Type type); bool BuildFieldAccess(const Instruction& instruction, uint32_t dex_offset, bool is_get); + void BuildArrayAccess(const Instruction& instruction, + uint32_t dex_offset, + bool is_get, + Primitive::Type anticipated_type); // Builds an invocation node and returns whether the instruction is supported. bool BuildInvoke(const Instruction& instruction, diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index e0db0f18be..bd8c27ec3e 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -30,13 +30,16 @@ namespace art { -void CodeGenerator::CompileBaseline(CodeAllocator* allocator) { +void CodeGenerator::CompileBaseline(CodeAllocator* allocator, bool is_leaf) { const GrowableArray<HBasicBlock*>& blocks = GetGraph()->GetBlocks(); DCHECK(blocks.Get(0) == GetGraph()->GetEntryBlock()); DCHECK(GoesToNextBlock(GetGraph()->GetEntryBlock(), blocks.Get(1))); block_labels_.SetSize(blocks.Size()); DCHECK_EQ(frame_size_, kUninitializedFrameSize); + if (!is_leaf) { + MarkNotLeaf(); + } ComputeFrameSize(GetGraph()->GetMaximumNumberOfOutVRegs() + GetGraph()->GetNumberOfLocalVRegs() + GetGraph()->GetNumberOfTemporaries() diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index 18e3e5a056..b31c3a3e83 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -70,7 +70,7 @@ class CodeGenerator : public ArenaObject { public: // Compiles the graph to executable instructions. Returns whether the compilation // succeeded. - void CompileBaseline(CodeAllocator* allocator); + void CompileBaseline(CodeAllocator* allocator, bool is_leaf = false); void CompileOptimized(CodeAllocator* allocator); static CodeGenerator* Create(ArenaAllocator* allocator, HGraph* graph, @@ -131,6 +131,14 @@ class CodeGenerator : public ArenaObject { void BuildNativeGCMap( std::vector<uint8_t>* vector, const DexCompilationUnit& dex_compilation_unit) const; + bool IsLeafMethod() const { + return is_leaf_; + } + + void MarkNotLeaf() { + is_leaf_ = false; + } + protected: CodeGenerator(HGraph* graph, size_t number_of_registers) : frame_size_(kUninitializedFrameSize), @@ -138,7 +146,8 @@ class CodeGenerator : public ArenaObject { block_labels_(graph->GetArena(), 0), pc_infos_(graph->GetArena(), 32), slow_paths_(graph->GetArena(), 8), - blocked_registers_(graph->GetArena()->AllocArray<bool>(number_of_registers)) {} + blocked_registers_(graph->GetArena()->AllocArray<bool>(number_of_registers)), + is_leaf_(true) {} ~CodeGenerator() {} // Register allocation logic. @@ -171,6 +180,8 @@ class CodeGenerator : public ArenaObject { // Temporary data structure used when doing register allocation. bool* const blocked_registers_; + bool is_leaf_; + DISALLOW_COPY_AND_ASSIGN(CodeGenerator); }; diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index 73c2d48320..eccc970042 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -17,12 +17,14 @@ #include "code_generator_arm.h" #include "entrypoints/quick/quick_entrypoints.h" +#include "gc/accounting/card_table.h" #include "mirror/array.h" #include "mirror/art_method.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 { @@ -32,6 +34,29 @@ arm::ArmManagedRegister Location::AsArm() const { namespace arm { +static constexpr bool kExplicitStackOverflowCheck = false; + +static constexpr int kNumberOfPushedRegistersAtEntry = 1 + 2; // LR, R6, R7 +static constexpr int kCurrentMethodStackOffset = 0; + +static Location ArmCoreLocation(Register reg) { + return Location::RegisterLocation(ArmManagedRegister::FromCoreRegister(reg)); +} + +static constexpr Register kRuntimeParameterCoreRegisters[] = { R0, R1, R2 }; +static constexpr size_t kRuntimeParameterCoreRegistersLength = + arraysize(kRuntimeParameterCoreRegisters); + +class InvokeRuntimeCallingConvention : public CallingConvention<Register> { + public: + InvokeRuntimeCallingConvention() + : CallingConvention(kRuntimeParameterCoreRegisters, + kRuntimeParameterCoreRegistersLength) {} + + private: + DISALLOW_COPY_AND_ASSIGN(InvokeRuntimeCallingConvention); +}; + #define __ reinterpret_cast<ArmAssembler*>(codegen->GetAssembler())-> class NullCheckSlowPathARM : public SlowPathCode { @@ -51,6 +76,47 @@ class NullCheckSlowPathARM : public SlowPathCode { DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathARM); }; +class StackOverflowCheckSlowPathARM : public SlowPathCode { + 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 BoundsCheckSlowPathARM : public SlowPathCode { + public: + explicit BoundsCheckSlowPathARM(uint32_t dex_pc, + Location index_location, + Location length_location) + : dex_pc_(dex_pc), index_location_(index_location), length_location_(length_location) {} + + virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + CodeGeneratorARM* arm_codegen = reinterpret_cast<CodeGeneratorARM*>(codegen); + __ Bind(GetEntryLabel()); + InvokeRuntimeCallingConvention calling_convention; + arm_codegen->Move32(ArmCoreLocation(calling_convention.GetRegisterAt(0)), index_location_); + arm_codegen->Move32(ArmCoreLocation(calling_convention.GetRegisterAt(1)), length_location_); + int32_t offset = QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pThrowArrayBounds).Int32Value(); + __ ldr(LR, Address(TR, offset)); + __ blx(LR); + codegen->RecordPcInfo(dex_pc_); + } + + private: + const uint32_t dex_pc_; + const Location index_location_; + const Location length_location_; + + DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathARM); +}; + #undef __ #define __ reinterpret_cast<ArmAssembler*>(GetAssembler())-> @@ -82,9 +148,6 @@ inline Condition ARMOppositeCondition(IfCondition cond) { return EQ; // Unreachable. } -static constexpr int kNumberOfPushedRegistersAtEntry = 1 + 2; // LR, R6, R7 -static constexpr int kCurrentMethodStackOffset = 0; - void CodeGeneratorARM::DumpCoreRegister(std::ostream& stream, int reg) const { stream << ArmManagedRegister::FromCoreRegister(Register(reg)); } @@ -97,7 +160,8 @@ CodeGeneratorARM::CodeGeneratorARM(HGraph* graph) : CodeGenerator(graph, kNumberOfRegIds), location_builder_(graph, this), instruction_visitor_(graph, this), - move_resolver_(graph->GetArena(), this) {} + move_resolver_(graph->GetArena(), this), + assembler_(true) {} size_t CodeGeneratorARM::FrameEntrySpillSize() const { return kNumberOfPushedRegistersAtEntry * kArmWordSize; @@ -195,16 +259,28 @@ size_t CodeGeneratorARM::GetNumberOfRegisters() const { return kNumberOfRegIds; } -static Location ArmCoreLocation(Register reg) { - return Location::RegisterLocation(ArmManagedRegister::FromCoreRegister(reg)); -} - InstructionCodeGeneratorARM::InstructionCodeGeneratorARM(HGraph* graph, CodeGeneratorARM* codegen) : HGraphVisitor(graph), assembler_(codegen->GetAssembler()), codegen_(codegen) {} void CodeGeneratorARM::GenerateFrameEntry() { + bool skip_overflow_check = IsLeafMethod() && !IsLargeFrame(GetFrameSize(), InstructionSet::kArm); + if (!skip_overflow_check) { + if (kExplicitStackOverflowCheck) { + SlowPathCode* slow_path = new (GetGraph()->GetArena()) StackOverflowCheckSlowPathARM(); + AddSlowPath(slow_path); + + __ LoadFromOffset(kLoadWord, IP, TR, Thread::StackEndOffset<kArmWordSize>().Int32Value()); + __ cmp(SP, ShifterOperand(IP)); + __ b(slow_path->GetEntryLabel(), CC); + } else { + __ AddConstant(IP, SP, -static_cast<int32_t>(GetStackOverflowReservedBytes(kArm))); + __ ldr(IP, Address(IP, 0)); + RecordPcInfo(0); + } + } + core_spill_mask_ |= (1 << LR | 1 << R6 | 1 << R7); __ PushList(1 << LR | 1 << R6 | 1 << R7); @@ -377,11 +453,17 @@ void CodeGeneratorARM::Move64(Location destination, Location source) { } void CodeGeneratorARM::Move(HInstruction* instruction, Location location, HInstruction* move_for) { + LocationSummary* locations = instruction->GetLocations(); + if (locations != nullptr && locations->Out().Equals(location)) { + return; + } + if (instruction->AsIntConstant() != nullptr) { int32_t value = instruction->AsIntConstant()->GetValue(); if (location.IsRegister()) { __ LoadImmediate(location.AsArm().AsCoreRegister(), value); } else { + DCHECK(location.IsStackSlot()); __ LoadImmediate(IP, value); __ str(IP, Address(SP, location.GetStackIndex())); } @@ -391,6 +473,7 @@ void CodeGeneratorARM::Move(HInstruction* instruction, Location location, HInstr __ LoadImmediate(location.AsArm().AsRegisterPairLow(), Low32Bits(value)); __ LoadImmediate(location.AsArm().AsRegisterPairHigh(), High32Bits(value)); } else { + DCHECK(location.IsDoubleStackSlot()); __ LoadImmediate(IP, Low32Bits(value)); __ str(IP, Address(SP, location.GetStackIndex())); __ LoadImmediate(IP, High32Bits(value)); @@ -424,11 +507,11 @@ void CodeGeneratorARM::Move(HInstruction* instruction, Location location, HInstr case Primitive::kPrimShort: case Primitive::kPrimNot: case Primitive::kPrimInt: - Move32(location, instruction->GetLocations()->Out()); + Move32(location, locations->Out()); break; case Primitive::kPrimLong: - Move64(location, instruction->GetLocations()->Out()); + Move64(location, locations->Out()); break; default: @@ -478,20 +561,33 @@ void InstructionCodeGeneratorARM::VisitIf(HIf* if_instr) { HCondition* condition = cond->AsCondition(); if (condition->NeedsMaterialization()) { // Condition has been materialized, compare the output to 0 - if (!if_instr->GetLocations()->InAt(0).IsRegister()) { - LOG(FATAL) << "Materialized condition is not in an ARM register"; - } + DCHECK(if_instr->GetLocations()->InAt(0).IsRegister()); __ cmp(if_instr->GetLocations()->InAt(0).AsArm().AsCoreRegister(), ShifterOperand(0)); __ b(codegen_->GetLabelOf(if_instr->IfTrueSuccessor()), EQ); } else { // Condition has not been materialized, use its inputs as the comparison and its // condition as the branch condition. - __ cmp(condition->GetLocations()->InAt(0).AsArm().AsCoreRegister(), - ShifterOperand(condition->GetLocations()->InAt(1).AsArm().AsCoreRegister())); + LocationSummary* locations = condition->GetLocations(); + if (locations->InAt(1).IsRegister()) { + __ cmp(locations->InAt(0).AsArm().AsCoreRegister(), + ShifterOperand(locations->InAt(1).AsArm().AsCoreRegister())); + } 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).AsArm().AsCoreRegister(), ShifterOperand(value)); + } else { + Register temp = IP; + __ LoadImmediate(temp, value); + __ cmp(locations->InAt(0).AsArm().AsCoreRegister(), ShifterOperand(temp)); + } + } __ b(codegen_->GetLabelOf(if_instr->IfTrueSuccessor()), ARMCondition(condition->GetCondition())); } + if (!codegen_->GoesToNextBlock(if_instr->GetBlock(), if_instr->IfFalseSuccessor())) { __ b(codegen_->GetLabelOf(if_instr->IfFalseSuccessor())); } @@ -501,7 +597,7 @@ void InstructionCodeGeneratorARM::VisitIf(HIf* if_instr) { void LocationsBuilderARM::VisitCondition(HCondition* comp) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(comp); locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); + locations->SetInAt(1, Location::RegisterOrConstant(comp->InputAt(1))); if (comp->NeedsMaterialization()) { locations->SetOut(Location::RequiresRegister()); } @@ -509,16 +605,29 @@ void LocationsBuilderARM::VisitCondition(HCondition* comp) { } void InstructionCodeGeneratorARM::VisitCondition(HCondition* comp) { - if (comp->NeedsMaterialization()) { - LocationSummary* locations = comp->GetLocations(); + if (!comp->NeedsMaterialization()) return; + + LocationSummary* locations = comp->GetLocations(); + if (locations->InAt(1).IsRegister()) { __ cmp(locations->InAt(0).AsArm().AsCoreRegister(), ShifterOperand(locations->InAt(1).AsArm().AsCoreRegister())); - __ it(ARMCondition(comp->GetCondition()), kItElse); - __ mov(locations->Out().AsArm().AsCoreRegister(), ShifterOperand(1), - ARMCondition(comp->GetCondition())); - __ mov(locations->Out().AsArm().AsCoreRegister(), ShifterOperand(0), - ARMOppositeCondition(comp->GetCondition())); + } 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).AsArm().AsCoreRegister(), ShifterOperand(value)); + } else { + Register temp = IP; + __ LoadImmediate(temp, value); + __ cmp(locations->InAt(0).AsArm().AsCoreRegister(), ShifterOperand(temp)); + } } + __ it(ARMCondition(comp->GetCondition()), kItElse); + __ mov(locations->Out().AsArm().AsCoreRegister(), ShifterOperand(1), + ARMCondition(comp->GetCondition())); + __ mov(locations->Out().AsArm().AsCoreRegister(), ShifterOperand(0), + ARMOppositeCondition(comp->GetCondition())); } void LocationsBuilderARM::VisitEqual(HEqual* comp) { @@ -611,20 +720,17 @@ void InstructionCodeGeneratorARM::VisitStoreLocal(HStoreLocal* store) { } void LocationsBuilderARM::VisitIntConstant(HIntConstant* constant) { - // TODO: Support constant locations. LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant); - locations->SetOut(Location::RequiresRegister()); + locations->SetOut(Location::ConstantLocation(constant)); constant->SetLocations(locations); } void InstructionCodeGeneratorARM::VisitIntConstant(HIntConstant* constant) { - codegen_->Move(constant, constant->GetLocations()->Out(), nullptr); } void LocationsBuilderARM::VisitLongConstant(HLongConstant* constant) { - // TODO: Support constant locations. LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant); - locations->SetOut(Location::RequiresRegister()); + locations->SetOut(Location::ConstantLocation(constant)); constant->SetLocations(locations); } @@ -688,6 +794,7 @@ void InstructionCodeGeneratorARM::VisitReturn(HReturn* ret) { } void LocationsBuilderARM::VisitInvokeStatic(HInvokeStatic* invoke) { + codegen_->MarkNotLeaf(); LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(invoke); locations->AddTemp(ArmCoreLocation(R0)); @@ -753,6 +860,7 @@ void InstructionCodeGeneratorARM::VisitInvokeStatic(HInvokeStatic* invoke) { __ blx(LR); codegen_->RecordPcInfo(invoke->GetDexPc()); + DCHECK(!codegen_->IsLeafMethod()); } void LocationsBuilderARM::VisitAdd(HAdd* add) { @@ -761,7 +869,7 @@ void LocationsBuilderARM::VisitAdd(HAdd* add) { case Primitive::kPrimInt: case Primitive::kPrimLong: { locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); + locations->SetInAt(1, Location::RegisterOrConstant(add->InputAt(1))); locations->SetOut(Location::RequiresRegister()); break; } @@ -783,9 +891,15 @@ void InstructionCodeGeneratorARM::VisitAdd(HAdd* add) { LocationSummary* locations = add->GetLocations(); switch (add->GetResultType()) { case Primitive::kPrimInt: - __ add(locations->Out().AsArm().AsCoreRegister(), - locations->InAt(0).AsArm().AsCoreRegister(), - ShifterOperand(locations->InAt(1).AsArm().AsCoreRegister())); + if (locations->InAt(1).IsRegister()) { + __ add(locations->Out().AsArm().AsCoreRegister(), + locations->InAt(0).AsArm().AsCoreRegister(), + ShifterOperand(locations->InAt(1).AsArm().AsCoreRegister())); + } else { + __ AddConstant(locations->Out().AsArm().AsCoreRegister(), + locations->InAt(0).AsArm().AsCoreRegister(), + locations->InAt(1).GetConstant()->AsIntConstant()->GetValue()); + } break; case Primitive::kPrimLong: @@ -815,7 +929,7 @@ void LocationsBuilderARM::VisitSub(HSub* sub) { case Primitive::kPrimInt: case Primitive::kPrimLong: { locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); + locations->SetInAt(1, Location::RegisterOrConstant(sub->InputAt(1))); locations->SetOut(Location::RequiresRegister()); break; } @@ -836,11 +950,18 @@ void LocationsBuilderARM::VisitSub(HSub* sub) { void InstructionCodeGeneratorARM::VisitSub(HSub* sub) { LocationSummary* locations = sub->GetLocations(); switch (sub->GetResultType()) { - case Primitive::kPrimInt: - __ sub(locations->Out().AsArm().AsCoreRegister(), - locations->InAt(0).AsArm().AsCoreRegister(), - ShifterOperand(locations->InAt(1).AsArm().AsCoreRegister())); + case Primitive::kPrimInt: { + if (locations->InAt(1).IsRegister()) { + __ sub(locations->Out().AsArm().AsCoreRegister(), + locations->InAt(0).AsArm().AsCoreRegister(), + ShifterOperand(locations->InAt(1).AsArm().AsCoreRegister())); + } else { + __ AddConstant(locations->Out().AsArm().AsCoreRegister(), + locations->InAt(0).AsArm().AsCoreRegister(), + -locations->InAt(1).GetConstant()->AsIntConstant()->GetValue()); + } break; + } case Primitive::kPrimLong: __ subs(locations->Out().AsArm().AsRegisterPairLow(), @@ -863,21 +984,8 @@ void InstructionCodeGeneratorARM::VisitSub(HSub* sub) { } } -static constexpr Register kRuntimeParameterCoreRegisters[] = { R0, R1 }; -static constexpr size_t kRuntimeParameterCoreRegistersLength = - arraysize(kRuntimeParameterCoreRegisters); - -class InvokeRuntimeCallingConvention : public CallingConvention<Register> { - public: - InvokeRuntimeCallingConvention() - : CallingConvention(kRuntimeParameterCoreRegisters, - kRuntimeParameterCoreRegistersLength) {} - - private: - DISALLOW_COPY_AND_ASSIGN(InvokeRuntimeCallingConvention); -}; - void LocationsBuilderARM::VisitNewInstance(HNewInstance* instruction) { + codegen_->MarkNotLeaf(); LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); InvokeRuntimeCallingConvention calling_convention; locations->AddTemp(ArmCoreLocation(calling_convention.GetRegisterAt(0))); @@ -896,6 +1004,7 @@ void InstructionCodeGeneratorARM::VisitNewInstance(HNewInstance* instruction) { __ blx(LR); codegen_->RecordPcInfo(instruction->GetDexPc()); + DCHECK(!codegen_->IsLeafMethod()); } void LocationsBuilderARM::VisitParameterValue(HParameterValue* instruction) { @@ -948,9 +1057,11 @@ void InstructionCodeGeneratorARM::VisitCompare(HCompare* compare) { 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. - __ LoadImmediate(output, 0); __ b(&done, EQ); __ b(&less, CC); @@ -986,6 +1097,11 @@ void LocationsBuilderARM::VisitInstanceFieldSet(HInstanceFieldSet* instruction) LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); locations->SetInAt(0, Location::RequiresRegister()); locations->SetInAt(1, Location::RequiresRegister()); + // Temporary registers for the write barrier. + if (instruction->InputAt(1)->GetType() == Primitive::kPrimNot) { + locations->AddTemp(Location::RequiresRegister()); + locations->AddTemp(Location::RequiresRegister()); + } instruction->SetLocations(locations); } @@ -1014,6 +1130,11 @@ void InstructionCodeGeneratorARM::VisitInstanceFieldSet(HInstanceFieldSet* instr case Primitive::kPrimNot: { Register value = locations->InAt(1).AsArm().AsCoreRegister(); __ StoreToOffset(kStoreWord, value, obj, offset); + if (field_type == Primitive::kPrimNot) { + Register temp = locations->GetTemp(0).AsArm().AsCoreRegister(); + Register card = locations->GetTemp(1).AsArm().AsCoreRegister(); + codegen_->MarkGCCard(temp, card, obj, value); + } break; } @@ -1115,6 +1236,251 @@ void InstructionCodeGeneratorARM::VisitNullCheck(HNullCheck* instruction) { __ b(slow_path->GetEntryLabel(), EQ); } +void LocationsBuilderARM::VisitArrayGet(HArrayGet* instruction) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1))); + locations->SetOut(Location::RequiresRegister()); + instruction->SetLocations(locations); +} + +void InstructionCodeGeneratorARM::VisitArrayGet(HArrayGet* instruction) { + LocationSummary* locations = instruction->GetLocations(); + Register obj = locations->InAt(0).AsArm().AsCoreRegister(); + Location index = locations->InAt(1); + + switch (instruction->GetType()) { + case Primitive::kPrimBoolean: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value(); + Register out = locations->Out().AsArm().AsCoreRegister(); + if (index.IsConstant()) { + size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset; + __ LoadFromOffset(kLoadUnsignedByte, out, obj, offset); + } else { + __ add(IP, obj, ShifterOperand(index.AsArm().AsCoreRegister())); + __ LoadFromOffset(kLoadUnsignedByte, out, IP, data_offset); + } + break; + } + + case Primitive::kPrimByte: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(int8_t)).Uint32Value(); + Register out = locations->Out().AsArm().AsCoreRegister(); + if (index.IsConstant()) { + size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset; + __ LoadFromOffset(kLoadSignedByte, out, obj, offset); + } else { + __ add(IP, obj, ShifterOperand(index.AsArm().AsCoreRegister())); + __ LoadFromOffset(kLoadSignedByte, out, IP, data_offset); + } + break; + } + + case Primitive::kPrimShort: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(int16_t)).Uint32Value(); + Register out = locations->Out().AsArm().AsCoreRegister(); + if (index.IsConstant()) { + size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset; + __ LoadFromOffset(kLoadSignedHalfword, out, obj, offset); + } else { + __ add(IP, obj, ShifterOperand(index.AsArm().AsCoreRegister(), LSL, TIMES_2)); + __ LoadFromOffset(kLoadSignedHalfword, out, IP, data_offset); + } + break; + } + + case Primitive::kPrimChar: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value(); + Register out = locations->Out().AsArm().AsCoreRegister(); + if (index.IsConstant()) { + size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset; + __ LoadFromOffset(kLoadUnsignedHalfword, out, obj, offset); + } else { + __ add(IP, obj, ShifterOperand(index.AsArm().AsCoreRegister(), LSL, TIMES_2)); + __ LoadFromOffset(kLoadUnsignedHalfword, out, IP, data_offset); + } + break; + } + + case Primitive::kPrimInt: + case Primitive::kPrimNot: { + DCHECK_EQ(sizeof(mirror::HeapReference<mirror::Object>), sizeof(int32_t)); + uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value(); + Register out = locations->Out().AsArm().AsCoreRegister(); + if (index.IsConstant()) { + size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; + __ LoadFromOffset(kLoadWord, out, obj, offset); + } else { + __ add(IP, obj, ShifterOperand(index.AsArm().AsCoreRegister(), LSL, TIMES_4)); + __ LoadFromOffset(kLoadWord, out, IP, data_offset); + } + break; + } + + case Primitive::kPrimLong: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value(); + ArmManagedRegister out = locations->Out().AsArm(); + if (index.IsConstant()) { + size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; + __ LoadFromOffset(kLoadWordPair, out.AsRegisterPairLow(), obj, offset); + } else { + __ add(IP, obj, ShifterOperand(index.AsArm().AsCoreRegister(), LSL, TIMES_8)); + __ LoadFromOffset(kLoadWordPair, out.AsRegisterPairLow(), IP, data_offset); + } + break; + } + + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + LOG(FATAL) << "Unimplemented register type " << instruction->GetType(); + + case Primitive::kPrimVoid: + LOG(FATAL) << "Unreachable type " << instruction->GetType(); + } +} + +void LocationsBuilderARM::VisitArraySet(HArraySet* instruction) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); + Primitive::Type value_type = instruction->InputAt(2)->GetType(); + if (value_type == Primitive::kPrimNot) { + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, ArmCoreLocation(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, ArmCoreLocation(calling_convention.GetRegisterAt(1))); + locations->SetInAt(2, ArmCoreLocation(calling_convention.GetRegisterAt(2))); + codegen_->MarkNotLeaf(); + } else { + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1))); + locations->SetInAt(2, Location::RequiresRegister()); + } + instruction->SetLocations(locations); +} + +void InstructionCodeGeneratorARM::VisitArraySet(HArraySet* instruction) { + LocationSummary* locations = instruction->GetLocations(); + Register obj = locations->InAt(0).AsArm().AsCoreRegister(); + Location index = locations->InAt(1); + Primitive::Type value_type = instruction->InputAt(2)->GetType(); + + switch (value_type) { + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value(); + Register value = locations->InAt(2).AsArm().AsCoreRegister(); + if (index.IsConstant()) { + size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset; + __ StoreToOffset(kStoreByte, value, obj, offset); + } else { + __ add(IP, obj, ShifterOperand(index.AsArm().AsCoreRegister())); + __ StoreToOffset(kStoreByte, value, IP, data_offset); + } + break; + } + + case Primitive::kPrimShort: + case Primitive::kPrimChar: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value(); + Register value = locations->InAt(2).AsArm().AsCoreRegister(); + if (index.IsConstant()) { + size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset; + __ StoreToOffset(kStoreHalfword, value, obj, offset); + } else { + __ add(IP, obj, ShifterOperand(index.AsArm().AsCoreRegister(), LSL, TIMES_2)); + __ StoreToOffset(kStoreHalfword, value, IP, data_offset); + } + break; + } + + case Primitive::kPrimInt: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value(); + Register value = locations->InAt(2).AsArm().AsCoreRegister(); + if (index.IsConstant()) { + size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; + __ StoreToOffset(kStoreWord, value, obj, offset); + } else { + __ add(IP, obj, ShifterOperand(index.AsArm().AsCoreRegister(), LSL, TIMES_4)); + __ StoreToOffset(kStoreWord, value, IP, data_offset); + } + break; + } + + case Primitive::kPrimNot: { + int32_t offset = QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pAputObject).Int32Value(); + __ ldr(LR, Address(TR, offset)); + __ blx(LR); + codegen_->RecordPcInfo(instruction->GetDexPc()); + DCHECK(!codegen_->IsLeafMethod()); + break; + } + + case Primitive::kPrimLong: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value(); + ArmManagedRegister value = locations->InAt(2).AsArm(); + if (index.IsConstant()) { + size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; + __ StoreToOffset(kStoreWordPair, value.AsRegisterPairLow(), obj, offset); + } else { + __ add(IP, obj, ShifterOperand(index.AsArm().AsCoreRegister(), LSL, TIMES_8)); + __ StoreToOffset(kStoreWordPair, value.AsRegisterPairLow(), IP, data_offset); + } + break; + } + + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + LOG(FATAL) << "Unimplemented register type " << instruction->GetType(); + + case Primitive::kPrimVoid: + LOG(FATAL) << "Unreachable type " << instruction->GetType(); + } +} + +void LocationsBuilderARM::VisitArrayLength(HArrayLength* instruction) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister()); + instruction->SetLocations(locations); +} + +void InstructionCodeGeneratorARM::VisitArrayLength(HArrayLength* instruction) { + LocationSummary* locations = instruction->GetLocations(); + uint32_t offset = mirror::Array::LengthOffset().Uint32Value(); + Register obj = locations->InAt(0).AsArm().AsCoreRegister(); + Register out = locations->Out().AsArm().AsCoreRegister(); + __ LoadFromOffset(kLoadWord, out, obj, offset); +} + +void LocationsBuilderARM::VisitBoundsCheck(HBoundsCheck* instruction) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + // TODO: Have a normalization phase that makes this instruction never used. + locations->SetOut(Location::SameAsFirstInput()); + instruction->SetLocations(locations); +} + +void InstructionCodeGeneratorARM::VisitBoundsCheck(HBoundsCheck* instruction) { + LocationSummary* locations = instruction->GetLocations(); + SlowPathCode* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathARM( + instruction->GetDexPc(), locations->InAt(0), locations->InAt(1)); + codegen_->AddSlowPath(slow_path); + + Register index = locations->InAt(0).AsArm().AsCoreRegister(); + Register length = locations->InAt(1).AsArm().AsCoreRegister(); + + __ cmp(index, ShifterOperand(length)); + __ b(slow_path->GetEntryLabel(), CS); +} + +void CodeGeneratorARM::MarkGCCard(Register temp, Register card, Register object, Register value) { + Label is_null; + __ CompareAndBranchIfZero(value, &is_null); + __ LoadFromOffset(kLoadWord, card, TR, Thread::CardTableOffset<kArmWordSize>().Int32Value()); + __ Lsr(temp, object, gc::accounting::CardTable::kCardShift); + __ strb(card, Address(card, temp)); + __ Bind(&is_null); +} + void LocationsBuilderARM::VisitTemporary(HTemporary* temp) { temp->SetLocations(nullptr); } @@ -1158,7 +1524,16 @@ void ParallelMoveResolverARM::EmitMove(size_t index) { __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex()); } } else { - LOG(FATAL) << "Unimplemented"; + DCHECK(source.IsConstant()); + DCHECK(source.GetConstant()->AsIntConstant() != nullptr); + int32_t value = source.GetConstant()->AsIntConstant()->GetValue(); + if (destination.IsRegister()) { + __ LoadImmediate(destination.AsArm().AsCoreRegister(), value); + } else { + DCHECK(destination.IsStackSlot()); + __ LoadImmediate(IP, value); + __ str(IP, Address(SP, destination.GetStackIndex())); + } } } diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h index 1b5974f9a2..610625c50b 100644 --- a/compiler/optimizing/code_generator_arm.h +++ b/compiler/optimizing/code_generator_arm.h @@ -20,7 +20,7 @@ #include "code_generator.h" #include "nodes.h" #include "parallel_move_resolver.h" -#include "utils/arm/assembler_arm32.h" +#include "utils/arm/assembler_thumb2.h" namespace art { namespace arm { @@ -89,7 +89,7 @@ class LocationsBuilderARM : public HGraphVisitor { #define DECLARE_VISIT_INSTRUCTION(name) \ virtual void Visit##name(H##name* instr); - FOR_EACH_INSTRUCTION(DECLARE_VISIT_INSTRUCTION) + FOR_EACH_CONCRETE_INSTRUCTION(DECLARE_VISIT_INSTRUCTION) #undef DECLARE_VISIT_INSTRUCTION @@ -107,7 +107,7 @@ class InstructionCodeGeneratorARM : public HGraphVisitor { #define DECLARE_VISIT_INSTRUCTION(name) \ virtual void Visit##name(H##name* instr); - FOR_EACH_INSTRUCTION(DECLARE_VISIT_INSTRUCTION) + FOR_EACH_CONCRETE_INSTRUCTION(DECLARE_VISIT_INSTRUCTION) #undef DECLARE_VISIT_INSTRUCTION @@ -172,19 +172,22 @@ class CodeGeneratorARM : public CodeGenerator { } virtual InstructionSet GetInstructionSet() const OVERRIDE { - return InstructionSet::kArm; + return InstructionSet::kThumb2; } - private: // Helper method to move a 32bits value between two locations. void Move32(Location destination, Location source); // Helper method to move a 64bits value between two locations. void Move64(Location destination, Location source); + // Emit a write barrier. + void MarkGCCard(Register temp, Register card, Register object, Register value); + + private: LocationsBuilderARM location_builder_; InstructionCodeGeneratorARM instruction_visitor_; ParallelMoveResolverARM move_resolver_; - Arm32Assembler assembler_; + Thumb2Assembler assembler_; DISALLOW_COPY_AND_ASSIGN(CodeGeneratorARM); }; diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 4e69a0cad8..ab53b17636 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -15,14 +15,16 @@ */ #include "code_generator_x86.h" -#include "utils/assembler.h" -#include "utils/x86/assembler_x86.h" -#include "utils/x86/managed_register_x86.h" #include "entrypoints/quick/quick_entrypoints.h" +#include "gc/accounting/card_table.h" #include "mirror/array.h" #include "mirror/art_method.h" #include "thread.h" +#include "utils/assembler.h" +#include "utils/stack_checks.h" +#include "utils/x86/assembler_x86.h" +#include "utils/x86/managed_register_x86.h" namespace art { @@ -32,6 +34,29 @@ x86::X86ManagedRegister Location::AsX86() const { namespace x86 { +static constexpr bool kExplicitStackOverflowCheck = false; + +static constexpr int kNumberOfPushedRegistersAtEntry = 1; +static constexpr int kCurrentMethodStackOffset = 0; + +static Location X86CpuLocation(Register reg) { + return Location::RegisterLocation(X86ManagedRegister::FromCpuRegister(reg)); +} + +static constexpr Register kRuntimeParameterCoreRegisters[] = { EAX, ECX, EDX }; +static constexpr size_t kRuntimeParameterCoreRegistersLength = + arraysize(kRuntimeParameterCoreRegisters); + +class InvokeRuntimeCallingConvention : public CallingConvention<Register> { + public: + InvokeRuntimeCallingConvention() + : CallingConvention(kRuntimeParameterCoreRegisters, + kRuntimeParameterCoreRegistersLength) {} + + private: + DISALLOW_COPY_AND_ASSIGN(InvokeRuntimeCallingConvention); +}; + #define __ reinterpret_cast<X86Assembler*>(codegen->GetAssembler())-> class NullCheckSlowPathX86 : public SlowPathCode { @@ -49,6 +74,46 @@ class NullCheckSlowPathX86 : public SlowPathCode { DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathX86); }; +class StackOverflowCheckSlowPathX86 : public SlowPathCode { + public: + StackOverflowCheckSlowPathX86() {} + + virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + __ Bind(GetEntryLabel()); + __ addl(ESP, + Immediate(codegen->GetFrameSize() - kNumberOfPushedRegistersAtEntry * kX86WordSize)); + __ fs()->jmp(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pThrowStackOverflow))); + } + + private: + DISALLOW_COPY_AND_ASSIGN(StackOverflowCheckSlowPathX86); +}; + +class BoundsCheckSlowPathX86 : public SlowPathCode { + public: + explicit BoundsCheckSlowPathX86(uint32_t dex_pc, + Location index_location, + Location length_location) + : dex_pc_(dex_pc), index_location_(index_location), length_location_(length_location) {} + + virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + CodeGeneratorX86* x86_codegen = reinterpret_cast<CodeGeneratorX86*>(codegen); + __ Bind(GetEntryLabel()); + InvokeRuntimeCallingConvention calling_convention; + x86_codegen->Move32(X86CpuLocation(calling_convention.GetRegisterAt(0)), index_location_); + x86_codegen->Move32(X86CpuLocation(calling_convention.GetRegisterAt(1)), length_location_); + __ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pThrowArrayBounds))); + codegen->RecordPcInfo(dex_pc_); + } + + private: + const uint32_t dex_pc_; + const Location index_location_; + const Location length_location_; + + DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathX86); +}; + #undef __ #define __ reinterpret_cast<X86Assembler*>(GetAssembler())-> @@ -66,9 +131,6 @@ inline Condition X86Condition(IfCondition cond) { return kEqual; } -static constexpr int kNumberOfPushedRegistersAtEntry = 1; -static constexpr int kCurrentMethodStackOffset = 0; - void CodeGeneratorX86::DumpCoreRegister(std::ostream& stream, int reg) const { stream << X86ManagedRegister::FromCpuRegister(Register(reg)); } @@ -169,10 +231,6 @@ size_t CodeGeneratorX86::GetNumberOfRegisters() const { return kNumberOfRegIds; } -static Location X86CpuLocation(Register reg) { - return Location::RegisterLocation(X86ManagedRegister::FromCpuRegister(reg)); -} - InstructionCodeGeneratorX86::InstructionCodeGeneratorX86(HGraph* graph, CodeGeneratorX86* codegen) : HGraphVisitor(graph), assembler_(codegen->GetAssembler()), @@ -183,8 +241,23 @@ void CodeGeneratorX86::GenerateFrameEntry() { static const int kFakeReturnRegister = 8; core_spill_mask_ |= (1 << kFakeReturnRegister); + bool skip_overflow_check = IsLeafMethod() && !IsLargeFrame(GetFrameSize(), InstructionSet::kX86); + if (!skip_overflow_check && !kExplicitStackOverflowCheck) { + __ testl(EAX, Address(ESP, -static_cast<int32_t>(GetStackOverflowReservedBytes(kX86)))); + RecordPcInfo(0); + } + // The return PC has already been pushed on the stack. __ subl(ESP, Immediate(GetFrameSize() - kNumberOfPushedRegistersAtEntry * kX86WordSize)); + + if (!skip_overflow_check && kExplicitStackOverflowCheck) { + SlowPathCode* slow_path = new (GetGraph()->GetArena()) StackOverflowCheckSlowPathX86(); + AddSlowPath(slow_path); + + __ fs()->cmpl(ESP, Address::Absolute(Thread::StackEndOffset<kX86WordSize>())); + __ j(kLess, slow_path->GetEntryLabel()); + } + __ movl(Address(ESP, kCurrentMethodStackOffset), EAX); } @@ -226,20 +299,6 @@ Location CodeGeneratorX86::GetStackLocation(HLoadLocal* load) const { return Location(); } -static constexpr Register kRuntimeParameterCoreRegisters[] = { EAX, ECX, EDX }; -static constexpr size_t kRuntimeParameterCoreRegistersLength = - arraysize(kRuntimeParameterCoreRegisters); - -class InvokeRuntimeCallingConvention : public CallingConvention<Register> { - public: - InvokeRuntimeCallingConvention() - : CallingConvention(kRuntimeParameterCoreRegisters, - kRuntimeParameterCoreRegistersLength) {} - - private: - DISALLOW_COPY_AND_ASSIGN(InvokeRuntimeCallingConvention); -}; - Location InvokeDexCallingConventionVisitor::GetNextLocation(Primitive::Type type) { switch (type) { case Primitive::kPrimBoolean: @@ -473,6 +532,10 @@ void InstructionCodeGeneratorX86::VisitIf(HIf* if_instr) { // LHS is guaranteed to be in a register (see LocationsBuilderX86::VisitCondition). if (rhs.IsRegister()) { __ cmpl(lhs.AsX86().AsCpuRegister(), rhs.AsX86().AsCpuRegister()); + } else if (rhs.IsConstant()) { + HIntConstant* instruction = rhs.GetConstant()->AsIntConstant(); + Immediate imm(instruction->AsIntConstant()->GetValue()); + __ cmpl(lhs.AsX86().AsCpuRegister(), imm); } else { __ cmpl(lhs.AsX86().AsCpuRegister(), Address(ESP, rhs.GetStackIndex())); } @@ -530,7 +593,7 @@ void LocationsBuilderX86::VisitCondition(HCondition* comp) { locations->SetInAt(0, Location::RequiresRegister()); locations->SetInAt(1, Location::Any()); if (comp->NeedsMaterialization()) { - locations->SetOut(Location::SameAsFirstInput()); + locations->SetOut(Location::RequiresRegister()); } comp->SetLocations(locations); } @@ -541,6 +604,10 @@ void InstructionCodeGeneratorX86::VisitCondition(HCondition* comp) { if (locations->InAt(1).IsRegister()) { __ cmpl(locations->InAt(0).AsX86().AsCpuRegister(), locations->InAt(1).AsX86().AsCpuRegister()); + } else if (locations->InAt(1).IsConstant()) { + HConstant* instruction = locations->InAt(1).GetConstant(); + Immediate imm(instruction->AsIntConstant()->GetValue()); + __ cmpl(locations->InAt(0).AsX86().AsCpuRegister(), imm); } else { __ cmpl(locations->InAt(0).AsX86().AsCpuRegister(), Address(ESP, locations->InAt(1).GetStackIndex())); @@ -598,20 +665,17 @@ void InstructionCodeGeneratorX86::VisitGreaterThanOrEqual(HGreaterThanOrEqual* c } void LocationsBuilderX86::VisitIntConstant(HIntConstant* constant) { - // TODO: Support constant locations. LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant); - locations->SetOut(Location::RequiresRegister()); + locations->SetOut(Location::ConstantLocation(constant)); constant->SetLocations(locations); } void InstructionCodeGeneratorX86::VisitIntConstant(HIntConstant* constant) { - codegen_->Move(constant, constant->GetLocations()->Out(), nullptr); } void LocationsBuilderX86::VisitLongConstant(HLongConstant* constant) { - // TODO: Support constant locations. LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant); - locations->SetOut(Location::RequiresRegister()); + locations->SetOut(Location::ConstantLocation(constant)); constant->SetLocations(locations); } @@ -676,6 +740,7 @@ void InstructionCodeGeneratorX86::VisitReturn(HReturn* ret) { } void LocationsBuilderX86::VisitInvokeStatic(HInvokeStatic* invoke) { + codegen_->MarkNotLeaf(); LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(invoke); locations->AddTemp(X86CpuLocation(EAX)); @@ -733,6 +798,7 @@ void InstructionCodeGeneratorX86::VisitInvokeStatic(HInvokeStatic* invoke) { // (temp + offset_of_quick_compiled_code)() __ call(Address(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value())); + DCHECK(!codegen_->IsLeafMethod()); codegen_->RecordPcInfo(invoke->GetDexPc()); } @@ -769,6 +835,10 @@ void InstructionCodeGeneratorX86::VisitAdd(HAdd* add) { if (locations->InAt(1).IsRegister()) { __ addl(locations->InAt(0).AsX86().AsCpuRegister(), locations->InAt(1).AsX86().AsCpuRegister()); + } else if (locations->InAt(1).IsConstant()) { + HConstant* instruction = locations->InAt(1).GetConstant(); + Immediate imm(instruction->AsIntConstant()->GetValue()); + __ addl(locations->InAt(0).AsX86().AsCpuRegister(), imm); } else { __ addl(locations->InAt(0).AsX86().AsCpuRegister(), Address(ESP, locations->InAt(1).GetStackIndex())); @@ -838,6 +908,10 @@ void InstructionCodeGeneratorX86::VisitSub(HSub* sub) { if (locations->InAt(1).IsRegister()) { __ subl(locations->InAt(0).AsX86().AsCpuRegister(), locations->InAt(1).AsX86().AsCpuRegister()); + } else if (locations->InAt(1).IsConstant()) { + HConstant* instruction = locations->InAt(1).GetConstant(); + Immediate imm(instruction->AsIntConstant()->GetValue()); + __ subl(locations->InAt(0).AsX86().AsCpuRegister(), imm); } else { __ subl(locations->InAt(0).AsX86().AsCpuRegister(), Address(ESP, locations->InAt(1).GetStackIndex())); @@ -875,6 +949,7 @@ void InstructionCodeGeneratorX86::VisitSub(HSub* sub) { } void LocationsBuilderX86::VisitNewInstance(HNewInstance* instruction) { + codegen_->MarkNotLeaf(); LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); locations->SetOut(X86CpuLocation(EAX)); InvokeRuntimeCallingConvention calling_convention; @@ -892,6 +967,7 @@ void InstructionCodeGeneratorX86::VisitNewInstance(HNewInstance* instruction) { Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pAllocObjectWithAccessCheck))); codegen_->RecordPcInfo(instruction->GetDexPc()); + DCHECK(!codegen_->IsLeafMethod()); } void LocationsBuilderX86::VisitParameterValue(HParameterValue* instruction) { @@ -996,6 +1072,12 @@ void LocationsBuilderX86::VisitInstanceFieldSet(HInstanceFieldSet* instruction) } else { locations->SetInAt(1, Location::RequiresRegister()); } + // Temporary registers for the write barrier. + if (field_type == Primitive::kPrimNot) { + locations->AddTemp(Location::RequiresRegister()); + // Ensure the card is in a byte register. + locations->AddTemp(X86CpuLocation(ECX)); + } instruction->SetLocations(locations); } @@ -1024,6 +1106,12 @@ void InstructionCodeGeneratorX86::VisitInstanceFieldSet(HInstanceFieldSet* instr case Primitive::kPrimNot: { Register value = locations->InAt(1).AsX86().AsCpuRegister(); __ movl(Address(obj, offset), value); + + if (field_type == Primitive::kPrimNot) { + Register temp = locations->GetTemp(0).AsX86().AsCpuRegister(); + Register card = locations->GetTemp(1).AsX86().AsCpuRegister(); + codegen_->MarkGCCard(temp, card, obj, value); + } break; } @@ -1043,6 +1131,18 @@ void InstructionCodeGeneratorX86::VisitInstanceFieldSet(HInstanceFieldSet* instr } } +void CodeGeneratorX86::MarkGCCard(Register temp, Register card, Register object, Register value) { + Label is_null; + __ testl(value, value); + __ j(kEqual, &is_null); + __ fs()->movl(card, Address::Absolute(Thread::CardTableOffset<kX86WordSize>().Int32Value())); + __ movl(temp, object); + __ shrl(temp, Immediate(gc::accounting::CardTable::kCardShift)); + __ movb(Address(temp, card, TIMES_1, 0), + X86ManagedRegister::FromCpuRegister(card).AsByteRegister()); + __ Bind(&is_null); +} + void LocationsBuilderX86::VisitInstanceFieldGet(HInstanceFieldGet* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); locations->SetInAt(0, Location::RequiresRegister()); @@ -1130,6 +1230,243 @@ void InstructionCodeGeneratorX86::VisitNullCheck(HNullCheck* instruction) { __ j(kEqual, slow_path->GetEntryLabel()); } +void LocationsBuilderX86::VisitArrayGet(HArrayGet* instruction) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1))); + locations->SetOut(Location::RequiresRegister()); + instruction->SetLocations(locations); +} + +void InstructionCodeGeneratorX86::VisitArrayGet(HArrayGet* instruction) { + LocationSummary* locations = instruction->GetLocations(); + Register obj = locations->InAt(0).AsX86().AsCpuRegister(); + Location index = locations->InAt(1); + + switch (instruction->GetType()) { + case Primitive::kPrimBoolean: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value(); + Register out = locations->Out().AsX86().AsCpuRegister(); + if (index.IsConstant()) { + __ movzxb(out, Address(obj, + (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset)); + } else { + __ movzxb(out, Address(obj, index.AsX86().AsCpuRegister(), TIMES_1, data_offset)); + } + break; + } + + case Primitive::kPrimByte: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(int8_t)).Uint32Value(); + Register out = locations->Out().AsX86().AsCpuRegister(); + if (index.IsConstant()) { + __ movsxb(out, Address(obj, + (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset)); + } else { + __ movsxb(out, Address(obj, index.AsX86().AsCpuRegister(), TIMES_1, data_offset)); + } + break; + } + + case Primitive::kPrimShort: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(int16_t)).Uint32Value(); + Register out = locations->Out().AsX86().AsCpuRegister(); + if (index.IsConstant()) { + __ movsxw(out, Address(obj, + (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset)); + } else { + __ movsxw(out, Address(obj, index.AsX86().AsCpuRegister(), TIMES_2, data_offset)); + } + break; + } + + case Primitive::kPrimChar: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value(); + Register out = locations->Out().AsX86().AsCpuRegister(); + if (index.IsConstant()) { + __ movzxw(out, Address(obj, + (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset)); + } else { + __ movzxw(out, Address(obj, index.AsX86().AsCpuRegister(), TIMES_2, data_offset)); + } + break; + } + + case Primitive::kPrimInt: + case Primitive::kPrimNot: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value(); + Register out = locations->Out().AsX86().AsCpuRegister(); + if (index.IsConstant()) { + __ movl(out, Address(obj, + (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset)); + } else { + __ movl(out, Address(obj, index.AsX86().AsCpuRegister(), TIMES_4, data_offset)); + } + break; + } + + case Primitive::kPrimLong: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value(); + X86ManagedRegister out = locations->Out().AsX86(); + if (index.IsConstant()) { + size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; + __ movl(out.AsRegisterPairLow(), Address(obj, offset)); + __ movl(out.AsRegisterPairHigh(), Address(obj, offset + kX86WordSize)); + } else { + __ movl(out.AsRegisterPairLow(), + Address(obj, index.AsX86().AsCpuRegister(), TIMES_8, data_offset)); + __ movl(out.AsRegisterPairHigh(), + Address(obj, index.AsX86().AsCpuRegister(), TIMES_8, data_offset + kX86WordSize)); + } + break; + } + + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + LOG(FATAL) << "Unimplemented register type " << instruction->GetType(); + + case Primitive::kPrimVoid: + LOG(FATAL) << "Unreachable type " << instruction->GetType(); + } +} + +void LocationsBuilderX86::VisitArraySet(HArraySet* instruction) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); + Primitive::Type value_type = instruction->InputAt(2)->GetType(); + if (value_type == Primitive::kPrimNot) { + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, X86CpuLocation(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, X86CpuLocation(calling_convention.GetRegisterAt(1))); + locations->SetInAt(2, X86CpuLocation(calling_convention.GetRegisterAt(2))); + codegen_->MarkNotLeaf(); + } else { + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1))); + if (value_type == Primitive::kPrimBoolean || value_type == Primitive::kPrimByte) { + // Ensure the value is in a byte register. + locations->SetInAt(2, X86CpuLocation(EAX)); + } else { + locations->SetInAt(2, Location::RequiresRegister()); + } + } + + instruction->SetLocations(locations); +} + +void InstructionCodeGeneratorX86::VisitArraySet(HArraySet* instruction) { + LocationSummary* locations = instruction->GetLocations(); + Register obj = locations->InAt(0).AsX86().AsCpuRegister(); + Location index = locations->InAt(1); + Primitive::Type value_type = instruction->InputAt(2)->GetType(); + + switch (value_type) { + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value(); + ByteRegister value = locations->InAt(2).AsX86().AsByteRegister(); + if (index.IsConstant()) { + size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset; + __ movb(Address(obj, offset), value); + } else { + __ movb(Address(obj, index.AsX86().AsCpuRegister(), TIMES_1, data_offset), value); + } + break; + } + + case Primitive::kPrimShort: + case Primitive::kPrimChar: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value(); + Register value = locations->InAt(2).AsX86().AsCpuRegister(); + if (index.IsConstant()) { + size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset; + __ movw(Address(obj, offset), value); + } else { + __ movw(Address(obj, index.AsX86().AsCpuRegister(), TIMES_2, data_offset), value); + } + break; + } + + case Primitive::kPrimInt: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value(); + Register value = locations->InAt(2).AsX86().AsCpuRegister(); + if (index.IsConstant()) { + size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; + __ movl(Address(obj, offset), value); + } else { + __ movl(Address(obj, index.AsX86().AsCpuRegister(), TIMES_4, data_offset), value); + } + break; + } + + case Primitive::kPrimNot: { + DCHECK(!codegen_->IsLeafMethod()); + __ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pAputObject))); + codegen_->RecordPcInfo(instruction->GetDexPc()); + break; + } + + case Primitive::kPrimLong: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value(); + X86ManagedRegister value = locations->InAt(2).AsX86(); + if (index.IsConstant()) { + size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; + __ movl(Address(obj, offset), value.AsRegisterPairLow()); + __ movl(Address(obj, offset + kX86WordSize), value.AsRegisterPairHigh()); + } else { + __ movl(Address(obj, index.AsX86().AsCpuRegister(), TIMES_8, data_offset), + value.AsRegisterPairLow()); + __ movl(Address(obj, index.AsX86().AsCpuRegister(), TIMES_8, data_offset + kX86WordSize), + value.AsRegisterPairHigh()); + } + break; + } + + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + LOG(FATAL) << "Unimplemented register type " << instruction->GetType(); + + case Primitive::kPrimVoid: + LOG(FATAL) << "Unreachable type " << instruction->GetType(); + } +} + +void LocationsBuilderX86::VisitArrayLength(HArrayLength* instruction) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister()); + instruction->SetLocations(locations); +} + +void InstructionCodeGeneratorX86::VisitArrayLength(HArrayLength* instruction) { + LocationSummary* locations = instruction->GetLocations(); + uint32_t offset = mirror::Array::LengthOffset().Uint32Value(); + Register obj = locations->InAt(0).AsX86().AsCpuRegister(); + Register out = locations->Out().AsX86().AsCpuRegister(); + __ movl(out, Address(obj, offset)); +} + +void LocationsBuilderX86::VisitBoundsCheck(HBoundsCheck* instruction) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + // TODO: Have a normalization phase that makes this instruction never used. + locations->SetOut(Location::SameAsFirstInput()); + instruction->SetLocations(locations); +} + +void InstructionCodeGeneratorX86::VisitBoundsCheck(HBoundsCheck* instruction) { + LocationSummary* locations = instruction->GetLocations(); + SlowPathCode* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathX86( + instruction->GetDexPc(), locations->InAt(0), locations->InAt(1)); + codegen_->AddSlowPath(slow_path); + + Register index = locations->InAt(0).AsX86().AsCpuRegister(); + Register length = locations->InAt(1).AsX86().AsCpuRegister(); + + __ cmpl(index, length); + __ j(kAboveEqual, slow_path->GetEntryLabel()); +} + void LocationsBuilderX86::VisitTemporary(HTemporary* temp) { temp->SetLocations(nullptr); } @@ -1178,6 +1515,14 @@ void ParallelMoveResolverX86::EmitMove(size_t index) { MoveMemoryToMemory(destination.GetStackIndex(), source.GetStackIndex()); } + } else if (source.IsConstant()) { + HIntConstant* instruction = source.GetConstant()->AsIntConstant(); + Immediate imm(instruction->AsIntConstant()->GetValue()); + if (destination.IsRegister()) { + __ movl(destination.AsX86().AsCpuRegister(), imm); + } else { + __ movl(Address(ESP, destination.GetStackIndex()), imm); + } } else { LOG(FATAL) << "Unimplemented"; } diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h index d622d2a685..7c502049d8 100644 --- a/compiler/optimizing/code_generator_x86.h +++ b/compiler/optimizing/code_generator_x86.h @@ -90,7 +90,7 @@ class LocationsBuilderX86 : public HGraphVisitor { #define DECLARE_VISIT_INSTRUCTION(name) \ virtual void Visit##name(H##name* instr); - FOR_EACH_INSTRUCTION(DECLARE_VISIT_INSTRUCTION) + FOR_EACH_CONCRETE_INSTRUCTION(DECLARE_VISIT_INSTRUCTION) #undef DECLARE_VISIT_INSTRUCTION @@ -108,7 +108,7 @@ class InstructionCodeGeneratorX86 : public HGraphVisitor { #define DECLARE_VISIT_INSTRUCTION(name) \ virtual void Visit##name(H##name* instr); - FOR_EACH_INSTRUCTION(DECLARE_VISIT_INSTRUCTION) + FOR_EACH_CONCRETE_INSTRUCTION(DECLARE_VISIT_INSTRUCTION) #undef DECLARE_VISIT_INSTRUCTION @@ -126,7 +126,7 @@ class InstructionCodeGeneratorX86 : public HGraphVisitor { class CodeGeneratorX86 : public CodeGenerator { public: explicit CodeGeneratorX86(HGraph* graph); - virtual ~CodeGeneratorX86() { } + virtual ~CodeGeneratorX86() {} virtual void GenerateFrameEntry() OVERRIDE; virtual void GenerateFrameExit() OVERRIDE; @@ -177,12 +177,15 @@ class CodeGeneratorX86 : public CodeGenerator { return InstructionSet::kX86; } - private: // Helper method to move a 32bits value between two locations. void Move32(Location destination, Location source); // Helper method to move a 64bits value between two locations. void Move64(Location destination, Location source); + // Emit a write barrier. + void MarkGCCard(Register temp, Register card, Register object, Register value); + + private: LocationsBuilderX86 location_builder_; InstructionCodeGeneratorX86 instruction_visitor_; ParallelMoveResolverX86 move_resolver_; diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index e3ce5ceb4f..e4259f51b4 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -17,11 +17,13 @@ #include "code_generator_x86_64.h" #include "entrypoints/quick/quick_entrypoints.h" +#include "gc/accounting/card_table.h" #include "mirror/array.h" #include "mirror/art_method.h" #include "mirror/object_reference.h" #include "thread.h" #include "utils/assembler.h" +#include "utils/stack_checks.h" #include "utils/x86_64/assembler_x86_64.h" #include "utils/x86_64/managed_register_x86_64.h" @@ -33,6 +35,32 @@ x86_64::X86_64ManagedRegister Location::AsX86_64() const { namespace x86_64 { +static constexpr bool kExplicitStackOverflowCheck = false; + +// Some x86_64 instructions require a register to be available as temp. +static constexpr Register TMP = R11; + +static constexpr int kNumberOfPushedRegistersAtEntry = 1; +static constexpr int kCurrentMethodStackOffset = 0; + +static Location X86_64CpuLocation(Register reg) { + return Location::RegisterLocation(X86_64ManagedRegister::FromCpuRegister(reg)); +} + +static constexpr Register kRuntimeParameterCoreRegisters[] = { RDI, RSI, RDX }; +static constexpr size_t kRuntimeParameterCoreRegistersLength = + arraysize(kRuntimeParameterCoreRegisters); + +class InvokeRuntimeCallingConvention : public CallingConvention<Register> { + public: + InvokeRuntimeCallingConvention() + : CallingConvention(kRuntimeParameterCoreRegisters, + kRuntimeParameterCoreRegistersLength) {} + + private: + DISALLOW_COPY_AND_ASSIGN(InvokeRuntimeCallingConvention); +}; + #define __ reinterpret_cast<X86_64Assembler*>(codegen->GetAssembler())-> class NullCheckSlowPathX86_64 : public SlowPathCode { @@ -41,7 +69,8 @@ class NullCheckSlowPathX86_64 : public SlowPathCode { virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { __ Bind(GetEntryLabel()); - __ gs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pThrowNullPointer), true)); + __ gs()->call( + Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pThrowNullPointer), true)); codegen->RecordPcInfo(dex_pc_); } @@ -50,6 +79,48 @@ class NullCheckSlowPathX86_64 : public SlowPathCode { DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathX86_64); }; +class StackOverflowCheckSlowPathX86_64 : public SlowPathCode { + public: + StackOverflowCheckSlowPathX86_64() {} + + virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + __ Bind(GetEntryLabel()); + __ addq(CpuRegister(RSP), + Immediate(codegen->GetFrameSize() - kNumberOfPushedRegistersAtEntry * kX86_64WordSize)); + __ gs()->jmp( + Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pThrowStackOverflow), true)); + } + + private: + DISALLOW_COPY_AND_ASSIGN(StackOverflowCheckSlowPathX86_64); +}; + +class BoundsCheckSlowPathX86_64 : public SlowPathCode { + public: + explicit BoundsCheckSlowPathX86_64(uint32_t dex_pc, + Location index_location, + Location length_location) + : dex_pc_(dex_pc), index_location_(index_location), length_location_(length_location) {} + + virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + CodeGeneratorX86_64* x64_codegen = reinterpret_cast<CodeGeneratorX86_64*>(codegen); + __ Bind(GetEntryLabel()); + InvokeRuntimeCallingConvention calling_convention; + x64_codegen->Move(X86_64CpuLocation(calling_convention.GetRegisterAt(0)), index_location_); + x64_codegen->Move(X86_64CpuLocation(calling_convention.GetRegisterAt(1)), length_location_); + __ gs()->call(Address::Absolute( + QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pThrowArrayBounds), true)); + codegen->RecordPcInfo(dex_pc_); + } + + private: + const uint32_t dex_pc_; + const Location index_location_; + const Location length_location_; + + DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathX86_64); +}; + #undef __ #define __ reinterpret_cast<X86_64Assembler*>(GetAssembler())-> @@ -67,12 +138,6 @@ inline Condition X86_64Condition(IfCondition cond) { return kEqual; } -// Some x86_64 instructions require a register to be available as temp. -static constexpr Register TMP = R11; - -static constexpr int kNumberOfPushedRegistersAtEntry = 1; -static constexpr int kCurrentMethodStackOffset = 0; - void CodeGeneratorX86_64::DumpCoreRegister(std::ostream& stream, int reg) const { stream << X86_64ManagedRegister::FromCpuRegister(Register(reg)); } @@ -81,10 +146,6 @@ void CodeGeneratorX86_64::DumpFloatingPointRegister(std::ostream& stream, int re stream << X86_64ManagedRegister::FromXmmRegister(FloatRegister(reg)); } -static Location X86_64CpuLocation(Register reg) { - return Location::RegisterLocation(X86_64ManagedRegister::FromCpuRegister(reg)); -} - CodeGeneratorX86_64::CodeGeneratorX86_64(HGraph* graph) : CodeGenerator(graph, kNumberOfRegIds), location_builder_(graph, this), @@ -95,7 +156,8 @@ size_t CodeGeneratorX86_64::FrameEntrySpillSize() const { return kNumberOfPushedRegistersAtEntry * kX86_64WordSize; } -InstructionCodeGeneratorX86_64::InstructionCodeGeneratorX86_64(HGraph* graph, CodeGeneratorX86_64* codegen) +InstructionCodeGeneratorX86_64::InstructionCodeGeneratorX86_64(HGraph* graph, + CodeGeneratorX86_64* codegen) : HGraphVisitor(graph), assembler_(codegen->GetAssembler()), codegen_(codegen) {} @@ -146,8 +208,28 @@ void CodeGeneratorX86_64::GenerateFrameEntry() { static const int kFakeReturnRegister = 16; core_spill_mask_ |= (1 << kFakeReturnRegister); + bool skip_overflow_check = IsLeafMethod() + && !IsLargeFrame(GetFrameSize(), InstructionSet::kX86_64); + + if (!skip_overflow_check && !kExplicitStackOverflowCheck) { + __ testq(CpuRegister(RAX), Address( + CpuRegister(RSP), -static_cast<int32_t>(GetStackOverflowReservedBytes(kX86_64)))); + RecordPcInfo(0); + } + // The return PC has already been pushed on the stack. - __ subq(CpuRegister(RSP), Immediate(GetFrameSize() - kNumberOfPushedRegistersAtEntry * kX86_64WordSize)); + __ subq(CpuRegister(RSP), + Immediate(GetFrameSize() - kNumberOfPushedRegistersAtEntry * kX86_64WordSize)); + + if (!skip_overflow_check && kExplicitStackOverflowCheck) { + SlowPathCode* slow_path = new (GetGraph()->GetArena()) StackOverflowCheckSlowPathX86_64(); + AddSlowPath(slow_path); + + __ gs()->cmpq(CpuRegister(RSP), + Address::Absolute(Thread::StackEndOffset<kX86_64WordSize>(), true)); + __ j(kLess, slow_path->GetEntryLabel()); + } + __ movl(Address(CpuRegister(RSP), kCurrentMethodStackOffset), CpuRegister(RDI)); } @@ -329,7 +411,14 @@ void InstructionCodeGeneratorX86_64::VisitIf(HIf* if_instr) { } else { Location lhs = condition->GetLocations()->InAt(0); Location rhs = condition->GetLocations()->InAt(1); - __ cmpl(lhs.AsX86_64().AsCpuRegister(), rhs.AsX86_64().AsCpuRegister()); + if (rhs.IsRegister()) { + __ cmpl(lhs.AsX86_64().AsCpuRegister(), rhs.AsX86_64().AsCpuRegister()); + } else if (rhs.IsConstant()) { + __ cmpl(lhs.AsX86_64().AsCpuRegister(), + Immediate(rhs.GetConstant()->AsIntConstant()->GetValue())); + } else { + __ cmpl(lhs.AsX86_64().AsCpuRegister(), Address(CpuRegister(RSP), rhs.GetStackIndex())); + } __ j(X86_64Condition(condition->GetCondition()), codegen_->GetLabelOf(if_instr->IfTrueSuccessor())); } @@ -382,7 +471,7 @@ void InstructionCodeGeneratorX86_64::VisitStoreLocal(HStoreLocal* store) { void LocationsBuilderX86_64::VisitCondition(HCondition* comp) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(comp); locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); + locations->SetInAt(1, Location::Any()); if (comp->NeedsMaterialization()) { locations->SetOut(Location::RequiresRegister()); } @@ -391,8 +480,17 @@ void LocationsBuilderX86_64::VisitCondition(HCondition* comp) { void InstructionCodeGeneratorX86_64::VisitCondition(HCondition* comp) { if (comp->NeedsMaterialization()) { - __ cmpq(comp->GetLocations()->InAt(0).AsX86_64().AsCpuRegister(), - comp->GetLocations()->InAt(1).AsX86_64().AsCpuRegister()); + LocationSummary* locations = comp->GetLocations(); + if (locations->InAt(1).IsRegister()) { + __ cmpq(locations->InAt(0).AsX86_64().AsCpuRegister(), + locations->InAt(1).AsX86_64().AsCpuRegister()); + } else if (locations->InAt(1).IsConstant()) { + __ cmpq(locations->InAt(0).AsX86_64().AsCpuRegister(), + Immediate(locations->InAt(1).GetConstant()->AsIntConstant()->GetValue())); + } else { + __ cmpq(locations->InAt(0).AsX86_64().AsCpuRegister(), + Address(CpuRegister(RSP), locations->InAt(1).GetStackIndex())); + } __ setcc(X86_64Condition(comp->GetCondition()), comp->GetLocations()->Out().AsX86_64().AsCpuRegister()); } @@ -480,25 +578,21 @@ void InstructionCodeGeneratorX86_64::VisitCompare(HCompare* compare) { } void LocationsBuilderX86_64::VisitIntConstant(HIntConstant* constant) { - // TODO: Support constant locations. LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant); - locations->SetOut(Location::RequiresRegister()); + locations->SetOut(Location::ConstantLocation(constant)); constant->SetLocations(locations); } void InstructionCodeGeneratorX86_64::VisitIntConstant(HIntConstant* constant) { - codegen_->Move(constant, constant->GetLocations()->Out(), nullptr); } void LocationsBuilderX86_64::VisitLongConstant(HLongConstant* constant) { - // TODO: Support constant locations. LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant); - locations->SetOut(Location::RequiresRegister()); + locations->SetOut(Location::ConstantLocation(constant)); constant->SetLocations(locations); } void InstructionCodeGeneratorX86_64::VisitLongConstant(HLongConstant* constant) { - codegen_->Move(constant, constant->GetLocations()->Out(), nullptr); } void LocationsBuilderX86_64::VisitReturnVoid(HReturnVoid* ret) { @@ -550,20 +644,6 @@ void InstructionCodeGeneratorX86_64::VisitReturn(HReturn* ret) { __ ret(); } -static constexpr Register kRuntimeParameterCoreRegisters[] = { RDI, RSI, RDX }; -static constexpr size_t kRuntimeParameterCoreRegistersLength = - arraysize(kRuntimeParameterCoreRegisters); - -class InvokeRuntimeCallingConvention : public CallingConvention<Register> { - public: - InvokeRuntimeCallingConvention() - : CallingConvention(kRuntimeParameterCoreRegisters, - kRuntimeParameterCoreRegistersLength) {} - - private: - DISALLOW_COPY_AND_ASSIGN(InvokeRuntimeCallingConvention); -}; - Location InvokeDexCallingConventionVisitor::GetNextLocation(Primitive::Type type) { switch (type) { case Primitive::kPrimBoolean: @@ -606,6 +686,7 @@ Location InvokeDexCallingConventionVisitor::GetNextLocation(Primitive::Type type } void LocationsBuilderX86_64::VisitInvokeStatic(HInvokeStatic* invoke) { + codegen_->MarkNotLeaf(); LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(invoke); locations->AddTemp(X86_64CpuLocation(RDI)); @@ -660,13 +741,19 @@ void InstructionCodeGeneratorX86_64::VisitInvokeStatic(HInvokeStatic* invoke) { // (temp + offset_of_quick_compiled_code)() __ call(Address(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().SizeValue())); + DCHECK(!codegen_->IsLeafMethod()); codegen_->RecordPcInfo(invoke->GetDexPc()); } void LocationsBuilderX86_64::VisitAdd(HAdd* add) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(add); switch (add->GetResultType()) { - case Primitive::kPrimInt: + case Primitive::kPrimInt: { + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::Any()); + locations->SetOut(Location::SameAsFirstInput()); + break; + } case Primitive::kPrimLong: { locations->SetInAt(0, Location::RequiresRegister()); locations->SetInAt(1, Location::RequiresRegister()); @@ -693,8 +780,17 @@ void InstructionCodeGeneratorX86_64::VisitAdd(HAdd* add) { locations->Out().AsX86_64().AsCpuRegister().AsRegister()); switch (add->GetResultType()) { case Primitive::kPrimInt: { - __ addl(locations->InAt(0).AsX86_64().AsCpuRegister(), - locations->InAt(1).AsX86_64().AsCpuRegister()); + if (locations->InAt(1).IsRegister()) { + __ addl(locations->InAt(0).AsX86_64().AsCpuRegister(), + locations->InAt(1).AsX86_64().AsCpuRegister()); + } else if (locations->InAt(1).IsConstant()) { + HConstant* instruction = locations->InAt(1).GetConstant(); + Immediate imm(instruction->AsIntConstant()->GetValue()); + __ addl(locations->InAt(0).AsX86_64().AsCpuRegister(), imm); + } else { + __ addl(locations->InAt(0).AsX86_64().AsCpuRegister(), + Address(CpuRegister(RSP), locations->InAt(1).GetStackIndex())); + } break; } case Primitive::kPrimLong: { @@ -718,7 +814,12 @@ void InstructionCodeGeneratorX86_64::VisitAdd(HAdd* add) { void LocationsBuilderX86_64::VisitSub(HSub* sub) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(sub); switch (sub->GetResultType()) { - case Primitive::kPrimInt: + case Primitive::kPrimInt: { + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::Any()); + locations->SetOut(Location::SameAsFirstInput()); + break; + } case Primitive::kPrimLong: { locations->SetInAt(0, Location::RequiresRegister()); locations->SetInAt(1, Location::RequiresRegister()); @@ -745,8 +846,17 @@ void InstructionCodeGeneratorX86_64::VisitSub(HSub* sub) { locations->Out().AsX86_64().AsCpuRegister().AsRegister()); switch (sub->GetResultType()) { case Primitive::kPrimInt: { - __ subl(locations->InAt(0).AsX86_64().AsCpuRegister(), - locations->InAt(1).AsX86_64().AsCpuRegister()); + if (locations->InAt(1).IsRegister()) { + __ subl(locations->InAt(0).AsX86_64().AsCpuRegister(), + locations->InAt(1).AsX86_64().AsCpuRegister()); + } else if (locations->InAt(1).IsConstant()) { + HConstant* instruction = locations->InAt(1).GetConstant(); + Immediate imm(instruction->AsIntConstant()->GetValue()); + __ subl(locations->InAt(0).AsX86_64().AsCpuRegister(), imm); + } else { + __ subl(locations->InAt(0).AsX86_64().AsCpuRegister(), + Address(CpuRegister(RSP), locations->InAt(1).GetStackIndex())); + } break; } case Primitive::kPrimLong: { @@ -768,6 +878,7 @@ void InstructionCodeGeneratorX86_64::VisitSub(HSub* sub) { } void LocationsBuilderX86_64::VisitNewInstance(HNewInstance* instruction) { + codegen_->MarkNotLeaf(); LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); locations->SetOut(X86_64CpuLocation(RAX)); instruction->SetLocations(locations); @@ -781,6 +892,7 @@ void InstructionCodeGeneratorX86_64::VisitNewInstance(HNewInstance* instruction) __ gs()->call(Address::Absolute( QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pAllocObjectWithAccessCheck), true)); + DCHECK(!codegen_->IsLeafMethod()); codegen_->RecordPcInfo(instruction->GetDexPc()); } @@ -831,6 +943,11 @@ void LocationsBuilderX86_64::VisitInstanceFieldSet(HInstanceFieldSet* instructio LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); locations->SetInAt(0, Location::RequiresRegister()); locations->SetInAt(1, Location::RequiresRegister()); + // Temporary registers for the write barrier. + if (instruction->InputAt(1)->GetType() == Primitive::kPrimNot) { + locations->AddTemp(Location::RequiresRegister()); + locations->AddTemp(Location::RequiresRegister()); + } instruction->SetLocations(locations); } @@ -857,6 +974,11 @@ void InstructionCodeGeneratorX86_64::VisitInstanceFieldSet(HInstanceFieldSet* in case Primitive::kPrimInt: case Primitive::kPrimNot: { __ movl(Address(obj, offset), value); + if (field_type == Primitive::kPrimNot) { + CpuRegister temp = locations->GetTemp(0).AsX86_64().AsCpuRegister(); + CpuRegister card = locations->GetTemp(1).AsX86_64().AsCpuRegister(); + codegen_->MarkGCCard(temp, card, obj, value); + } break; } @@ -954,6 +1076,245 @@ void InstructionCodeGeneratorX86_64::VisitNullCheck(HNullCheck* instruction) { __ j(kEqual, slow_path->GetEntryLabel()); } +void LocationsBuilderX86_64::VisitArrayGet(HArrayGet* instruction) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1))); + locations->SetOut(Location::RequiresRegister()); + instruction->SetLocations(locations); +} + +void InstructionCodeGeneratorX86_64::VisitArrayGet(HArrayGet* instruction) { + LocationSummary* locations = instruction->GetLocations(); + CpuRegister obj = locations->InAt(0).AsX86_64().AsCpuRegister(); + Location index = locations->InAt(1); + + switch (instruction->GetType()) { + case Primitive::kPrimBoolean: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value(); + CpuRegister out = locations->Out().AsX86_64().AsCpuRegister(); + if (index.IsConstant()) { + __ movzxb(out, Address(obj, + (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset)); + } else { + __ movzxb(out, Address(obj, index.AsX86_64().AsCpuRegister(), TIMES_1, data_offset)); + } + break; + } + + case Primitive::kPrimByte: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(int8_t)).Uint32Value(); + CpuRegister out = locations->Out().AsX86_64().AsCpuRegister(); + if (index.IsConstant()) { + __ movsxb(out, Address(obj, + (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset)); + } else { + __ movsxb(out, Address(obj, index.AsX86_64().AsCpuRegister(), TIMES_1, data_offset)); + } + break; + } + + case Primitive::kPrimShort: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(int16_t)).Uint32Value(); + CpuRegister out = locations->Out().AsX86_64().AsCpuRegister(); + if (index.IsConstant()) { + __ movsxw(out, Address(obj, + (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset)); + } else { + __ movsxw(out, Address(obj, index.AsX86_64().AsCpuRegister(), TIMES_2, data_offset)); + } + break; + } + + case Primitive::kPrimChar: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value(); + CpuRegister out = locations->Out().AsX86_64().AsCpuRegister(); + if (index.IsConstant()) { + __ movzxw(out, Address(obj, + (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset)); + } else { + __ movzxw(out, Address(obj, index.AsX86_64().AsCpuRegister(), TIMES_2, data_offset)); + } + break; + } + + case Primitive::kPrimInt: + case Primitive::kPrimNot: { + DCHECK_EQ(sizeof(mirror::HeapReference<mirror::Object>), sizeof(int32_t)); + uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value(); + CpuRegister out = locations->Out().AsX86_64().AsCpuRegister(); + if (index.IsConstant()) { + __ movl(out, Address(obj, + (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset)); + } else { + __ movl(out, Address(obj, index.AsX86_64().AsCpuRegister(), TIMES_4, data_offset)); + } + break; + } + + case Primitive::kPrimLong: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value(); + CpuRegister out = locations->Out().AsX86_64().AsCpuRegister(); + if (index.IsConstant()) { + __ movq(out, Address(obj, + (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset)); + } else { + __ movq(out, Address(obj, index.AsX86_64().AsCpuRegister(), TIMES_8, data_offset)); + } + break; + } + + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + LOG(FATAL) << "Unimplemented register type " << instruction->GetType(); + + case Primitive::kPrimVoid: + LOG(FATAL) << "Unreachable type " << instruction->GetType(); + } +} + +void LocationsBuilderX86_64::VisitArraySet(HArraySet* instruction) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); + Primitive::Type value_type = instruction->InputAt(2)->GetType(); + if (value_type == Primitive::kPrimNot) { + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, X86_64CpuLocation(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, X86_64CpuLocation(calling_convention.GetRegisterAt(1))); + locations->SetInAt(2, X86_64CpuLocation(calling_convention.GetRegisterAt(2))); + codegen_->MarkNotLeaf(); + } else { + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1))); + locations->SetInAt(2, Location::RequiresRegister()); + } + instruction->SetLocations(locations); +} + +void InstructionCodeGeneratorX86_64::VisitArraySet(HArraySet* instruction) { + LocationSummary* locations = instruction->GetLocations(); + CpuRegister obj = locations->InAt(0).AsX86_64().AsCpuRegister(); + Location index = locations->InAt(1); + Primitive::Type value_type = instruction->InputAt(2)->GetType(); + + switch (value_type) { + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value(); + CpuRegister value = locations->InAt(2).AsX86_64().AsCpuRegister(); + if (index.IsConstant()) { + size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset; + __ movb(Address(obj, offset), value); + } else { + __ movb(Address(obj, index.AsX86_64().AsCpuRegister(), TIMES_1, data_offset), value); + } + break; + } + + case Primitive::kPrimShort: + case Primitive::kPrimChar: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value(); + CpuRegister value = locations->InAt(2).AsX86_64().AsCpuRegister(); + if (index.IsConstant()) { + size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset; + __ movw(Address(obj, offset), value); + } else { + __ movw(Address(obj, index.AsX86_64().AsCpuRegister(), TIMES_2, data_offset), value); + } + break; + } + + case Primitive::kPrimInt: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value(); + CpuRegister value = locations->InAt(2).AsX86_64().AsCpuRegister(); + if (index.IsConstant()) { + size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; + __ movl(Address(obj, offset), value); + } else { + __ movl(Address(obj, index.AsX86_64().AsCpuRegister(), TIMES_4, data_offset), value); + } + break; + } + + case Primitive::kPrimNot: { + __ gs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pAputObject), true)); + DCHECK(!codegen_->IsLeafMethod()); + codegen_->RecordPcInfo(instruction->GetDexPc()); + break; + } + + case Primitive::kPrimLong: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value(); + CpuRegister value = locations->InAt(2).AsX86_64().AsCpuRegister(); + if (index.IsConstant()) { + size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; + __ movq(Address(obj, offset), value); + } else { + __ movq(Address(obj, index.AsX86_64().AsCpuRegister(), TIMES_8, data_offset), value); + } + break; + } + + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + LOG(FATAL) << "Unimplemented register type " << instruction->GetType(); + + case Primitive::kPrimVoid: + LOG(FATAL) << "Unreachable type " << instruction->GetType(); + } +} + +void LocationsBuilderX86_64::VisitArrayLength(HArrayLength* instruction) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister()); + instruction->SetLocations(locations); +} + +void InstructionCodeGeneratorX86_64::VisitArrayLength(HArrayLength* instruction) { + LocationSummary* locations = instruction->GetLocations(); + uint32_t offset = mirror::Array::LengthOffset().Uint32Value(); + CpuRegister obj = locations->InAt(0).AsX86_64().AsCpuRegister(); + CpuRegister out = locations->Out().AsX86_64().AsCpuRegister(); + __ movl(out, Address(obj, offset)); +} + +void LocationsBuilderX86_64::VisitBoundsCheck(HBoundsCheck* instruction) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + // TODO: Have a normalization phase that makes this instruction never used. + locations->SetOut(Location::SameAsFirstInput()); + instruction->SetLocations(locations); +} + +void InstructionCodeGeneratorX86_64::VisitBoundsCheck(HBoundsCheck* instruction) { + LocationSummary* locations = instruction->GetLocations(); + SlowPathCode* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathX86_64( + instruction->GetDexPc(), locations->InAt(0), locations->InAt(1)); + codegen_->AddSlowPath(slow_path); + + CpuRegister index = locations->InAt(0).AsX86_64().AsCpuRegister(); + CpuRegister length = locations->InAt(1).AsX86_64().AsCpuRegister(); + + __ cmpl(index, length); + __ j(kAboveEqual, slow_path->GetEntryLabel()); +} + +void CodeGeneratorX86_64::MarkGCCard(CpuRegister temp, + CpuRegister card, + CpuRegister object, + CpuRegister value) { + Label is_null; + __ testl(value, value); + __ j(kEqual, &is_null); + __ gs()->movq(card, Address::Absolute( + Thread::CardTableOffset<kX86_64WordSize>().Int32Value(), true)); + __ movq(temp, object); + __ shrq(temp, Immediate(gc::accounting::CardTable::kCardShift)); + __ movb(Address(temp, card, TIMES_1, 0), card); + __ Bind(&is_null); +} + void LocationsBuilderX86_64::VisitTemporary(HTemporary* temp) { temp->SetLocations(nullptr); } @@ -1008,6 +1369,26 @@ void ParallelMoveResolverX86_64::EmitMove(size_t index) { __ movq(CpuRegister(TMP), Address(CpuRegister(RSP), source.GetStackIndex())); __ movq(Address(CpuRegister(RSP), destination.GetStackIndex()), CpuRegister(TMP)); } + } else if (source.IsConstant()) { + HConstant* constant = source.GetConstant(); + if (constant->IsIntConstant()) { + Immediate imm(constant->AsIntConstant()->GetValue()); + if (destination.IsRegister()) { + __ movl(destination.AsX86_64().AsCpuRegister(), imm); + } else { + __ movl(Address(CpuRegister(RSP), destination.GetStackIndex()), imm); + } + } else if (constant->IsLongConstant()) { + int64_t value = constant->AsLongConstant()->GetValue(); + if (destination.IsRegister()) { + __ movq(destination.AsX86_64().AsCpuRegister(), Immediate(value)); + } else { + __ movq(CpuRegister(TMP), Immediate(value)); + __ movq(Address(CpuRegister(RSP), destination.GetStackIndex()), CpuRegister(TMP)); + } + } else { + LOG(FATAL) << "Unimplemented constant type"; + } } else { LOG(FATAL) << "Unimplemented"; } diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h index 8283dda4a5..44552ea465 100644 --- a/compiler/optimizing/code_generator_x86_64.h +++ b/compiler/optimizing/code_generator_x86_64.h @@ -87,7 +87,7 @@ class LocationsBuilderX86_64 : public HGraphVisitor { #define DECLARE_VISIT_INSTRUCTION(name) \ virtual void Visit##name(H##name* instr); - FOR_EACH_INSTRUCTION(DECLARE_VISIT_INSTRUCTION) + FOR_EACH_CONCRETE_INSTRUCTION(DECLARE_VISIT_INSTRUCTION) #undef DECLARE_VISIT_INSTRUCTION @@ -105,7 +105,7 @@ class InstructionCodeGeneratorX86_64 : public HGraphVisitor { #define DECLARE_VISIT_INSTRUCTION(name) \ virtual void Visit##name(H##name* instr); - FOR_EACH_INSTRUCTION(DECLARE_VISIT_INSTRUCTION) + FOR_EACH_CONCRETE_INSTRUCTION(DECLARE_VISIT_INSTRUCTION) #undef DECLARE_VISIT_INSTRUCTION @@ -176,10 +176,13 @@ class CodeGeneratorX86_64 : public CodeGenerator { return InstructionSet::kX86_64; } - private: + // Emit a write barrier. + void MarkGCCard(CpuRegister temp, CpuRegister card, CpuRegister object, CpuRegister value); + // Helper method to move a value between two locations. void Move(Location destination, Location source); + private: LocationsBuilderX86_64 location_builder_; InstructionCodeGeneratorX86_64 instruction_visitor_; ParallelMoveResolverX86_64 move_resolver_; diff --git a/compiler/optimizing/codegen_test.cc b/compiler/optimizing/codegen_test.cc index 7ec0c84167..d7ac10d164 100644 --- a/compiler/optimizing/codegen_test.cc +++ b/compiler/optimizing/codegen_test.cc @@ -48,10 +48,17 @@ class InternalCodeAllocator : public CodeAllocator { }; #if defined(__i386__) || defined(__arm__) || defined(__x86_64__) -static void Run(const InternalCodeAllocator& allocator, bool has_result, int32_t expected) { +static void Run(const InternalCodeAllocator& allocator, + const CodeGenerator& codegen, + bool has_result, + int32_t expected) { typedef int32_t (*fptr)(); CommonCompilerTest::MakeExecutable(allocator.GetMemory(), allocator.GetSize()); fptr f = reinterpret_cast<fptr>(allocator.GetMemory()); + if (codegen.GetInstructionSet() == kThumb2) { + // For thumb we need the bottom bit set. + f = reinterpret_cast<fptr>(reinterpret_cast<uintptr_t>(f) + 1); + } int32_t result = f(); if (has_result) { CHECK_EQ(result, expected); @@ -69,21 +76,23 @@ static void TestCode(const uint16_t* data, bool has_result = false, int32_t expe InternalCodeAllocator allocator; CodeGenerator* codegen = CodeGenerator::Create(&arena, graph, kX86); - codegen->CompileBaseline(&allocator); + // We avoid doing a stack overflow check that requires the runtime being setup, + // by making sure the compiler knows the methods we are running are leaf methods. + codegen->CompileBaseline(&allocator, true); #if defined(__i386__) - Run(allocator, has_result, expected); + Run(allocator, *codegen, has_result, expected); #endif codegen = CodeGenerator::Create(&arena, graph, kArm); - codegen->CompileBaseline(&allocator); + codegen->CompileBaseline(&allocator, true); #if defined(__arm__) - Run(allocator, has_result, expected); + Run(allocator, *codegen, has_result, expected); #endif codegen = CodeGenerator::Create(&arena, graph, kX86_64); - codegen->CompileBaseline(&allocator); + codegen->CompileBaseline(&allocator, true); #if defined(__x86_64__) - Run(allocator, has_result, expected); + Run(allocator, *codegen, has_result, expected); #endif } diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc index f033e2e22b..f011e85cc0 100644 --- a/compiler/optimizing/graph_visualizer.cc +++ b/compiler/optimizing/graph_visualizer.cc @@ -108,6 +108,10 @@ class HGraphVisualizerPrinter : public HGraphVisitor { } else { codegen_.DumpCoreRegister(output_, location.reg().RegId()); } + } else if (location.IsConstant()) { + output_ << "constant"; + } else if (location.IsInvalid()) { + output_ << "invalid"; } else if (location.IsStackSlot()) { output_ << location.GetStackIndex() << "(sp)"; } else { diff --git a/compiler/optimizing/locations.cc b/compiler/optimizing/locations.cc index 98766d2701..468cfb7923 100644 --- a/compiler/optimizing/locations.cc +++ b/compiler/optimizing/locations.cc @@ -29,4 +29,11 @@ LocationSummary::LocationSummary(HInstruction* instruction) } } + +Location Location::RegisterOrConstant(HInstruction* instruction) { + return instruction->IsConstant() + ? Location::ConstantLocation(instruction->AsConstant()) + : Location::RequiresRegister(); +} + } // namespace art diff --git a/compiler/optimizing/locations.h b/compiler/optimizing/locations.h index 40a39ad80d..aaddb099a6 100644 --- a/compiler/optimizing/locations.h +++ b/compiler/optimizing/locations.h @@ -24,6 +24,7 @@ namespace art { +class HConstant; class HInstruction; /** @@ -34,23 +35,33 @@ class Location : public ValueObject { public: enum Kind { kInvalid = 0, - kStackSlot = 1, // Word size slot. - kDoubleStackSlot = 2, // 64bit stack slot. - kRegister = 3, + kConstant = 1, + kStackSlot = 2, // Word size slot. + kDoubleStackSlot = 3, // 64bit stack slot. + kRegister = 4, // On 32bits architectures, quick can pass a long where the // low bits are in the last parameter register, and the high // bits are in a stack slot. The kQuickParameter kind is for // handling this special case. - kQuickParameter = 4, + kQuickParameter = 5, // Unallocated location represents a location that is not fixed and can be // allocated by a register allocator. Each unallocated location has // a policy that specifies what kind of location is suitable. Payload // contains register allocation policy. - kUnallocated = 5, + kUnallocated = 6, }; Location() : value_(kInvalid) { + // Verify that non-tagged location kinds do not interfere with kConstantTag. + COMPILE_ASSERT((kInvalid & kLocationTagMask) != kConstant, TagError); + COMPILE_ASSERT((kUnallocated & kLocationTagMask) != kConstant, TagError); + COMPILE_ASSERT((kStackSlot & kLocationTagMask) != kConstant, TagError); + COMPILE_ASSERT((kDoubleStackSlot & kLocationTagMask) != kConstant, TagError); + COMPILE_ASSERT((kRegister & kLocationTagMask) != kConstant, TagError); + COMPILE_ASSERT((kConstant & kLocationTagMask) == kConstant, TagError); + COMPILE_ASSERT((kQuickParameter & kLocationTagMask) == kConstant, TagError); + DCHECK(!IsValid()); } @@ -61,6 +72,20 @@ class Location : public ValueObject { return *this; } + bool IsConstant() const { + return (value_ & kLocationTagMask) == kConstant; + } + + static Location ConstantLocation(HConstant* constant) { + DCHECK(constant != nullptr); + return Location(kConstant | reinterpret_cast<uword>(constant)); + } + + HConstant* GetConstant() const { + DCHECK(IsConstant()); + return reinterpret_cast<HConstant*>(value_ & ~kLocationTagMask); + } + bool IsValid() const { return value_ != kInvalid; } @@ -69,11 +94,6 @@ class Location : public ValueObject { return !IsValid(); } - bool IsConstant() const { - // TODO: support constants. - return false; - } - // Empty location. Used if there the location should be ignored. static Location NoLocation() { return Location(); @@ -162,12 +182,13 @@ class Location : public ValueObject { const char* DebugString() const { switch (GetKind()) { - case kInvalid: return "?"; + case kInvalid: return "I"; case kRegister: return "R"; case kStackSlot: return "S"; case kDoubleStackSlot: return "DS"; case kQuickParameter: return "Q"; case kUnallocated: return "U"; + case kConstant: return "C"; } return "?"; } @@ -196,6 +217,8 @@ class Location : public ValueObject { return UnallocatedLocation(kRequiresRegister); } + static Location RegisterOrConstant(HInstruction* instruction); + // The location of the first input to the instruction will be // used to replace this unallocated location. static Location SameAsFirstInput() { @@ -215,6 +238,7 @@ class Location : public ValueObject { // Number of bits required to encode Kind value. static constexpr uint32_t kBitsForKind = 4; static constexpr uint32_t kBitsForPayload = kWordSize * kBitsPerByte - kBitsForKind; + static constexpr uword kLocationTagMask = 0x3; explicit Location(uword value) : value_(value) {} diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index e87b044cc9..bb699e47c3 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -408,7 +408,7 @@ class HBasicBlock : public ArenaObject { DISALLOW_COPY_AND_ASSIGN(HBasicBlock); }; -#define FOR_EACH_INSTRUCTION(M) \ +#define FOR_EACH_CONCRETE_INSTRUCTION(M) \ M(Add) \ M(Condition) \ M(Equal) \ @@ -437,9 +437,16 @@ class HBasicBlock : public ArenaObject { M(Compare) \ M(InstanceFieldGet) \ M(InstanceFieldSet) \ + M(ArrayGet) \ + M(ArraySet) \ + M(ArrayLength) \ + M(BoundsCheck) \ M(NullCheck) \ M(Temporary) \ +#define FOR_EACH_INSTRUCTION(M) \ + FOR_EACH_CONCRETE_INSTRUCTION(M) \ + M(Constant) #define FORWARD_DECLARATION(type) class H##type; FOR_EACH_INSTRUCTION(FORWARD_DECLARATION) @@ -494,6 +501,7 @@ class HInstruction : public ArenaObject { void SetBlock(HBasicBlock* block) { block_ = block; } bool IsInBlock() const { return block_ != nullptr; } bool IsInLoop() const { return block_->IsInLoop(); } + bool IsLoopHeaderPhi() { return IsPhi() && block_->IsLoopHeader(); } virtual size_t InputCount() const = 0; virtual HInstruction* InputAt(size_t i) const = 0; @@ -1078,11 +1086,21 @@ class HStoreLocal : public HTemplateInstruction<2> { DISALLOW_COPY_AND_ASSIGN(HStoreLocal); }; +class HConstant : public HExpression<0> { + public: + explicit HConstant(Primitive::Type type) : HExpression(type) {} + + DECLARE_INSTRUCTION(Constant); + + private: + DISALLOW_COPY_AND_ASSIGN(HConstant); +}; + // Constants of the type int. Those can be from Dex instructions, or // synthesized (for example with the if-eqz instruction). -class HIntConstant : public HExpression<0> { +class HIntConstant : public HConstant { public: - explicit HIntConstant(int32_t value) : HExpression(Primitive::kPrimInt), value_(value) {} + explicit HIntConstant(int32_t value) : HConstant(Primitive::kPrimInt), value_(value) {} int32_t GetValue() const { return value_; } @@ -1094,14 +1112,12 @@ class HIntConstant : public HExpression<0> { DISALLOW_COPY_AND_ASSIGN(HIntConstant); }; -class HLongConstant : public HExpression<0> { +class HLongConstant : public HConstant { public: - explicit HLongConstant(int64_t value) : HExpression(Primitive::kPrimLong), value_(value) {} + explicit HLongConstant(int64_t value) : HConstant(Primitive::kPrimLong), value_(value) {} int64_t GetValue() const { return value_; } - virtual Primitive::Type GetType() const { return Primitive::kPrimLong; } - DECLARE_INSTRUCTION(LongConstant); private: @@ -1278,13 +1294,12 @@ class HPhi : public HInstruction { DECLARE_INSTRUCTION(Phi); - protected: + private: GrowableArray<HInstruction*> inputs_; const uint32_t reg_number_; Primitive::Type type_; bool is_live_; - private: DISALLOW_COPY_AND_ASSIGN(HPhi); }; @@ -1357,6 +1372,80 @@ class HInstanceFieldSet : public HTemplateInstruction<2> { DISALLOW_COPY_AND_ASSIGN(HInstanceFieldSet); }; +class HArrayGet : public HExpression<2> { + public: + HArrayGet(HInstruction* array, HInstruction* index, Primitive::Type type) + : HExpression(type) { + SetRawInputAt(0, array); + SetRawInputAt(1, index); + } + + DECLARE_INSTRUCTION(ArrayGet); + + private: + DISALLOW_COPY_AND_ASSIGN(HArrayGet); +}; + +class HArraySet : public HTemplateInstruction<3> { + public: + HArraySet(HInstruction* array, + HInstruction* index, + HInstruction* value, + uint32_t dex_pc) : dex_pc_(dex_pc) { + SetRawInputAt(0, array); + SetRawInputAt(1, index); + SetRawInputAt(2, value); + } + + virtual bool NeedsEnvironment() const { + // We currently always call a runtime method to catch array store + // exceptions. + return InputAt(2)->GetType() == Primitive::kPrimNot; + } + + uint32_t GetDexPc() const { return dex_pc_; } + + DECLARE_INSTRUCTION(ArraySet); + + private: + const uint32_t dex_pc_; + + DISALLOW_COPY_AND_ASSIGN(HArraySet); +}; + +class HArrayLength : public HExpression<1> { + public: + explicit HArrayLength(HInstruction* array) : HExpression(Primitive::kPrimInt) { + SetRawInputAt(0, array); + } + + DECLARE_INSTRUCTION(ArrayLength); + + private: + DISALLOW_COPY_AND_ASSIGN(HArrayLength); +}; + +class HBoundsCheck : public HExpression<2> { + public: + HBoundsCheck(HInstruction* index, HInstruction* length, uint32_t dex_pc) + : HExpression(index->GetType()), dex_pc_(dex_pc) { + DCHECK(index->GetType() == Primitive::kPrimInt); + SetRawInputAt(0, index); + SetRawInputAt(1, length); + } + + virtual bool NeedsEnvironment() const { return true; } + + uint32_t GetDexPc() const { return dex_pc_; } + + DECLARE_INSTRUCTION(BoundsCheck); + + private: + const uint32_t dex_pc_; + + DISALLOW_COPY_AND_ASSIGN(HBoundsCheck); +}; + /** * Some DEX instructions are folded into multiple HInstructions that need * to stay live until the last HInstruction. This class diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index b621e510f3..8a5077b962 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -79,13 +79,14 @@ CompiledMethod* OptimizingCompiler::TryCompile(const DexFile::CodeItem* code_ite jobject class_loader, const DexFile& dex_file) const { InstructionSet instruction_set = GetCompilerDriver()->GetInstructionSet(); - // The optimizing compiler currently does not have a Thumb2 assembler. - if (instruction_set == kThumb2) { - instruction_set = kArm; + // Always use the thumb2 assembler: some runtime functionality (like implicit stack + // overflow checks) assume thumb2. + if (instruction_set == kArm) { + instruction_set = kThumb2; } // Do not attempt to compile on architectures we do not support. - if (instruction_set != kX86 && instruction_set != kX86_64 && instruction_set != kArm) { + if (instruction_set != kX86 && instruction_set != kX86_64 && instruction_set != kThumb2) { return nullptr; } diff --git a/compiler/optimizing/register_allocator.cc b/compiler/optimizing/register_allocator.cc index 68130dd5fc..bd3a7d9767 100644 --- a/compiler/optimizing/register_allocator.cc +++ b/compiler/optimizing/register_allocator.cc @@ -153,13 +153,13 @@ void RegisterAllocator::AllocateRegistersInternal() { if (current->HasRegister()) { DCHECK(instruction->IsParameterValue()); inactive_.Add(current); - } else if (current->HasSpillSlot()) { - DCHECK(instruction->IsParameterValue()); + } else if (current->HasSpillSlot() || instruction->IsConstant()) { // Split before first register use. size_t first_register_use = current->FirstRegisterUse(); if (first_register_use != kNoLifetime) { LiveInterval* split = Split(current, first_register_use - 1); - // The new interval may start at a late + // Don't add direclty to `unhandled_`, it needs to be sorted and the start + // of this new interval might be after intervals already in the list. AddToUnhandled(split); } else { // Nothing to do, we won't allocate a register for this value. @@ -579,6 +579,11 @@ void RegisterAllocator::AllocateSpillSlotFor(LiveInterval* interval) { return; } + if (defined_by->IsConstant()) { + // Constants don't need a spill slot. + return; + } + LiveInterval* last_sibling = interval; while (last_sibling->GetNextSibling() != nullptr) { last_sibling = last_sibling->GetNextSibling(); @@ -644,11 +649,16 @@ static Location ConvertToLocation(LiveInterval* interval) { if (interval->HasRegister()) { return Location::RegisterLocation(ManagedRegister(interval->GetRegister())); } else { - DCHECK(interval->GetParent()->HasSpillSlot()); - if (NeedTwoSpillSlot(interval->GetType())) { - return Location::DoubleStackSlot(interval->GetParent()->GetSpillSlot()); + HInstruction* defined_by = interval->GetParent()->GetDefinedBy(); + if (defined_by->IsConstant()) { + return defined_by->GetLocations()->Out(); } else { - return Location::StackSlot(interval->GetParent()->GetSpillSlot()); + DCHECK(interval->GetParent()->HasSpillSlot()); + if (NeedTwoSpillSlot(interval->GetType())) { + return Location::DoubleStackSlot(interval->GetParent()->GetSpillSlot()); + } else { + return Location::StackSlot(interval->GetParent()->GetSpillSlot()); + } } } } diff --git a/compiler/optimizing/register_allocator.h b/compiler/optimizing/register_allocator.h index e35ff56c75..be1c7ec7c6 100644 --- a/compiler/optimizing/register_allocator.h +++ b/compiler/optimizing/register_allocator.h @@ -66,7 +66,10 @@ class RegisterAllocator { static bool CanAllocateRegistersFor(const HGraph& graph, InstructionSet instruction_set); static bool Supports(InstructionSet instruction_set) { - return instruction_set == kX86 || instruction_set == kArm || instruction_set == kX86_64; + return instruction_set == kX86 + || instruction_set == kArm + || instruction_set == kX86_64 + || instruction_set == kThumb2; } size_t GetNumberOfSpillSlots() const { diff --git a/compiler/optimizing/register_allocator_test.cc b/compiler/optimizing/register_allocator_test.cc index a7283ab329..bafe577f90 100644 --- a/compiler/optimizing/register_allocator_test.cc +++ b/compiler/optimizing/register_allocator_test.cc @@ -22,6 +22,7 @@ #include "optimizing_unit_test.h" #include "register_allocator.h" #include "ssa_liveness_analysis.h" +#include "ssa_phi_elimination.h" #include "utils/arena_allocator.h" #include "gtest/gtest.h" @@ -356,4 +357,38 @@ TEST(RegisterAllocatorTest, FirstRegisterUse) { ASSERT_EQ(new_interval->FirstRegisterUse(), last_add->GetLifetimePosition() + 1); } +TEST(RegisterAllocatorTest, DeadPhi) { + /* Test for a dead loop phi taking as back-edge input a phi that also has + * this loop phi as input. Walking backwards in SsaDeadPhiElimination + * does not solve the problem because the loop phi will be visited last. + * + * Test the following snippet: + * int a = 0 + * do { + * if (true) { + * a = 2; + * } + * } while (true); + */ + + const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( + Instruction::CONST_4 | 0 | 0, + Instruction::CONST_4 | 1 << 8 | 0, + Instruction::IF_NE | 1 << 8 | 1 << 12, 3, + Instruction::CONST_4 | 2 << 12 | 0 << 8, + Instruction::GOTO | 0xFD00, + Instruction::RETURN_VOID); + + ArenaPool pool; + ArenaAllocator allocator(&pool); + HGraph* graph = BuildSSAGraph(data, &allocator); + SsaDeadPhiElimination(graph).Run(); + CodeGenerator* codegen = CodeGenerator::Create(&allocator, graph, kX86); + SsaLivenessAnalysis liveness(*graph, codegen); + liveness.Analyze(); + RegisterAllocator register_allocator(&allocator, codegen, liveness); + register_allocator.AllocateRegisters(); + ASSERT_TRUE(register_allocator.Validate(false)); +} + } // namespace art diff --git a/compiler/optimizing/ssa_phi_elimination.cc b/compiler/optimizing/ssa_phi_elimination.cc index 13fa03f9a3..a079954166 100644 --- a/compiler/optimizing/ssa_phi_elimination.cc +++ b/compiler/optimizing/ssa_phi_elimination.cc @@ -53,8 +53,9 @@ void SsaDeadPhiElimination::Run() { } } - // Remove phis that are not live. Visit in post order to ensure - // we only remove phis with no users (dead phis might use dead phis). + // Remove phis that are not live. Visit in post order so that phis + // that are not inputs of loop phis can be removed when they have + // no users left (dead phis might use dead phis). for (HPostOrderIterator it(*graph_); !it.Done(); it.Advance()) { HBasicBlock* block = it.Current(); HInstruction* current = block->GetFirstPhi(); @@ -62,6 +63,17 @@ void SsaDeadPhiElimination::Run() { while (current != nullptr) { next = current->GetNext(); if (current->AsPhi()->IsDead()) { + if (current->HasUses()) { + for (HUseIterator<HInstruction> it(current->GetUses()); !it.Done(); it.Advance()) { + HUseListNode<HInstruction>* user_node = it.Current(); + HInstruction* user = user_node->GetUser(); + DCHECK(user->IsLoopHeaderPhi()); + DCHECK(user->AsPhi()->IsDead()); + // Just put itself as an input. The phi will be removed in this loop anyway. + user->SetRawInputAt(user_node->GetIndex(), user); + current->RemoveUser(user, user_node->GetIndex()); + } + } block->RemovePhi(current->AsPhi()); } current = next; diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h new file mode 100644 index 0000000000..5e1329e63c --- /dev/null +++ b/compiler/optimizing/stack_map_stream.h @@ -0,0 +1,211 @@ +/* + * 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. + */ + +#ifndef ART_COMPILER_OPTIMIZING_STACK_MAP_STREAM_H_ +#define ART_COMPILER_OPTIMIZING_STACK_MAP_STREAM_H_ + +#include "base/bit_vector.h" +#include "memory_region.h" +#include "stack_map.h" +#include "utils/allocation.h" +#include "utils/growable_array.h" + +namespace art { + +/** + * Collects and builds a CodeInfo for a method. + */ +template<typename T> +class StackMapStream : public ValueObject { + public: + explicit StackMapStream(ArenaAllocator* allocator) + : stack_maps_(allocator, 10), + dex_register_maps_(allocator, 10 * 4), + inline_infos_(allocator, 2), + stack_mask_max_(-1), + number_of_stack_maps_with_inline_info_(0) {} + + // Compute bytes needed to encode a mask with the given maximum element. + static uint32_t StackMaskEncodingSize(int max_element) { + int number_of_bits = max_element + 1; // Need room for max element too. + return RoundUp(number_of_bits, kBitsPerByte) / kBitsPerByte; + } + + // See runtime/stack_map.h to know what these fields contain. + struct StackMapEntry { + uint32_t dex_pc; + T native_pc; + uint32_t register_mask; + BitVector* sp_mask; + uint32_t num_dex_registers; + uint8_t inlining_depth; + size_t dex_register_maps_start_index; + size_t inline_infos_start_index; + }; + + struct DexRegisterEntry { + DexRegisterMap::LocationKind kind; + int32_t value; + }; + + struct InlineInfoEntry { + uint32_t method_index; + }; + + void AddStackMapEntry(uint32_t dex_pc, + T native_pc, + uint32_t register_mask, + BitVector* sp_mask, + uint32_t num_dex_registers, + uint8_t inlining_depth) { + StackMapEntry entry; + entry.dex_pc = dex_pc; + entry.native_pc = native_pc; + entry.register_mask = register_mask; + entry.sp_mask = sp_mask; + entry.num_dex_registers = num_dex_registers; + entry.inlining_depth = inlining_depth; + entry.dex_register_maps_start_index = dex_register_maps_.Size(); + entry.inline_infos_start_index = inline_infos_.Size(); + stack_maps_.Add(entry); + + stack_mask_max_ = std::max(stack_mask_max_, sp_mask->GetHighestBitSet()); + if (inlining_depth > 0) { + number_of_stack_maps_with_inline_info_++; + } + } + + void AddDexRegisterEntry(DexRegisterMap::LocationKind kind, int32_t value) { + DexRegisterEntry entry; + entry.kind = kind; + entry.value = value; + dex_register_maps_.Add(entry); + } + + void AddInlineInfoEntry(uint32_t method_index) { + InlineInfoEntry entry; + entry.method_index = method_index; + inline_infos_.Add(entry); + } + + size_t ComputeNeededSize() const { + return CodeInfo<T>::kFixedSize + + ComputeStackMapSize() + + ComputeDexRegisterMapSize() + + ComputeInlineInfoSize(); + } + + size_t ComputeStackMapSize() const { + return stack_maps_.Size() * (StackMap<T>::kFixedSize + StackMaskEncodingSize(stack_mask_max_)); + } + + size_t ComputeDexRegisterMapSize() const { + // We currently encode all dex register information per stack map. + return stack_maps_.Size() * DexRegisterMap::kFixedSize + // For each dex register entry. + + (dex_register_maps_.Size() * DexRegisterMap::SingleEntrySize()); + } + + size_t ComputeInlineInfoSize() const { + return inline_infos_.Size() * InlineInfo::SingleEntrySize() + // For encoding the depth. + + (number_of_stack_maps_with_inline_info_ * InlineInfo::kFixedSize); + } + + size_t ComputeInlineInfoStart() const { + return ComputeDexRegisterMapStart() + ComputeDexRegisterMapSize(); + } + + size_t ComputeDexRegisterMapStart() const { + return CodeInfo<T>::kFixedSize + ComputeStackMapSize(); + } + + void FillIn(MemoryRegion region) { + CodeInfo<T> code_info(region); + + size_t stack_mask_size = StackMaskEncodingSize(stack_mask_max_); + uint8_t* memory_start = region.start(); + + MemoryRegion dex_register_maps_region = region.Subregion( + ComputeDexRegisterMapStart(), + ComputeDexRegisterMapSize()); + + MemoryRegion inline_infos_region = region.Subregion( + ComputeInlineInfoStart(), + ComputeInlineInfoSize()); + + code_info.SetNumberOfStackMaps(stack_maps_.Size()); + code_info.SetStackMaskSize(stack_mask_size); + + uintptr_t next_dex_register_map_offset = 0; + uintptr_t next_inline_info_offset = 0; + for (size_t i = 0, e = stack_maps_.Size(); i < e; ++i) { + StackMap<T> stack_map = code_info.GetStackMapAt(i); + StackMapEntry entry = stack_maps_.Get(i); + + stack_map.SetDexPc(entry.dex_pc); + stack_map.SetNativePc(entry.native_pc); + stack_map.SetRegisterMask(entry.register_mask); + stack_map.SetStackMask(*entry.sp_mask); + + // Set the register map. + MemoryRegion region = dex_register_maps_region.Subregion( + next_dex_register_map_offset, + DexRegisterMap::kFixedSize + entry.num_dex_registers * DexRegisterMap::SingleEntrySize()); + next_dex_register_map_offset += region.size(); + DexRegisterMap dex_register_map(region); + stack_map.SetDexRegisterMapOffset(region.start() - memory_start); + + for (size_t i = 0; i < entry.num_dex_registers; ++i) { + DexRegisterEntry register_entry = + dex_register_maps_.Get(i + entry.dex_register_maps_start_index); + dex_register_map.SetRegisterInfo(i, register_entry.kind, register_entry.value); + } + + // Set the inlining info. + if (entry.inlining_depth != 0) { + MemoryRegion region = inline_infos_region.Subregion( + next_inline_info_offset, + InlineInfo::kFixedSize + entry.inlining_depth * InlineInfo::SingleEntrySize()); + next_inline_info_offset += region.size(); + InlineInfo inline_info(region); + + stack_map.SetInlineDescriptorOffset(region.start() - memory_start); + + inline_info.SetDepth(entry.inlining_depth); + for (size_t i = 0; i < entry.inlining_depth; ++i) { + InlineInfoEntry inline_entry = inline_infos_.Get(i + entry.inline_infos_start_index); + inline_info.SetMethodReferenceIndexAtDepth(i, inline_entry.method_index); + } + } else { + stack_map.SetInlineDescriptorOffset(InlineInfo::kNoInlineInfo); + } + } + } + + private: + GrowableArray<StackMapEntry> stack_maps_; + GrowableArray<DexRegisterEntry> dex_register_maps_; + GrowableArray<InlineInfoEntry> inline_infos_; + int stack_mask_max_; + size_t number_of_stack_maps_with_inline_info_; + + DISALLOW_COPY_AND_ASSIGN(StackMapStream); +}; + +} // namespace art + +#endif // ART_COMPILER_OPTIMIZING_STACK_MAP_STREAM_H_ diff --git a/compiler/optimizing/stack_map_test.cc b/compiler/optimizing/stack_map_test.cc new file mode 100644 index 0000000000..a70259e7b9 --- /dev/null +++ b/compiler/optimizing/stack_map_test.cc @@ -0,0 +1,134 @@ +/* + * 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 "stack_map.h" +#include "stack_map_stream.h" +#include "utils/arena_bit_vector.h" + +#include "gtest/gtest.h" + +namespace art { + +bool SameBits(MemoryRegion region, const BitVector& bit_vector) { + for (size_t i = 0; i < region.size_in_bits(); ++i) { + if (region.LoadBit(i) != bit_vector.IsBitSet(i)) { + return false; + } + } + return true; +} + +TEST(StackMapTest, Test1) { + ArenaPool pool; + ArenaAllocator arena(&pool); + StackMapStream<size_t> stream(&arena); + + ArenaBitVector sp_mask(&arena, 0, false); + stream.AddStackMapEntry(0, 64, 0x3, &sp_mask, 2, 0); + stream.AddDexRegisterEntry(DexRegisterMap::kInStack, 0); + stream.AddDexRegisterEntry(DexRegisterMap::kConstant, -2); + + size_t size = stream.ComputeNeededSize(); + void* memory = arena.Alloc(size, kArenaAllocMisc); + MemoryRegion region(memory, size); + stream.FillIn(region); + + CodeInfo<size_t> code_info(region); + ASSERT_EQ(0u, code_info.GetStackMaskSize()); + ASSERT_EQ(1u, code_info.GetNumberOfStackMaps()); + + StackMap<size_t> stack_map = code_info.GetStackMapAt(0); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0))); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePc(64))); + ASSERT_EQ(0u, stack_map.GetDexPc()); + ASSERT_EQ(64u, stack_map.GetNativePc()); + ASSERT_EQ(0x3u, stack_map.GetRegisterMask()); + ASSERT_FALSE(stack_map.HasInlineInfo()); + + MemoryRegion stack_mask = stack_map.GetStackMask(); + ASSERT_TRUE(SameBits(stack_mask, sp_mask)); + + DexRegisterMap dex_registers = code_info.GetDexRegisterMapOf(stack_map, 2); + ASSERT_EQ(DexRegisterMap::kInStack, dex_registers.GetLocationKind(0)); + ASSERT_EQ(DexRegisterMap::kConstant, dex_registers.GetLocationKind(1)); + ASSERT_EQ(0, dex_registers.GetValue(0)); + ASSERT_EQ(-2, dex_registers.GetValue(1)); +} + +TEST(StackMapTest, Test2) { + ArenaPool pool; + ArenaAllocator arena(&pool); + StackMapStream<size_t> stream(&arena); + + ArenaBitVector sp_mask1(&arena, 0, true); + sp_mask1.SetBit(2); + sp_mask1.SetBit(4); + stream.AddStackMapEntry(0, 64, 0x3, &sp_mask1, 2, 2); + stream.AddDexRegisterEntry(DexRegisterMap::kInStack, 0); + stream.AddDexRegisterEntry(DexRegisterMap::kConstant, -2); + stream.AddInlineInfoEntry(42); + stream.AddInlineInfoEntry(82); + + ArenaBitVector sp_mask2(&arena, 0, true); + sp_mask2.SetBit(3); + sp_mask1.SetBit(8); + stream.AddStackMapEntry(1, 128, 0xFF, &sp_mask2, 1, 0); + stream.AddDexRegisterEntry(DexRegisterMap::kInRegister, 0); + + size_t size = stream.ComputeNeededSize(); + void* memory = arena.Alloc(size, kArenaAllocMisc); + MemoryRegion region(memory, size); + stream.FillIn(region); + + CodeInfo<size_t> code_info(region); + ASSERT_EQ(1u, code_info.GetStackMaskSize()); + ASSERT_EQ(2u, code_info.GetNumberOfStackMaps()); + + StackMap<size_t> stack_map = code_info.GetStackMapAt(0); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0))); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePc(64))); + ASSERT_EQ(0u, stack_map.GetDexPc()); + ASSERT_EQ(64u, stack_map.GetNativePc()); + ASSERT_EQ(0x3u, stack_map.GetRegisterMask()); + + MemoryRegion stack_mask = stack_map.GetStackMask(); + ASSERT_TRUE(SameBits(stack_mask, sp_mask1)); + + DexRegisterMap dex_registers = code_info.GetDexRegisterMapOf(stack_map, 2); + ASSERT_EQ(DexRegisterMap::kInStack, dex_registers.GetLocationKind(0)); + ASSERT_EQ(DexRegisterMap::kConstant, dex_registers.GetLocationKind(1)); + ASSERT_EQ(0, dex_registers.GetValue(0)); + ASSERT_EQ(-2, dex_registers.GetValue(1)); + + InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map); + ASSERT_EQ(2u, inline_info.GetDepth()); + ASSERT_EQ(42u, inline_info.GetMethodReferenceIndexAtDepth(0)); + ASSERT_EQ(82u, inline_info.GetMethodReferenceIndexAtDepth(1)); + + stack_map = code_info.GetStackMapAt(1); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(1u))); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePc(128u))); + ASSERT_EQ(1u, stack_map.GetDexPc()); + ASSERT_EQ(128u, stack_map.GetNativePc()); + ASSERT_EQ(0xFFu, stack_map.GetRegisterMask()); + + stack_mask = stack_map.GetStackMask(); + ASSERT_TRUE(SameBits(stack_mask, sp_mask2)); + + ASSERT_FALSE(stack_map.HasInlineInfo()); +} + +} // namespace art |