diff options
Diffstat (limited to 'compiler/optimizing')
44 files changed, 4107 insertions, 722 deletions
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc index 5bcc65b03b..79cbd0ee21 100644 --- a/compiler/optimizing/builder.cc +++ b/compiler/optimizing/builder.cc @@ -171,6 +171,7 @@ HGraph* HGraphBuilder::BuildGraph(const DexFile::CodeItem& code_item) { const uint16_t* code_ptr = code_item.insns_; const uint16_t* code_end = code_item.insns_ + code_item.insns_size_in_code_units_; + code_start_ = code_ptr; // Setup the graph with the entry block and exit block. graph_ = new (arena_) HGraph(arena_); @@ -267,6 +268,13 @@ HBasicBlock* HGraphBuilder::FindBlockStartingAt(int32_t index) const { } template<typename T> +void HGraphBuilder::Unop_12x(const Instruction& instruction, Primitive::Type type) { + HInstruction* first = LoadLocal(instruction.VRegB(), type); + current_block_->AddInstruction(new (arena_) T(type, first)); + UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction()); +} + +template<typename T> void HGraphBuilder::Binop_23x(const Instruction& instruction, Primitive::Type type) { HInstruction* first = LoadLocal(instruction.VRegB(), type); HInstruction* second = LoadLocal(instruction.VRegC(), type); @@ -409,6 +417,7 @@ bool HGraphBuilder::BuildInvoke(const Instruction& instruction, DCHECK_EQ(argument_index, number_of_arguments); current_block_->AddInstruction(invoke); + latest_result_ = invoke; return true; } @@ -496,6 +505,62 @@ void HGraphBuilder::BuildArrayAccess(const Instruction& instruction, } } +void HGraphBuilder::BuildFilledNewArray(uint32_t dex_offset, + uint32_t type_index, + uint32_t number_of_vreg_arguments, + bool is_range, + uint32_t* args, + uint32_t register_index) { + HInstruction* length = GetIntConstant(number_of_vreg_arguments); + HInstruction* object = new (arena_) HNewArray(length, dex_offset, type_index); + current_block_->AddInstruction(object); + + const char* descriptor = dex_file_->StringByTypeIdx(type_index); + DCHECK_EQ(descriptor[0], '[') << descriptor; + char primitive = descriptor[1]; + DCHECK(primitive == 'I' + || primitive == 'L' + || primitive == '[') << descriptor; + bool is_reference_array = (primitive == 'L') || (primitive == '['); + Primitive::Type type = is_reference_array ? Primitive::kPrimNot : Primitive::kPrimInt; + + Temporaries temps(graph_, 1); + temps.Add(object); + for (size_t i = 0; i < number_of_vreg_arguments; ++i) { + HInstruction* value = LoadLocal(is_range ? register_index + i : args[i], type); + HInstruction* index = GetIntConstant(i); + current_block_->AddInstruction( + new (arena_) HArraySet(object, index, value, type, dex_offset)); + } + latest_result_ = object; +} + +template <typename T> +void HGraphBuilder::BuildFillArrayData(HInstruction* object, + const T* data, + uint32_t element_count, + Primitive::Type anticipated_type, + uint32_t dex_offset) { + for (uint32_t i = 0; i < element_count; ++i) { + HInstruction* index = GetIntConstant(i); + HInstruction* value = GetIntConstant(data[i]); + current_block_->AddInstruction(new (arena_) HArraySet( + object, index, value, anticipated_type, dex_offset)); + } +} + +void HGraphBuilder::BuildFillWideArrayData(HInstruction* object, + const uint64_t* data, + uint32_t element_count, + uint32_t dex_offset) { + for (uint32_t i = 0; i < element_count; ++i) { + HInstruction* index = GetIntConstant(i); + HInstruction* value = GetLongConstant(data[i]); + current_block_->AddInstruction(new (arena_) HArraySet( + object, index, value, Primitive::kPrimLong, dex_offset)); + } +} + void HGraphBuilder::PotentiallyAddSuspendCheck(int32_t target_offset, uint32_t dex_offset) { if (target_offset <= 0) { // Unconditionnally add a suspend check to backward branches. We can remove @@ -678,6 +743,16 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32 break; } + case Instruction::NEG_INT: { + Unop_12x<HNeg>(instruction, Primitive::kPrimInt); + break; + } + + case Instruction::NOT_INT: { + Unop_12x<HNot>(instruction, Primitive::kPrimInt); + break; + } + case Instruction::ADD_INT: { Binop_23x<HAdd>(instruction, Primitive::kPrimInt); break; @@ -708,11 +783,41 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32 break; } + case Instruction::SUB_FLOAT: { + Binop_23x<HSub>(instruction, Primitive::kPrimFloat); + break; + } + + case Instruction::SUB_DOUBLE: { + Binop_23x<HSub>(instruction, Primitive::kPrimDouble); + break; + } + case Instruction::ADD_INT_2ADDR: { Binop_12x<HAdd>(instruction, Primitive::kPrimInt); break; } + case Instruction::MUL_INT: { + Binop_23x<HMul>(instruction, Primitive::kPrimInt); + break; + } + + case Instruction::MUL_LONG: { + Binop_23x<HMul>(instruction, Primitive::kPrimLong); + break; + } + + case Instruction::MUL_FLOAT: { + Binop_23x<HMul>(instruction, Primitive::kPrimFloat); + break; + } + + case Instruction::MUL_DOUBLE: { + Binop_23x<HMul>(instruction, Primitive::kPrimDouble); + break; + } + case Instruction::ADD_LONG_2ADDR: { Binop_12x<HAdd>(instruction, Primitive::kPrimLong); break; @@ -738,6 +843,36 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32 break; } + case Instruction::SUB_FLOAT_2ADDR: { + Binop_12x<HSub>(instruction, Primitive::kPrimFloat); + break; + } + + case Instruction::SUB_DOUBLE_2ADDR: { + Binop_12x<HSub>(instruction, Primitive::kPrimDouble); + break; + } + + case Instruction::MUL_INT_2ADDR: { + Binop_12x<HMul>(instruction, Primitive::kPrimInt); + break; + } + + case Instruction::MUL_LONG_2ADDR: { + Binop_12x<HMul>(instruction, Primitive::kPrimLong); + break; + } + + case Instruction::MUL_FLOAT_2ADDR: { + Binop_12x<HMul>(instruction, Primitive::kPrimFloat); + break; + } + + case Instruction::MUL_DOUBLE_2ADDR: { + Binop_12x<HMul>(instruction, Primitive::kPrimDouble); + break; + } + case Instruction::ADD_INT_LIT16: { Binop_22s<HAdd>(instruction, false); break; @@ -748,6 +883,11 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32 break; } + case Instruction::MUL_INT_LIT16: { + Binop_22s<HMul>(instruction, false); + break; + } + case Instruction::ADD_INT_LIT8: { Binop_22b<HAdd>(instruction, false); break; @@ -758,6 +898,11 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32 break; } + case Instruction::MUL_INT_LIT8: { + Binop_22b<HMul>(instruction, false); + break; + } + case Instruction::NEW_INSTANCE: { current_block_->AddInstruction( new (arena_) HNewInstance(dex_offset, instruction.VRegB_21c())); @@ -765,10 +910,88 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32 break; } + case Instruction::NEW_ARRAY: { + HInstruction* length = LoadLocal(instruction.VRegB_22c(), Primitive::kPrimInt); + current_block_->AddInstruction( + new (arena_) HNewArray(length, dex_offset, instruction.VRegC_22c())); + UpdateLocal(instruction.VRegA_22c(), current_block_->GetLastInstruction()); + break; + } + + case Instruction::FILLED_NEW_ARRAY: { + uint32_t number_of_vreg_arguments = instruction.VRegA_35c(); + uint32_t type_index = instruction.VRegB_35c(); + uint32_t args[5]; + instruction.GetVarArgs(args); + BuildFilledNewArray(dex_offset, type_index, number_of_vreg_arguments, false, args, 0); + break; + } + + case Instruction::FILLED_NEW_ARRAY_RANGE: { + uint32_t number_of_vreg_arguments = instruction.VRegA_3rc(); + uint32_t type_index = instruction.VRegB_3rc(); + uint32_t register_index = instruction.VRegC_3rc(); + BuildFilledNewArray( + dex_offset, type_index, number_of_vreg_arguments, true, nullptr, register_index); + break; + } + + case Instruction::FILL_ARRAY_DATA: { + Temporaries temps(graph_, 1); + HInstruction* array = LoadLocal(instruction.VRegA_31t(), Primitive::kPrimNot); + HNullCheck* null_check = new (arena_) HNullCheck(array, dex_offset); + current_block_->AddInstruction(null_check); + temps.Add(null_check); + + HInstruction* length = new (arena_) HArrayLength(null_check); + current_block_->AddInstruction(length); + + int32_t payload_offset = instruction.VRegB_31t() + dex_offset; + const Instruction::ArrayDataPayload* payload = + reinterpret_cast<const Instruction::ArrayDataPayload*>(code_start_ + payload_offset); + const uint8_t* data = payload->data; + uint32_t element_count = payload->element_count; + + // Implementation of this DEX instruction seems to be that the bounds check is + // done before doing any stores. + HInstruction* last_index = GetIntConstant(payload->element_count - 1); + current_block_->AddInstruction(new (arena_) HBoundsCheck(last_index, length, dex_offset)); + + switch (payload->element_width) { + case 1: + BuildFillArrayData(null_check, data, element_count, Primitive::kPrimByte, dex_offset); + break; + case 2: + BuildFillArrayData(null_check, + reinterpret_cast<const uint16_t*>(data), + element_count, + Primitive::kPrimShort, + dex_offset); + break; + case 4: + BuildFillArrayData(null_check, + reinterpret_cast<const uint32_t*>(data), + element_count, + Primitive::kPrimInt, + dex_offset); + break; + case 8: + BuildFillWideArrayData(null_check, + reinterpret_cast<const uint64_t*>(data), + element_count, + dex_offset); + break; + default: + LOG(FATAL) << "Unknown element width for " << payload->element_width; + } + break; + } + case Instruction::MOVE_RESULT: case Instruction::MOVE_RESULT_WIDE: case Instruction::MOVE_RESULT_OBJECT: - UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction()); + UpdateLocal(instruction.VRegA(), latest_result_); + latest_result_ = nullptr; break; case Instruction::CMP_LONG: { diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h index e68cdb0b1d..c5e02db88c 100644 --- a/compiler/optimizing/builder.h +++ b/compiler/optimizing/builder.h @@ -48,7 +48,9 @@ class HGraphBuilder : public ValueObject { dex_file_(dex_file), dex_compilation_unit_(dex_compilation_unit), compiler_driver_(driver), - return_type_(Primitive::GetType(dex_compilation_unit_->GetShorty()[0])) {} + return_type_(Primitive::GetType(dex_compilation_unit_->GetShorty()[0])), + code_start_(nullptr), + latest_result_(nullptr) {} // Only for unit testing. HGraphBuilder(ArenaAllocator* arena, Primitive::Type return_type = Primitive::kPrimInt) @@ -64,7 +66,9 @@ class HGraphBuilder : public ValueObject { dex_file_(nullptr), dex_compilation_unit_(nullptr), compiler_driver_(nullptr), - return_type_(return_type) {} + return_type_(return_type), + code_start_(nullptr), + latest_result_(nullptr) {} HGraph* BuildGraph(const DexFile::CodeItem& code); @@ -95,6 +99,9 @@ class HGraphBuilder : public ValueObject { bool InitializeParameters(uint16_t number_of_parameters); template<typename T> + void Unop_12x(const Instruction& instruction, Primitive::Type type); + + template<typename T> void Binop_23x(const Instruction& instruction, Primitive::Type type); template<typename T> @@ -126,6 +133,31 @@ class HGraphBuilder : public ValueObject { uint32_t* args, uint32_t register_index); + // Builds a new array node and the instructions that fill it. + void BuildFilledNewArray(uint32_t dex_offset, + uint32_t type_index, + uint32_t number_of_vreg_arguments, + bool is_range, + uint32_t* args, + uint32_t register_index); + + // Fills the given object with data as specified in the fill-array-data + // instruction. Currently only used for non-reference and non-floating point + // arrays. + template <typename T> + void BuildFillArrayData(HInstruction* object, + const T* data, + uint32_t element_count, + Primitive::Type anticipated_type, + uint32_t dex_offset); + + // Fills the given object with data as specified in the fill-array-data + // instruction. The data must be for long and double arrays. + void BuildFillWideArrayData(HInstruction* object, + const uint64_t* data, + uint32_t element_count, + uint32_t dex_offset); + ArenaAllocator* const arena_; // A list of the size of the dex code holding block information for @@ -148,6 +180,14 @@ class HGraphBuilder : public ValueObject { CompilerDriver* const compiler_driver_; const Primitive::Type return_type_; + // The pointer in the dex file where the instructions of the code item + // being currently compiled start. + const uint16_t* code_start_; + + // The last invoke or fill-new-array being built. Only to be + // used by move-result instructions. + HInstruction* latest_result_; + DISALLOW_COPY_AND_ASSIGN(HGraphBuilder); }; diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index 29dbd8b33d..c4286a401b 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -17,6 +17,7 @@ #include "code_generator.h" #include "code_generator_arm.h" +#include "code_generator_arm64.h" #include "code_generator_x86.h" #include "code_generator_x86_64.h" #include "compiled_method.h" @@ -36,7 +37,7 @@ 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()); + Initialize(); DCHECK_EQ(frame_size_, kUninitializedFrameSize); if (!is_leaf) { @@ -54,7 +55,7 @@ void CodeGenerator::CompileBaseline(CodeAllocator* allocator, bool is_leaf) { HGraphVisitor* instruction_visitor = GetInstructionVisitor(); for (size_t i = 0, e = blocks.Size(); i < e; ++i) { HBasicBlock* block = blocks.Get(i); - Bind(GetLabelOf(block)); + Bind(block); for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) { HInstruction* current = it.Current(); current->Accept(location_builder); @@ -76,13 +77,13 @@ void CodeGenerator::CompileOptimized(CodeAllocator* allocator) { const GrowableArray<HBasicBlock*>& blocks = GetGraph()->GetBlocks(); DCHECK(blocks.Get(0) == GetGraph()->GetEntryBlock()); DCHECK(GoesToNextBlock(GetGraph()->GetEntryBlock(), blocks.Get(1))); - block_labels_.SetSize(blocks.Size()); + Initialize(); GenerateFrameEntry(); HGraphVisitor* instruction_visitor = GetInstructionVisitor(); for (size_t i = 0, e = blocks.Size(); i < e; ++i) { HBasicBlock* block = blocks.Get(i); - Bind(GetLabelOf(block)); + Bind(block); for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) { HInstruction* current = it.Current(); current->Accept(instruction_visitor); @@ -273,10 +274,6 @@ bool CodeGenerator::GoesToNextBlock(HBasicBlock* current, HBasicBlock* next) con return current->GetBlockId() + 1 == next->GetBlockId(); } -Label* CodeGenerator::GetLabelOf(HBasicBlock* block) const { - return block_labels_.GetRawStorage() + block->GetBlockId(); -} - CodeGenerator* CodeGenerator::Create(ArenaAllocator* allocator, HGraph* graph, InstructionSet instruction_set) { @@ -285,6 +282,9 @@ CodeGenerator* CodeGenerator::Create(ArenaAllocator* allocator, case kThumb2: { return new (allocator) arm::CodeGeneratorARM(graph); } + case kArm64: { + return new (allocator) arm64::CodeGeneratorARM64(graph); + } case kMips: return nullptr; case kX86: { @@ -477,8 +477,7 @@ void CodeGenerator::RecordPcInfo(HInstruction* instruction, uint32_t dex_pc) { case Location::kRegister : { int id = location.reg(); stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kInRegister, id); - if (current->GetType() == Primitive::kPrimDouble - || current->GetType() == Primitive::kPrimLong) { + if (current->GetType() == Primitive::kPrimLong) { stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kInRegister, id); ++i; DCHECK_LT(i, environment_size); @@ -486,52 +485,55 @@ void CodeGenerator::RecordPcInfo(HInstruction* instruction, uint32_t dex_pc) { break; } + case Location::kFpuRegister : { + int id = location.reg(); + stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kInFpuRegister, id); + if (current->GetType() == Primitive::kPrimDouble) { + stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kInFpuRegister, id); + ++i; + DCHECK_LT(i, environment_size); + } + break; + } + default: LOG(FATAL) << "Unexpected kind " << location.GetKind(); } } } -size_t CodeGenerator::GetStackOffsetOfSavedRegister(size_t index) { - return first_register_slot_in_slow_path_ + index * GetWordSize(); -} - void CodeGenerator::SaveLiveRegisters(LocationSummary* locations) { RegisterSet* register_set = locations->GetLiveRegisters(); - uint32_t count = 0; + size_t stack_offset = first_register_slot_in_slow_path_; for (size_t i = 0, e = GetNumberOfCoreRegisters(); i < e; ++i) { if (register_set->ContainsCoreRegister(i)) { - size_t stack_offset = GetStackOffsetOfSavedRegister(count); - ++count; - SaveCoreRegister(Location::StackSlot(stack_offset), i); // If the register holds an object, update the stack mask. if (locations->RegisterContainsObject(i)) { locations->SetStackBit(stack_offset / kVRegSize); } + stack_offset += SaveCoreRegister(stack_offset, i); } } for (size_t i = 0, e = GetNumberOfFloatingPointRegisters(); i < e; ++i) { if (register_set->ContainsFloatingPointRegister(i)) { - LOG(FATAL) << "Unimplemented"; + stack_offset += SaveFloatingPointRegister(stack_offset, i); } } } void CodeGenerator::RestoreLiveRegisters(LocationSummary* locations) { RegisterSet* register_set = locations->GetLiveRegisters(); - uint32_t count = 0; + size_t stack_offset = first_register_slot_in_slow_path_; for (size_t i = 0, e = GetNumberOfCoreRegisters(); i < e; ++i) { if (register_set->ContainsCoreRegister(i)) { - size_t stack_offset = GetStackOffsetOfSavedRegister(count); - ++count; - RestoreCoreRegister(Location::StackSlot(stack_offset), i); + stack_offset += RestoreCoreRegister(stack_offset, i); } } for (size_t i = 0, e = GetNumberOfFloatingPointRegisters(); i < e; ++i) { if (register_set->ContainsFloatingPointRegister(i)) { - LOG(FATAL) << "Unimplemented"; + stack_offset += RestoreFloatingPointRegister(stack_offset, i); } } } diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index 4eba791723..220d745561 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -24,13 +24,13 @@ #include "memory_region.h" #include "nodes.h" #include "stack_map_stream.h" -#include "utils/assembler.h" namespace art { static size_t constexpr kVRegSize = 4; static size_t constexpr kUninitializedFrameSize = 0; +class Assembler; class CodeGenerator; class DexCompilationUnit; class SrcMap; @@ -53,18 +53,12 @@ struct PcInfo { class SlowPathCode : public ArenaObject { public: - SlowPathCode() : entry_label_(), exit_label_() {} + SlowPathCode() {} virtual ~SlowPathCode() {} - Label* GetEntryLabel() { return &entry_label_; } - Label* GetExitLabel() { return &exit_label_; } - virtual void EmitNativeCode(CodeGenerator* codegen) = 0; private: - Label entry_label_; - Label exit_label_; - DISALLOW_COPY_AND_ASSIGN(SlowPathCode); }; @@ -80,7 +74,6 @@ class CodeGenerator : public ArenaObject { HGraph* GetGraph() const { return graph_; } - Label* GetLabelOf(HBasicBlock* block) const; bool GoesToNextBlock(HBasicBlock* current, HBasicBlock* next) const; size_t GetStackSlotOfParameter(HParameterValue* parameter) const { @@ -90,9 +83,10 @@ class CodeGenerator : public ArenaObject { + parameter->GetIndex() * kVRegSize; } + virtual void Initialize() = 0; virtual void GenerateFrameEntry() = 0; virtual void GenerateFrameExit() = 0; - virtual void Bind(Label* label) = 0; + virtual void Bind(HBasicBlock* block) = 0; virtual void Move(HInstruction* instruction, Location location, HInstruction* move_for) = 0; virtual HGraphVisitor* GetLocationBuilder() = 0; virtual HGraphVisitor* GetInstructionVisitor() = 0; @@ -116,8 +110,18 @@ class CodeGenerator : public ArenaObject { virtual void DumpCoreRegister(std::ostream& stream, int reg) const = 0; virtual void DumpFloatingPointRegister(std::ostream& stream, int reg) const = 0; virtual InstructionSet GetInstructionSet() const = 0; - virtual void SaveCoreRegister(Location stack_location, uint32_t reg_id) = 0; - virtual void RestoreCoreRegister(Location stack_location, uint32_t reg_id) = 0; + // Saves the register in the stack. Returns the size taken on stack. + virtual size_t SaveCoreRegister(size_t stack_index, uint32_t reg_id) = 0; + // Restores the register from the stack. Returns the size taken on stack. + virtual size_t RestoreCoreRegister(size_t stack_index, uint32_t reg_id) = 0; + virtual size_t SaveFloatingPointRegister(size_t stack_index, uint32_t reg_id) { + LOG(FATAL) << "Unimplemented"; + return 0u; + } + virtual size_t RestoreFloatingPointRegister(size_t stack_index, uint32_t reg_id) { + LOG(FATAL) << "Unimplemented"; + return 0u; + } void RecordPcInfo(HInstruction* instruction, uint32_t dex_pc); @@ -151,6 +155,7 @@ class CodeGenerator : public ArenaObject { void ClearSpillSlotsFromLoopPhisInStackMap(HSuspendCheck* suspend_check) const; bool* GetBlockedCoreRegisters() const { return blocked_core_registers_; } + bool* GetBlockedFloatingPointRegisters() const { return blocked_fpu_registers_; } protected: CodeGenerator(HGraph* graph, @@ -167,7 +172,6 @@ class CodeGenerator : public ArenaObject { number_of_fpu_registers_(number_of_fpu_registers), number_of_register_pairs_(number_of_register_pairs), graph_(graph), - block_labels_(graph->GetArena(), 0), pc_infos_(graph->GetArena(), 32), slow_paths_(graph->GetArena(), 8), is_leaf_(true), @@ -205,8 +209,6 @@ class CodeGenerator : public ArenaObject { HGraph* const graph_; - // Labels for each block that will be compiled. - GrowableArray<Label> block_labels_; GrowableArray<PcInfo> pc_infos_; GrowableArray<SlowPathCode*> slow_paths_; diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index 2be5c90ed6..7b00d2f523 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -60,7 +60,21 @@ class InvokeRuntimeCallingConvention : public CallingConvention<Register, DRegis #define __ reinterpret_cast<ArmAssembler*>(codegen->GetAssembler())-> -class NullCheckSlowPathARM : public SlowPathCode { +class SlowPathCodeARM : public SlowPathCode { + public: + SlowPathCodeARM() : entry_label_(), exit_label_() {} + + Label* GetEntryLabel() { return &entry_label_; } + Label* GetExitLabel() { return &exit_label_; } + + private: + Label entry_label_; + Label exit_label_; + + DISALLOW_COPY_AND_ASSIGN(SlowPathCodeARM); +}; + +class NullCheckSlowPathARM : public SlowPathCodeARM { public: explicit NullCheckSlowPathARM(HNullCheck* instruction) : instruction_(instruction) {} @@ -77,7 +91,7 @@ class NullCheckSlowPathARM : public SlowPathCode { DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathARM); }; -class StackOverflowCheckSlowPathARM : public SlowPathCode { +class StackOverflowCheckSlowPathARM : public SlowPathCodeARM { public: StackOverflowCheckSlowPathARM() {} @@ -91,12 +105,13 @@ class StackOverflowCheckSlowPathARM : public SlowPathCode { DISALLOW_COPY_AND_ASSIGN(StackOverflowCheckSlowPathARM); }; -class SuspendCheckSlowPathARM : public SlowPathCode { +class SuspendCheckSlowPathARM : public SlowPathCodeARM { public: explicit SuspendCheckSlowPathARM(HSuspendCheck* instruction, HBasicBlock* successor) : instruction_(instruction), successor_(successor) {} virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); __ Bind(GetEntryLabel()); codegen->SaveLiveRegisters(instruction_->GetLocations()); int32_t offset = QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pTestSuspend).Int32Value(); @@ -107,7 +122,7 @@ class SuspendCheckSlowPathARM : public SlowPathCode { if (successor_ == nullptr) { __ b(GetReturnLabel()); } else { - __ b(codegen->GetLabelOf(successor_)); + __ b(arm_codegen->GetLabelOf(successor_)); } } @@ -127,7 +142,7 @@ class SuspendCheckSlowPathARM : public SlowPathCode { DISALLOW_COPY_AND_ASSIGN(SuspendCheckSlowPathARM); }; -class BoundsCheckSlowPathARM : public SlowPathCode { +class BoundsCheckSlowPathARM : public SlowPathCodeARM { public: BoundsCheckSlowPathARM(HBoundsCheck* instruction, Location index_location, @@ -137,7 +152,7 @@ class BoundsCheckSlowPathARM : public SlowPathCode { length_location_(length_location) {} virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { - CodeGeneratorARM* arm_codegen = reinterpret_cast<CodeGeneratorARM*>(codegen); + CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); __ Bind(GetEntryLabel()); InvokeRuntimeCallingConvention calling_convention; arm_codegen->Move32(Location::RegisterLocation(calling_convention.GetRegisterAt(0)), index_location_); @@ -195,16 +210,19 @@ void CodeGeneratorARM::DumpFloatingPointRegister(std::ostream& stream, int reg) stream << ArmManagedRegister::FromDRegister(DRegister(reg)); } -void CodeGeneratorARM::SaveCoreRegister(Location stack_location, uint32_t reg_id) { - __ StoreToOffset(kStoreWord, static_cast<Register>(reg_id), SP, stack_location.GetStackIndex()); +size_t CodeGeneratorARM::SaveCoreRegister(size_t stack_index, uint32_t reg_id) { + __ StoreToOffset(kStoreWord, static_cast<Register>(reg_id), SP, stack_index); + return kArmWordSize; } -void CodeGeneratorARM::RestoreCoreRegister(Location stack_location, uint32_t reg_id) { - __ LoadFromOffset(kLoadWord, static_cast<Register>(reg_id), SP, stack_location.GetStackIndex()); +size_t CodeGeneratorARM::RestoreCoreRegister(size_t stack_index, uint32_t reg_id) { + __ LoadFromOffset(kLoadWord, static_cast<Register>(reg_id), SP, stack_index); + return kArmWordSize; } CodeGeneratorARM::CodeGeneratorARM(HGraph* graph) : CodeGenerator(graph, kNumberOfCoreRegisters, kNumberOfDRegisters, kNumberOfRegisterPairs), + block_labels_(graph->GetArena(), 0), location_builder_(graph, this), instruction_visitor_(graph, this), move_resolver_(graph->GetArena(), this), @@ -220,19 +238,12 @@ Location CodeGeneratorARM::AllocateFreeRegister(Primitive::Type type) const { size_t reg = FindFreeEntry(blocked_register_pairs_, kNumberOfRegisterPairs); ArmManagedRegister pair = ArmManagedRegister::FromRegisterPair(static_cast<RegisterPair>(reg)); + DCHECK(!blocked_core_registers_[pair.AsRegisterPairLow()]); + DCHECK(!blocked_core_registers_[pair.AsRegisterPairHigh()]); + blocked_core_registers_[pair.AsRegisterPairLow()] = true; blocked_core_registers_[pair.AsRegisterPairHigh()] = true; - // Block all other register pairs that share a register with `pair`. - for (int i = 0; i < kNumberOfRegisterPairs; i++) { - ArmManagedRegister current = - ArmManagedRegister::FromRegisterPair(static_cast<RegisterPair>(i)); - if (current.AsRegisterPairLow() == pair.AsRegisterPairLow() - || current.AsRegisterPairLow() == pair.AsRegisterPairHigh() - || current.AsRegisterPairHigh() == pair.AsRegisterPairLow() - || current.AsRegisterPairHigh() == pair.AsRegisterPairHigh()) { - blocked_register_pairs_[i] = true; - } - } + UpdateBlockedPairRegisters(); return Location::RegisterPairLocation(pair.AsRegisterPairLow(), pair.AsRegisterPairHigh()); } @@ -278,7 +289,6 @@ void CodeGeneratorARM::SetupBlockedRegisters() const { // Reserve R4 for suspend check. blocked_core_registers_[R4] = true; - blocked_register_pairs_[R4_R5] = true; // Reserve thread register. blocked_core_registers_[TR] = true; @@ -302,6 +312,19 @@ void CodeGeneratorARM::SetupBlockedRegisters() const { blocked_fpu_registers_[D13] = true; blocked_fpu_registers_[D14] = true; blocked_fpu_registers_[D15] = true; + + UpdateBlockedPairRegisters(); +} + +void CodeGeneratorARM::UpdateBlockedPairRegisters() const { + for (int i = 0; i < kNumberOfRegisterPairs; i++) { + ArmManagedRegister current = + ArmManagedRegister::FromRegisterPair(static_cast<RegisterPair>(i)); + if (blocked_core_registers_[current.AsRegisterPairLow()] + || blocked_core_registers_[current.AsRegisterPairHigh()]) { + blocked_register_pairs_[i] = true; + } + } } InstructionCodeGeneratorARM::InstructionCodeGeneratorARM(HGraph* graph, CodeGeneratorARM* codegen) @@ -313,7 +336,7 @@ void CodeGeneratorARM::GenerateFrameEntry() { bool skip_overflow_check = IsLeafMethod() && !FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kArm); if (!skip_overflow_check) { if (kExplicitStackOverflowCheck) { - SlowPathCode* slow_path = new (GetGraph()->GetArena()) StackOverflowCheckSlowPathARM(); + SlowPathCodeARM* slow_path = new (GetGraph()->GetArena()) StackOverflowCheckSlowPathARM(); AddSlowPath(slow_path); __ LoadFromOffset(kLoadWord, IP, TR, Thread::StackEndOffset<kArmWordSize>().Int32Value()); @@ -339,8 +362,8 @@ void CodeGeneratorARM::GenerateFrameExit() { __ PopList(1 << PC | 1 << R6 | 1 << R7); } -void CodeGeneratorARM::Bind(Label* label) { - __ Bind(label); +void CodeGeneratorARM::Bind(HBasicBlock* block) { + __ Bind(GetLabelOf(block)); } Location CodeGeneratorARM::GetStackLocation(HLoadLocal* load) const { @@ -527,7 +550,7 @@ void CodeGeneratorARM::Move(HInstruction* instruction, Location location, HInstr return; } - if (instruction->AsIntConstant() != nullptr) { + if (instruction->IsIntConstant()) { int32_t value = instruction->AsIntConstant()->GetValue(); if (location.IsRegister()) { __ LoadImmediate(location.As<Register>(), value); @@ -536,7 +559,7 @@ void CodeGeneratorARM::Move(HInstruction* instruction, Location location, HInstr __ LoadImmediate(IP, value); __ StoreToOffset(kStoreWord, IP, SP, location.GetStackIndex()); } - } else if (instruction->AsLongConstant() != nullptr) { + } else if (instruction->IsLongConstant()) { int64_t value = instruction->AsLongConstant()->GetValue(); if (location.IsRegisterPair()) { __ LoadImmediate(location.AsRegisterPairLow<Register>(), Low32Bits(value)); @@ -548,7 +571,7 @@ void CodeGeneratorARM::Move(HInstruction* instruction, Location location, HInstr __ LoadImmediate(IP, High32Bits(value)); __ StoreToOffset(kStoreWord, IP, SP, location.GetHighStackIndex(kArmWordSize)); } - } else if (instruction->AsLoadLocal() != nullptr) { + } else if (instruction->IsLoadLocal()) { uint32_t stack_slot = GetStackSlot(instruction->AsLoadLocal()->GetLocal()); switch (instruction->GetType()) { case Primitive::kPrimBoolean: @@ -635,42 +658,57 @@ void LocationsBuilderARM::VisitIf(HIf* if_instr) { new (GetGraph()->GetArena()) LocationSummary(if_instr, LocationSummary::kNoCall); HInstruction* cond = if_instr->InputAt(0); if (!cond->IsCondition() || cond->AsCondition()->NeedsMaterialization()) { - locations->SetInAt(0, Location::RequiresRegister(), Location::kDiesAtEntry); + locations->SetInAt(0, Location::RequiresRegister()); } } void InstructionCodeGeneratorARM::VisitIf(HIf* if_instr) { HInstruction* cond = if_instr->InputAt(0); - if (!cond->IsCondition() || cond->AsCondition()->NeedsMaterialization()) { - // Condition has been materialized, compare the output to 0 - DCHECK(if_instr->GetLocations()->InAt(0).IsRegister()); - __ cmp(if_instr->GetLocations()->InAt(0).As<Register>(), - ShifterOperand(0)); - __ b(codegen_->GetLabelOf(if_instr->IfTrueSuccessor()), NE); + if (cond->IsIntConstant()) { + // Constant condition, statically compared against 1. + int32_t cond_value = cond->AsIntConstant()->GetValue(); + if (cond_value == 1) { + if (!codegen_->GoesToNextBlock(if_instr->GetBlock(), + if_instr->IfTrueSuccessor())) { + __ b(codegen_->GetLabelOf(if_instr->IfTrueSuccessor())); + } + return; + } else { + DCHECK_EQ(cond_value, 0); + } } else { - // Condition has not been materialized, use its inputs as the comparison and its - // condition as the branch condition. - LocationSummary* locations = cond->GetLocations(); - if (locations->InAt(1).IsRegister()) { - __ cmp(locations->InAt(0).As<Register>(), - ShifterOperand(locations->InAt(1).As<Register>())); + if (!cond->IsCondition() || cond->AsCondition()->NeedsMaterialization()) { + // Condition has been materialized, compare the output to 0 + DCHECK(if_instr->GetLocations()->InAt(0).IsRegister()); + __ cmp(if_instr->GetLocations()->InAt(0).As<Register>(), + ShifterOperand(0)); + __ b(codegen_->GetLabelOf(if_instr->IfTrueSuccessor()), NE); } else { - DCHECK(locations->InAt(1).IsConstant()); - int32_t value = locations->InAt(1).GetConstant()->AsIntConstant()->GetValue(); - ShifterOperand operand; - if (ShifterOperand::CanHoldArm(value, &operand)) { - __ cmp(locations->InAt(0).As<Register>(), ShifterOperand(value)); + // Condition has not been materialized, use its inputs as the + // comparison and its condition as the branch condition. + LocationSummary* locations = cond->GetLocations(); + if (locations->InAt(1).IsRegister()) { + __ cmp(locations->InAt(0).As<Register>(), + ShifterOperand(locations->InAt(1).As<Register>())); } else { - Register temp = IP; - __ LoadImmediate(temp, value); - __ cmp(locations->InAt(0).As<Register>(), ShifterOperand(temp)); + DCHECK(locations->InAt(1).IsConstant()); + int32_t value = + locations->InAt(1).GetConstant()->AsIntConstant()->GetValue(); + ShifterOperand operand; + if (ShifterOperand::CanHoldArm(value, &operand)) { + __ cmp(locations->InAt(0).As<Register>(), ShifterOperand(value)); + } else { + Register temp = IP; + __ LoadImmediate(temp, value); + __ cmp(locations->InAt(0).As<Register>(), ShifterOperand(temp)); + } } + __ b(codegen_->GetLabelOf(if_instr->IfTrueSuccessor()), + ARMCondition(cond->AsCondition()->GetCondition())); } - __ b(codegen_->GetLabelOf(if_instr->IfTrueSuccessor()), - ARMCondition(cond->AsCondition()->GetCondition())); } - - if (!codegen_->GoesToNextBlock(if_instr->GetBlock(), if_instr->IfFalseSuccessor())) { + if (!codegen_->GoesToNextBlock(if_instr->GetBlock(), + if_instr->IfFalseSuccessor())) { __ b(codegen_->GetLabelOf(if_instr->IfFalseSuccessor())); } } @@ -679,10 +717,10 @@ void InstructionCodeGeneratorARM::VisitIf(HIf* if_instr) { void LocationsBuilderARM::VisitCondition(HCondition* comp) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(comp, LocationSummary::kNoCall); - locations->SetInAt(0, Location::RequiresRegister(), Location::kDiesAtEntry); - locations->SetInAt(1, Location::RegisterOrConstant(comp->InputAt(1)), Location::kDiesAtEntry); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RegisterOrConstant(comp->InputAt(1))); if (comp->NeedsMaterialization()) { - locations->SetOut(Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); } } @@ -810,6 +848,7 @@ void LocationsBuilderARM::VisitIntConstant(HIntConstant* constant) { } void InstructionCodeGeneratorARM::VisitIntConstant(HIntConstant* constant) { + // Will be generated at use site. } void LocationsBuilderARM::VisitLongConstant(HLongConstant* constant) { @@ -822,6 +861,26 @@ void InstructionCodeGeneratorARM::VisitLongConstant(HLongConstant* constant) { // Will be generated at use site. } +void LocationsBuilderARM::VisitFloatConstant(HFloatConstant* constant) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall); + locations->SetOut(Location::ConstantLocation(constant)); +} + +void InstructionCodeGeneratorARM::VisitFloatConstant(HFloatConstant* constant) { + // Will be generated at use site. +} + +void LocationsBuilderARM::VisitDoubleConstant(HDoubleConstant* constant) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall); + locations->SetOut(Location::ConstantLocation(constant)); +} + +void InstructionCodeGeneratorARM::VisitDoubleConstant(HDoubleConstant* constant) { + // Will be generated at use site. +} + void LocationsBuilderARM::VisitReturnVoid(HReturnVoid* ret) { ret->SetLocations(nullptr); } @@ -979,16 +1038,57 @@ void InstructionCodeGeneratorARM::VisitInvokeVirtual(HInvokeVirtual* invoke) { codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); } +void LocationsBuilderARM::VisitNeg(HNeg* neg) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(neg, LocationSummary::kNoCall); + switch (neg->GetResultType()) { + case Primitive::kPrimInt: + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister()); + break; + + case Primitive::kPrimLong: + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + LOG(FATAL) << "Not yet implemented neg type " << neg->GetResultType(); + break; + + default: + LOG(FATAL) << "Unexpected neg type " << neg->GetResultType(); + } +} + +void InstructionCodeGeneratorARM::VisitNeg(HNeg* neg) { + LocationSummary* locations = neg->GetLocations(); + Location out = locations->Out(); + Location in = locations->InAt(0); + switch (neg->GetResultType()) { + case Primitive::kPrimInt: + DCHECK(in.IsRegister()); + __ rsb(out.As<Register>(), in.As<Register>(), ShifterOperand(0)); + break; + + case Primitive::kPrimLong: + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + LOG(FATAL) << "Not yet implemented neg type " << neg->GetResultType(); + break; + + default: + LOG(FATAL) << "Unexpected neg type " << neg->GetResultType(); + } +} + void LocationsBuilderARM::VisitAdd(HAdd* add) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(add, LocationSummary::kNoCall); switch (add->GetResultType()) { case Primitive::kPrimInt: case Primitive::kPrimLong: { - bool dies_at_entry = add->GetResultType() != Primitive::kPrimLong; - locations->SetInAt(0, Location::RequiresRegister(), dies_at_entry); - locations->SetInAt(1, Location::RegisterOrConstant(add->InputAt(1)), dies_at_entry); - locations->SetOut(Location::RequiresRegister()); + bool output_overlaps = (add->GetResultType() == Primitive::kPrimLong); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RegisterOrConstant(add->InputAt(1))); + locations->SetOut(Location::RequiresRegister(), output_overlaps); break; } @@ -1051,59 +1151,149 @@ void LocationsBuilderARM::VisitSub(HSub* sub) { switch (sub->GetResultType()) { case Primitive::kPrimInt: case Primitive::kPrimLong: { - bool dies_at_entry = sub->GetResultType() != Primitive::kPrimLong; - locations->SetInAt(0, Location::RequiresRegister(), dies_at_entry); - locations->SetInAt(1, Location::RegisterOrConstant(sub->InputAt(1)), dies_at_entry); - locations->SetOut(Location::RequiresRegister()); + bool output_overlaps = (sub->GetResultType() == Primitive::kPrimLong); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RegisterOrConstant(sub->InputAt(1))); + locations->SetOut(Location::RequiresRegister(), output_overlaps); break; } - - case Primitive::kPrimBoolean: - case Primitive::kPrimByte: - case Primitive::kPrimChar: - case Primitive::kPrimShort: - LOG(FATAL) << "Unexpected sub type " << sub->GetResultType(); + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: { + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetInAt(1, Location::RequiresFpuRegister()); + locations->SetOut(Location::RequiresFpuRegister()); break; - + } default: - LOG(FATAL) << "Unimplemented sub type " << sub->GetResultType(); + LOG(FATAL) << "Unexpected sub type " << sub->GetResultType(); } } void InstructionCodeGeneratorARM::VisitSub(HSub* sub) { LocationSummary* locations = sub->GetLocations(); + Location out = locations->Out(); + Location first = locations->InAt(0); + Location second = locations->InAt(1); switch (sub->GetResultType()) { case Primitive::kPrimInt: { - if (locations->InAt(1).IsRegister()) { - __ sub(locations->Out().As<Register>(), - locations->InAt(0).As<Register>(), - ShifterOperand(locations->InAt(1).As<Register>())); + if (second.IsRegister()) { + __ sub(out.As<Register>(), first.As<Register>(), ShifterOperand(second.As<Register>())); } else { - __ AddConstant(locations->Out().As<Register>(), - locations->InAt(0).As<Register>(), - -locations->InAt(1).GetConstant()->AsIntConstant()->GetValue()); + __ AddConstant(out.As<Register>(), + first.As<Register>(), + -second.GetConstant()->AsIntConstant()->GetValue()); } break; } - case Primitive::kPrimLong: - __ subs(locations->Out().AsRegisterPairLow<Register>(), - locations->InAt(0).AsRegisterPairLow<Register>(), - ShifterOperand(locations->InAt(1).AsRegisterPairLow<Register>())); - __ sbc(locations->Out().AsRegisterPairHigh<Register>(), - locations->InAt(0).AsRegisterPairHigh<Register>(), - ShifterOperand(locations->InAt(1).AsRegisterPairHigh<Register>())); + case Primitive::kPrimLong: { + __ subs(out.AsRegisterPairLow<Register>(), + first.AsRegisterPairLow<Register>(), + ShifterOperand(second.AsRegisterPairLow<Register>())); + __ sbc(out.AsRegisterPairHigh<Register>(), + first.AsRegisterPairHigh<Register>(), + ShifterOperand(second.AsRegisterPairHigh<Register>())); break; + } - case Primitive::kPrimBoolean: - case Primitive::kPrimByte: - case Primitive::kPrimChar: - case Primitive::kPrimShort: + case Primitive::kPrimFloat: { + __ vsubs(FromDToLowS(out.As<DRegister>()), + FromDToLowS(first.As<DRegister>()), + FromDToLowS(second.As<DRegister>())); + break; + } + + case Primitive::kPrimDouble: { + __ vsubd(out.As<DRegister>(), first.As<DRegister>(), second.As<DRegister>()); + break; + } + + + default: LOG(FATAL) << "Unexpected sub type " << sub->GetResultType(); + } +} + +void LocationsBuilderARM::VisitMul(HMul* mul) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(mul, LocationSummary::kNoCall); + switch (mul->GetResultType()) { + case Primitive::kPrimInt: + case Primitive::kPrimLong: { + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + break; + } + + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: { + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetInAt(1, Location::RequiresFpuRegister()); + locations->SetOut(Location::RequiresFpuRegister()); + break; + } + + default: + LOG(FATAL) << "Unexpected mul type " << mul->GetResultType(); + } +} + +void InstructionCodeGeneratorARM::VisitMul(HMul* mul) { + LocationSummary* locations = mul->GetLocations(); + Location out = locations->Out(); + Location first = locations->InAt(0); + Location second = locations->InAt(1); + switch (mul->GetResultType()) { + case Primitive::kPrimInt: { + __ mul(out.As<Register>(), first.As<Register>(), second.As<Register>()); + break; + } + case Primitive::kPrimLong: { + Register out_hi = out.AsRegisterPairHigh<Register>(); + Register out_lo = out.AsRegisterPairLow<Register>(); + Register in1_hi = first.AsRegisterPairHigh<Register>(); + Register in1_lo = first.AsRegisterPairLow<Register>(); + Register in2_hi = second.AsRegisterPairHigh<Register>(); + Register in2_lo = second.AsRegisterPairLow<Register>(); + + // Extra checks to protect caused by the existence of R1_R2. + // The algorithm is wrong if out.hi is either in1.lo or in2.lo: + // (e.g. in1=r0_r1, in2=r2_r3 and out=r1_r2); + DCHECK_NE(out_hi, in1_lo); + DCHECK_NE(out_hi, in2_lo); + + // input: in1 - 64 bits, in2 - 64 bits + // output: out + // formula: out.hi : out.lo = (in1.lo * in2.hi + in1.hi * in2.lo)* 2^32 + in1.lo * in2.lo + // parts: out.hi = in1.lo * in2.hi + in1.hi * in2.lo + (in1.lo * in2.lo)[63:32] + // parts: out.lo = (in1.lo * in2.lo)[31:0] + + // IP <- in1.lo * in2.hi + __ mul(IP, in1_lo, in2_hi); + // out.hi <- in1.lo * in2.hi + in1.hi * in2.lo + __ mla(out_hi, in1_hi, in2_lo, IP); + // out.lo <- (in1.lo * in2.lo)[31:0]; + __ umull(out_lo, IP, in1_lo, in2_lo); + // out.hi <- in2.hi * in1.lo + in2.lo * in1.hi + (in1.lo * in2.lo)[63:32] + __ add(out_hi, out_hi, ShifterOperand(IP)); + break; + } + + case Primitive::kPrimFloat: { + __ vmuls(FromDToLowS(out.As<DRegister>()), + FromDToLowS(first.As<DRegister>()), + FromDToLowS(second.As<DRegister>())); break; + } + + case Primitive::kPrimDouble: { + __ vmuld(out.As<DRegister>(), first.As<DRegister>(), second.As<DRegister>()); + break; + } default: - LOG(FATAL) << "Unimplemented sub type " << sub->GetResultType(); + LOG(FATAL) << "Unexpected mul type " << mul->GetResultType(); } } @@ -1129,6 +1319,29 @@ void InstructionCodeGeneratorARM::VisitNewInstance(HNewInstance* instruction) { DCHECK(!codegen_->IsLeafMethod()); } +void LocationsBuilderARM::VisitNewArray(HNewArray* instruction) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall); + InvokeRuntimeCallingConvention calling_convention; + locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(1))); + locations->SetOut(Location::RegisterLocation(R0)); + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(2))); +} + +void InstructionCodeGeneratorARM::VisitNewArray(HNewArray* instruction) { + InvokeRuntimeCallingConvention calling_convention; + LoadCurrentMethod(calling_convention.GetRegisterAt(1)); + __ LoadImmediate(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex()); + + int32_t offset = QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pAllocArrayWithAccessCheck).Int32Value(); + __ LoadFromOffset(kLoadWord, LR, TR, offset); + __ blx(LR); + + codegen_->RecordPcInfo(instruction, instruction->GetDexPc()); + DCHECK(!codegen_->IsLeafMethod()); +} + void LocationsBuilderARM::VisitParameterValue(HParameterValue* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); @@ -1145,25 +1358,41 @@ void InstructionCodeGeneratorARM::VisitParameterValue(HParameterValue* instructi // Nothing to do, the parameter is already at its location. } -void LocationsBuilderARM::VisitNot(HNot* instruction) { +void LocationsBuilderARM::VisitNot(HNot* not_) { LocationSummary* locations = - new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); - locations->SetInAt(0, Location::RequiresRegister(), Location::kDiesAtEntry); - locations->SetOut(Location::RequiresRegister()); + new (GetGraph()->GetArena()) LocationSummary(not_, LocationSummary::kNoCall); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); } -void InstructionCodeGeneratorARM::VisitNot(HNot* instruction) { - LocationSummary* locations = instruction->GetLocations(); - __ eor(locations->Out().As<Register>(), - locations->InAt(0).As<Register>(), ShifterOperand(1)); +void InstructionCodeGeneratorARM::VisitNot(HNot* not_) { + LocationSummary* locations = not_->GetLocations(); + Location out = locations->Out(); + Location in = locations->InAt(0); + switch (not_->InputAt(0)->GetType()) { + case Primitive::kPrimBoolean: + __ eor(out.As<Register>(), in.As<Register>(), ShifterOperand(1)); + break; + + case Primitive::kPrimInt: + __ mvn(out.As<Register>(), ShifterOperand(in.As<Register>())); + break; + + case Primitive::kPrimLong: + LOG(FATAL) << "Not yet implemented type for not operation " << not_->GetResultType(); + break; + + default: + LOG(FATAL) << "Unimplemented type for not operation " << not_->GetResultType(); + } } void LocationsBuilderARM::VisitCompare(HCompare* compare) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(compare, LocationSummary::kNoCall); - locations->SetInAt(0, Location::RequiresRegister(), Location::kDiesAtEntry); - locations->SetInAt(1, Location::RequiresRegister(), Location::kDiesAtEntry); - locations->SetOut(Location::RequiresRegister()); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); } void InstructionCodeGeneratorARM::VisitCompare(HCompare* compare) { @@ -1219,9 +1448,8 @@ void LocationsBuilderARM::VisitInstanceFieldSet(HInstanceFieldSet* instruction) LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); bool is_object_type = instruction->GetFieldType() == Primitive::kPrimNot; - bool dies_at_entry = !is_object_type; - locations->SetInAt(0, Location::RequiresRegister(), dies_at_entry); - locations->SetInAt(1, Location::RequiresRegister(), dies_at_entry); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); // Temporary registers for the write barrier. if (is_object_type) { locations->AddTemp(Location::RequiresRegister()); @@ -1281,8 +1509,8 @@ void InstructionCodeGeneratorARM::VisitInstanceFieldSet(HInstanceFieldSet* instr void LocationsBuilderARM::VisitInstanceFieldGet(HInstanceFieldGet* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); - locations->SetInAt(0, Location::RequiresRegister(), Location::kDiesAtEntry); - locations->SetOut(Location::RequiresRegister()); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); } void InstructionCodeGeneratorARM::VisitInstanceFieldGet(HInstanceFieldGet* instruction) { @@ -1349,7 +1577,7 @@ void LocationsBuilderARM::VisitNullCheck(HNullCheck* instruction) { } void InstructionCodeGeneratorARM::VisitNullCheck(HNullCheck* instruction) { - SlowPathCode* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathARM(instruction); + SlowPathCodeARM* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathARM(instruction); codegen_->AddSlowPath(slow_path); LocationSummary* locations = instruction->GetLocations(); @@ -1368,10 +1596,9 @@ void InstructionCodeGeneratorARM::VisitNullCheck(HNullCheck* instruction) { void LocationsBuilderARM::VisitArrayGet(HArrayGet* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); - locations->SetInAt(0, Location::RequiresRegister(), Location::kDiesAtEntry); - locations->SetInAt( - 1, Location::RegisterOrConstant(instruction->InputAt(1)), Location::kDiesAtEntry); - locations->SetOut(Location::RequiresRegister()); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1))); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); } void InstructionCodeGeneratorARM::VisitArrayGet(HArrayGet* instruction) { @@ -1481,10 +1708,9 @@ void LocationsBuilderARM::VisitArraySet(HArraySet* instruction) { locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2))); } else { - locations->SetInAt(0, Location::RequiresRegister(), Location::kDiesAtEntry); - locations->SetInAt( - 1, Location::RegisterOrConstant(instruction->InputAt(1)), Location::kDiesAtEntry); - locations->SetInAt(2, Location::RequiresRegister(), Location::kDiesAtEntry); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1))); + locations->SetInAt(2, Location::RequiresRegister()); } } @@ -1571,8 +1797,8 @@ void InstructionCodeGeneratorARM::VisitArraySet(HArraySet* instruction) { void LocationsBuilderARM::VisitArrayLength(HArrayLength* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); - locations->SetInAt(0, Location::RequiresRegister(), Location::kDiesAtEntry); - locations->SetOut(Location::RequiresRegister()); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); } void InstructionCodeGeneratorARM::VisitArrayLength(HArrayLength* instruction) { @@ -1595,7 +1821,7 @@ void LocationsBuilderARM::VisitBoundsCheck(HBoundsCheck* instruction) { void InstructionCodeGeneratorARM::VisitBoundsCheck(HBoundsCheck* instruction) { LocationSummary* locations = instruction->GetLocations(); - SlowPathCode* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathARM( + SlowPathCodeARM* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathARM( instruction, locations->InAt(0), locations->InAt(1)); codegen_->AddSlowPath(slow_path); @@ -1693,7 +1919,7 @@ void ParallelMoveResolverARM::EmitMove(size_t index) { } } else { DCHECK(source.IsConstant()); - DCHECK(source.GetConstant()->AsIntConstant() != nullptr); + DCHECK(source.GetConstant()->IsIntConstant()); int32_t value = source.GetConstant()->AsIntConstant()->GetValue(); if (destination.IsRegister()) { __ LoadImmediate(destination.As<Register>(), value); diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h index 874db0fd54..1fe8a7eacc 100644 --- a/compiler/optimizing/code_generator_arm.h +++ b/compiler/optimizing/code_generator_arm.h @@ -140,10 +140,10 @@ class CodeGeneratorARM : public CodeGenerator { virtual void GenerateFrameEntry() OVERRIDE; virtual void GenerateFrameExit() OVERRIDE; - virtual void Bind(Label* label) OVERRIDE; + virtual void Bind(HBasicBlock* block) OVERRIDE; virtual void Move(HInstruction* instruction, Location location, HInstruction* move_for) OVERRIDE; - virtual void SaveCoreRegister(Location stack_location, uint32_t reg_id) OVERRIDE; - virtual void RestoreCoreRegister(Location stack_location, uint32_t reg_id) OVERRIDE; + virtual size_t SaveCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE; + virtual size_t RestoreCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE; virtual size_t GetWordSize() const OVERRIDE { return kArmWordSize; @@ -164,6 +164,7 @@ class CodeGeneratorARM : public CodeGenerator { } virtual void SetupBlockedRegisters() const OVERRIDE; + virtual Location AllocateFreeRegister(Primitive::Type type) const OVERRIDE; virtual Location GetStackLocation(HLoadLocal* load) const OVERRIDE; @@ -171,6 +172,9 @@ class CodeGeneratorARM : public CodeGenerator { virtual void DumpCoreRegister(std::ostream& stream, int reg) const OVERRIDE; virtual void DumpFloatingPointRegister(std::ostream& stream, int reg) const OVERRIDE; + // Blocks all register pairs made out of blocked core registers. + void UpdateBlockedPairRegisters() const; + ParallelMoveResolverARM* GetMoveResolver() { return &move_resolver_; } @@ -187,7 +191,17 @@ class CodeGeneratorARM : public CodeGenerator { // Emit a write barrier. void MarkGCCard(Register temp, Register card, Register object, Register value); + Label* GetLabelOf(HBasicBlock* block) const { + return block_labels_.GetRawStorage() + block->GetBlockId(); + } + + virtual void Initialize() OVERRIDE { + block_labels_.SetSize(GetGraph()->GetBlocks().Size()); + } + private: + // Labels for each block that will be compiled. + GrowableArray<Label> block_labels_; LocationsBuilderARM location_builder_; InstructionCodeGeneratorARM instruction_visitor_; ParallelMoveResolverARM move_resolver_; diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc new file mode 100644 index 0000000000..79528ac128 --- /dev/null +++ b/compiler/optimizing/code_generator_arm64.cc @@ -0,0 +1,1205 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "code_generator_arm64.h" + +#include "entrypoints/quick/quick_entrypoints.h" +#include "gc/accounting/card_table.h" +#include "mirror/array-inl.h" +#include "mirror/art_method.h" +#include "mirror/class.h" +#include "thread.h" +#include "utils/arm64/assembler_arm64.h" +#include "utils/assembler.h" +#include "utils/stack_checks.h" + + +using namespace vixl; // NOLINT(build/namespaces) + +#ifdef __ +#error "ARM64 Codegen VIXL macro-assembler macro already defined." +#endif + + +namespace art { + +namespace arm64 { + +static bool IsFPType(Primitive::Type type) { + return type == Primitive::kPrimFloat || type == Primitive::kPrimDouble; +} + +// TODO: clean-up some of the constant definitions. +static constexpr size_t kHeapRefSize = sizeof(mirror::HeapReference<mirror::Object>); +static constexpr int kCurrentMethodStackOffset = 0; + +namespace { +// Convenience helpers to ease conversion to and from VIXL operands. + +int VIXLRegCodeFromART(int code) { + // TODO: static check? + DCHECK_EQ(SP, 31); + DCHECK_EQ(WSP, 31); + DCHECK_EQ(XZR, 32); + DCHECK_EQ(WZR, 32); + if (code == SP) { + return vixl::kSPRegInternalCode; + } + if (code == XZR) { + return vixl::kZeroRegCode; + } + return code; +} + +int ARTRegCodeFromVIXL(int code) { + // TODO: static check? + DCHECK_EQ(SP, 31); + DCHECK_EQ(WSP, 31); + DCHECK_EQ(XZR, 32); + DCHECK_EQ(WZR, 32); + if (code == vixl::kSPRegInternalCode) { + return SP; + } + if (code == vixl::kZeroRegCode) { + return XZR; + } + return code; +} + +Register XRegisterFrom(Location location) { + return Register::XRegFromCode(VIXLRegCodeFromART(location.reg())); +} + +Register WRegisterFrom(Location location) { + return Register::WRegFromCode(VIXLRegCodeFromART(location.reg())); +} + +Register RegisterFrom(Location location, Primitive::Type type) { + DCHECK(type != Primitive::kPrimVoid && !IsFPType(type)); + return type == Primitive::kPrimLong ? XRegisterFrom(location) : WRegisterFrom(location); +} + +Register OutputRegister(HInstruction* instr) { + return RegisterFrom(instr->GetLocations()->Out(), instr->GetType()); +} + +Register InputRegisterAt(HInstruction* instr, int input_index) { + return RegisterFrom(instr->GetLocations()->InAt(input_index), + instr->InputAt(input_index)->GetType()); +} + +int64_t Int64ConstantFrom(Location location) { + HConstant* instr = location.GetConstant(); + return instr->IsIntConstant() ? instr->AsIntConstant()->GetValue() + : instr->AsLongConstant()->GetValue(); +} + +Operand OperandFrom(Location location, Primitive::Type type) { + if (location.IsRegister()) { + return Operand(RegisterFrom(location, type)); + } else { + return Operand(Int64ConstantFrom(location)); + } +} + +Operand InputOperandAt(HInstruction* instr, int input_index) { + return OperandFrom(instr->GetLocations()->InAt(input_index), + instr->InputAt(input_index)->GetType()); +} + +MemOperand StackOperandFrom(Location location) { + return MemOperand(sp, location.GetStackIndex()); +} + +MemOperand HeapOperand(const Register& base, Offset offset) { + // A heap reference must be 32bit, so fit in a W register. + DCHECK(base.IsW()); + return MemOperand(base.X(), offset.SizeValue()); +} + +MemOperand HeapOperandFrom(Location location, Primitive::Type type, Offset offset) { + return HeapOperand(RegisterFrom(location, type), offset); +} + +Location LocationFrom(const Register& reg) { + return Location::RegisterLocation(ARTRegCodeFromVIXL(reg.code())); +} + +} // namespace + +inline Condition ARM64Condition(IfCondition cond) { + switch (cond) { + case kCondEQ: return eq; + case kCondNE: return ne; + case kCondLT: return lt; + case kCondLE: return le; + case kCondGT: return gt; + case kCondGE: return ge; + default: + LOG(FATAL) << "Unknown if condition"; + } + return nv; // Unreachable. +} + +static const Register kRuntimeParameterCoreRegisters[] = { x0, x1, x2, x3, x4, x5, x6, x7 }; +static constexpr size_t kRuntimeParameterCoreRegistersLength = + arraysize(kRuntimeParameterCoreRegisters); +static const FPRegister kRuntimeParameterFpuRegisters[] = { }; +static constexpr size_t kRuntimeParameterFpuRegistersLength = 0; + +class InvokeRuntimeCallingConvention : public CallingConvention<Register, FPRegister> { + public: + static constexpr size_t kParameterCoreRegistersLength = arraysize(kParameterCoreRegisters); + + InvokeRuntimeCallingConvention() + : CallingConvention(kRuntimeParameterCoreRegisters, + kRuntimeParameterCoreRegistersLength, + kRuntimeParameterFpuRegisters, + kRuntimeParameterFpuRegistersLength) {} + + Location GetReturnLocation(Primitive::Type return_type); + + private: + DISALLOW_COPY_AND_ASSIGN(InvokeRuntimeCallingConvention); +}; + +Location InvokeRuntimeCallingConvention::GetReturnLocation(Primitive::Type return_type) { + DCHECK_NE(return_type, Primitive::kPrimVoid); + if (return_type == Primitive::kPrimFloat || return_type == Primitive::kPrimDouble) { + LOG(FATAL) << "Unimplemented return type " << return_type; + } + return LocationFrom(x0); +} + +#define __ reinterpret_cast<Arm64Assembler*>(codegen->GetAssembler())->vixl_masm_-> + +class SlowPathCodeARM64 : public SlowPathCode { + public: + SlowPathCodeARM64() : entry_label_(), exit_label_() {} + + vixl::Label* GetEntryLabel() { return &entry_label_; } + vixl::Label* GetExitLabel() { return &exit_label_; } + + private: + vixl::Label entry_label_; + vixl::Label exit_label_; + + DISALLOW_COPY_AND_ASSIGN(SlowPathCodeARM64); +}; + +class BoundsCheckSlowPathARM64 : public SlowPathCodeARM64 { + public: + explicit BoundsCheckSlowPathARM64(HBoundsCheck* instruction, + Location index_location, + Location length_location) + : instruction_(instruction), + index_location_(index_location), + length_location_(length_location) {} + + virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + CodeGeneratorARM64* arm64_codegen = reinterpret_cast<CodeGeneratorARM64*>(codegen); + __ Bind(GetEntryLabel()); + InvokeRuntimeCallingConvention calling_convention; + arm64_codegen->MoveHelper(LocationFrom(calling_convention.GetRegisterAt(0)), + index_location_, Primitive::kPrimInt); + arm64_codegen->MoveHelper(LocationFrom(calling_convention.GetRegisterAt(1)), + length_location_, Primitive::kPrimInt); + size_t offset = QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pThrowArrayBounds).SizeValue(); + __ Ldr(lr, MemOperand(tr, offset)); + __ Blr(lr); + codegen->RecordPcInfo(instruction_, instruction_->GetDexPc()); + } + + private: + HBoundsCheck* const instruction_; + const Location index_location_; + const Location length_location_; + + DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathARM64); +}; + +class NullCheckSlowPathARM64 : public SlowPathCodeARM64 { + public: + explicit NullCheckSlowPathARM64(HNullCheck* instr) : instruction_(instr) {} + + virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + __ Bind(GetEntryLabel()); + int32_t offset = QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pThrowNullPointer).Int32Value(); + __ Ldr(lr, MemOperand(tr, offset)); + __ Blr(lr); + codegen->RecordPcInfo(instruction_, instruction_->GetDexPc()); + } + + private: + HNullCheck* const instruction_; + + DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathARM64); +}; + +class SuspendCheckSlowPathARM64 : public SlowPathCodeARM64 { + public: + explicit SuspendCheckSlowPathARM64(HSuspendCheck* instruction, + HBasicBlock* successor) + : instruction_(instruction), successor_(successor) {} + + virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + size_t offset = QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pTestSuspend).SizeValue(); + __ Bind(GetEntryLabel()); + __ Ldr(lr, MemOperand(tr, offset)); + __ Blr(lr); + codegen->RecordPcInfo(instruction_, instruction_->GetDexPc()); + __ B(GetReturnLabel()); + } + + vixl::Label* GetReturnLabel() { + DCHECK(successor_ == nullptr); + return &return_label_; + } + + + private: + HSuspendCheck* const instruction_; + // If not null, the block to branch to after the suspend check. + HBasicBlock* const successor_; + + // If `successor_` is null, the label to branch to after the suspend check. + vixl::Label return_label_; + + DISALLOW_COPY_AND_ASSIGN(SuspendCheckSlowPathARM64); +}; + +#undef __ + +Location InvokeDexCallingConventionVisitor::GetNextLocation(Primitive::Type type) { + Location next_location; + if (type == Primitive::kPrimVoid) { + LOG(FATAL) << "Unreachable type " << type; + } + + if (type == Primitive::kPrimFloat || type == Primitive::kPrimDouble) { + LOG(FATAL) << "Unimplemented type " << type; + } + + if (gp_index_ < calling_convention.GetNumberOfRegisters()) { + next_location = LocationFrom(calling_convention.GetRegisterAt(gp_index_)); + if (type == Primitive::kPrimLong) { + // Double stack slot reserved on the stack. + stack_index_++; + } + } else { // Stack. + if (type == Primitive::kPrimLong) { + next_location = Location::DoubleStackSlot(calling_convention.GetStackOffsetOf(stack_index_)); + // Double stack slot reserved on the stack. + stack_index_++; + } else { + next_location = Location::StackSlot(calling_convention.GetStackOffsetOf(stack_index_)); + } + } + // Move to the next register/stack slot. + gp_index_++; + stack_index_++; + return next_location; +} + +CodeGeneratorARM64::CodeGeneratorARM64(HGraph* graph) + : CodeGenerator(graph, + kNumberOfAllocatableRegisters, + kNumberOfAllocatableFloatingPointRegisters, + kNumberOfAllocatableRegisterPairs), + block_labels_(nullptr), + location_builder_(graph, this), + instruction_visitor_(graph, this) {} + +#define __ reinterpret_cast<Arm64Assembler*>(GetAssembler())->vixl_masm_-> + +void CodeGeneratorARM64::GenerateFrameEntry() { + // TODO: Add proper support for the stack overflow check. + UseScratchRegisterScope temps(assembler_.vixl_masm_); + Register temp = temps.AcquireX(); + __ Add(temp, sp, -static_cast<int32_t>(GetStackOverflowReservedBytes(kArm64))); + __ Ldr(temp, MemOperand(temp, 0)); + RecordPcInfo(nullptr, 0); + + CPURegList preserved_regs = GetFramePreservedRegisters(); + int frame_size = GetFrameSize(); + core_spill_mask_ |= preserved_regs.list(); + + __ Str(w0, MemOperand(sp, -frame_size, PreIndex)); + __ PokeCPURegList(preserved_regs, frame_size - preserved_regs.TotalSizeInBytes()); + + // Stack layout: + // sp[frame_size - 8] : lr. + // ... : other preserved registers. + // sp[frame_size - regs_size]: first preserved register. + // ... : reserved frame space. + // sp[0] : context pointer. +} + +void CodeGeneratorARM64::GenerateFrameExit() { + int frame_size = GetFrameSize(); + CPURegList preserved_regs = GetFramePreservedRegisters(); + __ PeekCPURegList(preserved_regs, frame_size - preserved_regs.TotalSizeInBytes()); + __ Drop(frame_size); +} + +void CodeGeneratorARM64::Bind(HBasicBlock* block) { + __ Bind(GetLabelOf(block)); +} + +void CodeGeneratorARM64::MoveHelper(Location destination, + Location source, + Primitive::Type type) { + if (source.Equals(destination)) { + return; + } + if (destination.IsRegister()) { + Register dst = RegisterFrom(destination, type); + if (source.IsRegister()) { + Register src = RegisterFrom(source, type); + DCHECK(dst.IsSameSizeAndType(src)); + __ Mov(dst, src); + } else { + DCHECK(dst.Is64Bits() || !source.IsDoubleStackSlot()); + __ Ldr(dst, StackOperandFrom(source)); + } + } else { + DCHECK(destination.IsStackSlot() || destination.IsDoubleStackSlot()); + if (source.IsRegister()) { + __ Str(RegisterFrom(source, type), StackOperandFrom(destination)); + } else { + UseScratchRegisterScope temps(assembler_.vixl_masm_); + Register temp = destination.IsDoubleStackSlot() ? temps.AcquireX() : temps.AcquireW(); + __ Ldr(temp, StackOperandFrom(source)); + __ Str(temp, StackOperandFrom(destination)); + } + } +} + +void CodeGeneratorARM64::Move(HInstruction* instruction, + Location location, + HInstruction* move_for) { + LocationSummary* locations = instruction->GetLocations(); + if (locations != nullptr && locations->Out().Equals(location)) { + return; + } + + Primitive::Type type = instruction->GetType(); + + if (instruction->IsIntConstant() || instruction->IsLongConstant()) { + int64_t value = instruction->IsIntConstant() ? instruction->AsIntConstant()->GetValue() + : instruction->AsLongConstant()->GetValue(); + if (location.IsRegister()) { + Register dst = RegisterFrom(location, type); + DCHECK((instruction->IsIntConstant() && dst.Is32Bits()) || + (instruction->IsLongConstant() && dst.Is64Bits())); + __ Mov(dst, value); + } else { + DCHECK(location.IsStackSlot() || location.IsDoubleStackSlot()); + UseScratchRegisterScope temps(assembler_.vixl_masm_); + Register temp = instruction->IsIntConstant() ? temps.AcquireW() : temps.AcquireX(); + __ Mov(temp, value); + __ Str(temp, StackOperandFrom(location)); + } + + } else if (instruction->IsLoadLocal()) { + uint32_t stack_slot = GetStackSlot(instruction->AsLoadLocal()->GetLocal()); + switch (type) { + case Primitive::kPrimNot: + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimChar: + case Primitive::kPrimShort: + case Primitive::kPrimInt: + MoveHelper(location, Location::StackSlot(stack_slot), type); + break; + case Primitive::kPrimLong: + MoveHelper(location, Location::DoubleStackSlot(stack_slot), type); + break; + default: + LOG(FATAL) << "Unimplemented type" << type; + } + + } else { + DCHECK((instruction->GetNext() == move_for) || instruction->GetNext()->IsTemporary()); + MoveHelper(location, locations->Out(), type); + } +} + +size_t CodeGeneratorARM64::FrameEntrySpillSize() const { + return GetFramePreservedRegistersSize(); +} + +Location CodeGeneratorARM64::GetStackLocation(HLoadLocal* load) const { + Primitive::Type type = load->GetType(); + switch (type) { + case Primitive::kPrimNot: + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimChar: + case Primitive::kPrimShort: + case Primitive::kPrimInt: + return Location::StackSlot(GetStackSlot(load->GetLocal())); + case Primitive::kPrimLong: + return Location::DoubleStackSlot(GetStackSlot(load->GetLocal())); + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + LOG(FATAL) << "Unimplemented type " << type; + break; + case Primitive::kPrimVoid: + default: + LOG(FATAL) << "Unexpected type " << type; + } + LOG(FATAL) << "Unreachable"; + return Location::NoLocation(); +} + +void CodeGeneratorARM64::MarkGCCard(Register object, Register value) { + UseScratchRegisterScope temps(assembler_.vixl_masm_); + Register card = temps.AcquireX(); + Register temp = temps.AcquireX(); + vixl::Label done; + __ Cbz(value, &done); + __ Ldr(card, MemOperand(tr, Thread::CardTableOffset<kArm64WordSize>().Int32Value())); + __ Lsr(temp, object, gc::accounting::CardTable::kCardShift); + __ Strb(card, MemOperand(card, temp)); + __ Bind(&done); +} + +void CodeGeneratorARM64::SetupBlockedRegisters() const { + // Block reserved registers: + // ip0 (VIXL temporary) + // ip1 (VIXL temporary) + // xSuspend (Suspend counter) + // lr + // sp is not part of the allocatable registers, so we don't need to block it. + CPURegList reserved_core_registers = vixl_reserved_core_registers; + reserved_core_registers.Combine(runtime_reserved_core_registers); + // TODO: See if we should instead allow allocating but preserve those if used. + reserved_core_registers.Combine(quick_callee_saved_registers); + while (!reserved_core_registers.IsEmpty()) { + blocked_core_registers_[reserved_core_registers.PopLowestIndex().code()] = true; + } +} + +Location CodeGeneratorARM64::AllocateFreeRegister(Primitive::Type type) const { + if (type == Primitive::kPrimVoid) { + LOG(FATAL) << "Unreachable type " << type; + } + + if (type == Primitive::kPrimFloat || type == Primitive::kPrimDouble) { + LOG(FATAL) << "Unimplemented support for floating-point"; + } + + ssize_t reg = FindFreeEntry(blocked_core_registers_, kNumberOfXRegisters); + DCHECK_NE(reg, -1); + blocked_core_registers_[reg] = true; + + if (IsFPType(type)) { + return Location::FpuRegisterLocation(reg); + } else { + return Location::RegisterLocation(reg); + } +} + +void CodeGeneratorARM64::DumpCoreRegister(std::ostream& stream, int reg) const { + stream << Arm64ManagedRegister::FromXRegister(XRegister(reg)); +} + +void CodeGeneratorARM64::DumpFloatingPointRegister(std::ostream& stream, int reg) const { + stream << Arm64ManagedRegister::FromDRegister(DRegister(reg)); +} + +#undef __ +#define __ assembler_->vixl_masm_-> + +InstructionCodeGeneratorARM64::InstructionCodeGeneratorARM64(HGraph* graph, + CodeGeneratorARM64* codegen) + : HGraphVisitor(graph), + assembler_(codegen->GetAssembler()), + codegen_(codegen) {} + +#define FOR_EACH_UNIMPLEMENTED_INSTRUCTION(M) \ + M(ArrayGet) \ + M(ArraySet) \ + M(DoubleConstant) \ + M(FloatConstant) \ + M(Mul) \ + M(Neg) \ + M(NewArray) \ + M(ParallelMove) + +#define UNIMPLEMENTED_INSTRUCTION_BREAK_CODE(name) name##UnimplementedInstructionBreakCode + +enum UnimplementedInstructionBreakCode { +#define ENUM_UNIMPLEMENTED_INSTRUCTION(name) UNIMPLEMENTED_INSTRUCTION_BREAK_CODE(name), + FOR_EACH_UNIMPLEMENTED_INSTRUCTION(ENUM_UNIMPLEMENTED_INSTRUCTION) +#undef ENUM_UNIMPLEMENTED_INSTRUCTION +}; + +#define DEFINE_UNIMPLEMENTED_INSTRUCTION_VISITORS(name) \ + void InstructionCodeGeneratorARM64::Visit##name(H##name* instr) { \ + __ Brk(UNIMPLEMENTED_INSTRUCTION_BREAK_CODE(name)); \ + } \ + void LocationsBuilderARM64::Visit##name(H##name* instr) { \ + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instr); \ + locations->SetOut(Location::Any()); \ + } + FOR_EACH_UNIMPLEMENTED_INSTRUCTION(DEFINE_UNIMPLEMENTED_INSTRUCTION_VISITORS) +#undef DEFINE_UNIMPLEMENTED_INSTRUCTION_VISITORS + +#undef UNIMPLEMENTED_INSTRUCTION_BREAK_CODE + +void LocationsBuilderARM64::HandleAddSub(HBinaryOperation* instr) { + DCHECK(instr->IsAdd() || instr->IsSub()); + DCHECK_EQ(instr->InputCount(), 2U); + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instr); + Primitive::Type type = instr->GetResultType(); + switch (type) { + case Primitive::kPrimInt: + case Primitive::kPrimLong: { + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RegisterOrConstant(instr->InputAt(1))); + locations->SetOut(Location::RequiresRegister()); + break; + } + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimChar: + case Primitive::kPrimShort: + LOG(FATAL) << "Unexpected " << instr->DebugName() << " type " << type; + break; + default: + LOG(FATAL) << "Unimplemented " << instr->DebugName() << " type " << type; + } +} + +void InstructionCodeGeneratorARM64::HandleAddSub(HBinaryOperation* instr) { + DCHECK(instr->IsAdd() || instr->IsSub()); + + Primitive::Type type = instr->GetType(); + Register dst = OutputRegister(instr); + Register lhs = InputRegisterAt(instr, 0); + Operand rhs = InputOperandAt(instr, 1); + + switch (type) { + case Primitive::kPrimInt: + case Primitive::kPrimLong: + if (instr->IsAdd()) { + __ Add(dst, lhs, rhs); + } else { + __ Sub(dst, lhs, rhs); + } + break; + + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimChar: + case Primitive::kPrimShort: + LOG(FATAL) << "Unexpected add/sub type " << type; + break; + default: + LOG(FATAL) << "Unimplemented add/sub type " << type; + } +} + +void LocationsBuilderARM64::VisitAdd(HAdd* instruction) { + HandleAddSub(instruction); +} + +void InstructionCodeGeneratorARM64::VisitAdd(HAdd* instruction) { + HandleAddSub(instruction); +} + +void LocationsBuilderARM64::VisitArrayLength(HArrayLength* instruction) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister()); +} + +void InstructionCodeGeneratorARM64::VisitArrayLength(HArrayLength* instruction) { + __ Ldr(OutputRegister(instruction), + HeapOperand(InputRegisterAt(instruction, 0), mirror::Array::LengthOffset())); +} + +void LocationsBuilderARM64::VisitCompare(HCompare* instruction) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1))); + locations->SetOut(Location::RequiresRegister()); +} + +void InstructionCodeGeneratorARM64::VisitCompare(HCompare* instruction) { + Primitive::Type in_type = instruction->InputAt(0)->GetType(); + + DCHECK_EQ(in_type, Primitive::kPrimLong); + switch (in_type) { + case Primitive::kPrimLong: { + vixl::Label done; + Register result = OutputRegister(instruction); + Register left = InputRegisterAt(instruction, 0); + Operand right = InputOperandAt(instruction, 1); + __ Subs(result, left, right); + __ B(eq, &done); + __ Mov(result, 1); + __ Cneg(result, result, le); + __ Bind(&done); + break; + } + default: + LOG(FATAL) << "Unimplemented compare type " << in_type; + } +} + +void LocationsBuilderARM64::VisitCondition(HCondition* instruction) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1))); + if (instruction->NeedsMaterialization()) { + locations->SetOut(Location::RequiresRegister()); + } +} + +void InstructionCodeGeneratorARM64::VisitCondition(HCondition* instruction) { + if (!instruction->NeedsMaterialization()) { + return; + } + + LocationSummary* locations = instruction->GetLocations(); + Register lhs = InputRegisterAt(instruction, 0); + Operand rhs = InputOperandAt(instruction, 1); + Register res = RegisterFrom(locations->Out(), instruction->GetType()); + Condition cond = ARM64Condition(instruction->GetCondition()); + + __ Cmp(lhs, rhs); + __ Csel(res, vixl::Assembler::AppropriateZeroRegFor(res), Operand(1), InvertCondition(cond)); +} + +#define FOR_EACH_CONDITION_INSTRUCTION(M) \ + M(Equal) \ + M(NotEqual) \ + M(LessThan) \ + M(LessThanOrEqual) \ + M(GreaterThan) \ + M(GreaterThanOrEqual) +#define DEFINE_CONDITION_VISITORS(Name) \ +void LocationsBuilderARM64::Visit##Name(H##Name* comp) { VisitCondition(comp); } \ +void InstructionCodeGeneratorARM64::Visit##Name(H##Name* comp) { VisitCondition(comp); } +FOR_EACH_CONDITION_INSTRUCTION(DEFINE_CONDITION_VISITORS) +#undef FOR_EACH_CONDITION_INSTRUCTION + +void LocationsBuilderARM64::VisitExit(HExit* exit) { + exit->SetLocations(nullptr); +} + +void InstructionCodeGeneratorARM64::VisitExit(HExit* exit) { + if (kIsDebugBuild) { + down_cast<Arm64Assembler*>(GetAssembler())->Comment("Unreachable"); + __ Brk(0); // TODO: Introduce special markers for such code locations. + } +} + +void LocationsBuilderARM64::VisitGoto(HGoto* got) { + got->SetLocations(nullptr); +} + +void InstructionCodeGeneratorARM64::VisitGoto(HGoto* got) { + HBasicBlock* successor = got->GetSuccessor(); + // TODO: Support for suspend checks emission. + if (!codegen_->GoesToNextBlock(got->GetBlock(), successor)) { + __ B(codegen_->GetLabelOf(successor)); + } +} + +void LocationsBuilderARM64::VisitIf(HIf* if_instr) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(if_instr); + HInstruction* cond = if_instr->InputAt(0); + DCHECK(cond->IsCondition()); + if (cond->AsCondition()->NeedsMaterialization()) { + locations->SetInAt(0, Location::RequiresRegister()); + } +} + +void InstructionCodeGeneratorARM64::VisitIf(HIf* if_instr) { + HInstruction* cond = if_instr->InputAt(0); + DCHECK(cond->IsCondition()); + HCondition* condition = cond->AsCondition(); + vixl::Label* true_target = codegen_->GetLabelOf(if_instr->IfTrueSuccessor()); + vixl::Label* false_target = codegen_->GetLabelOf(if_instr->IfFalseSuccessor()); + + // TODO: Support constant condition input in VisitIf. + + if (condition->NeedsMaterialization()) { + // The condition instruction has been materialized, compare the output to 0. + Location cond_val = if_instr->GetLocations()->InAt(0); + DCHECK(cond_val.IsRegister()); + __ Cbnz(InputRegisterAt(if_instr, 0), true_target); + + } else { + // The condition instruction has not been materialized, use its inputs as + // the comparison and its condition as the branch condition. + Register lhs = InputRegisterAt(condition, 0); + Operand rhs = InputOperandAt(condition, 1); + Condition cond = ARM64Condition(condition->GetCondition()); + if ((cond == eq || cond == ne) && rhs.IsImmediate() && (rhs.immediate() == 0)) { + if (cond == eq) { + __ Cbz(lhs, true_target); + } else { + __ Cbnz(lhs, true_target); + } + } else { + __ Cmp(lhs, rhs); + __ B(cond, true_target); + } + } + + if (!codegen_->GoesToNextBlock(if_instr->GetBlock(), if_instr->IfFalseSuccessor())) { + __ B(false_target); + } +} + +void LocationsBuilderARM64::VisitInstanceFieldGet(HInstanceFieldGet* instruction) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister()); +} + +void InstructionCodeGeneratorARM64::VisitInstanceFieldGet(HInstanceFieldGet* instruction) { + Primitive::Type res_type = instruction->GetType(); + Register res = OutputRegister(instruction); + Register obj = InputRegisterAt(instruction, 0); + uint32_t offset = instruction->GetFieldOffset().Uint32Value(); + + switch (res_type) { + case Primitive::kPrimBoolean: { + __ Ldrb(res, MemOperand(obj, offset)); + break; + } + case Primitive::kPrimByte: { + __ Ldrsb(res, MemOperand(obj, offset)); + break; + } + case Primitive::kPrimShort: { + __ Ldrsh(res, MemOperand(obj, offset)); + break; + } + case Primitive::kPrimChar: { + __ Ldrh(res, MemOperand(obj, offset)); + break; + } + case Primitive::kPrimInt: + case Primitive::kPrimNot: + case Primitive::kPrimLong: { // TODO: support volatile. + DCHECK(res.IsX() == (res_type == Primitive::kPrimLong)); + __ Ldr(res, MemOperand(obj, offset)); + break; + } + + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + LOG(FATAL) << "Unimplemented register res_type " << res_type; + break; + + case Primitive::kPrimVoid: + LOG(FATAL) << "Unreachable res_type " << res_type; + } +} + +void LocationsBuilderARM64::VisitInstanceFieldSet(HInstanceFieldSet* instruction) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); +} + +void InstructionCodeGeneratorARM64::VisitInstanceFieldSet(HInstanceFieldSet* instruction) { + Register obj = InputRegisterAt(instruction, 0); + Register value = InputRegisterAt(instruction, 1); + Primitive::Type field_type = instruction->InputAt(1)->GetType(); + uint32_t offset = instruction->GetFieldOffset().Uint32Value(); + + switch (field_type) { + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: { + __ Strb(value, MemOperand(obj, offset)); + break; + } + + case Primitive::kPrimShort: + case Primitive::kPrimChar: { + __ Strh(value, MemOperand(obj, offset)); + break; + } + + case Primitive::kPrimInt: + case Primitive::kPrimNot: + case Primitive::kPrimLong: { + DCHECK(value.IsX() == (field_type == Primitive::kPrimLong)); + __ Str(value, MemOperand(obj, offset)); + + if (field_type == Primitive::kPrimNot) { + codegen_->MarkGCCard(obj, value); + } + break; + } + + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + LOG(FATAL) << "Unimplemented register type " << field_type; + break; + + case Primitive::kPrimVoid: + LOG(FATAL) << "Unreachable type " << field_type; + } +} + +void LocationsBuilderARM64::VisitIntConstant(HIntConstant* constant) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant); + locations->SetOut(Location::ConstantLocation(constant)); +} + +void InstructionCodeGeneratorARM64::VisitIntConstant(HIntConstant* constant) { + // Will be generated at use site. +} + +void LocationsBuilderARM64::VisitInvokeStatic(HInvokeStatic* invoke) { + HandleInvoke(invoke); +} + +void LocationsBuilderARM64::VisitInvokeVirtual(HInvokeVirtual* invoke) { + HandleInvoke(invoke); +} + +void LocationsBuilderARM64::HandleInvoke(HInvoke* invoke) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(invoke, LocationSummary::kCall); + locations->AddTemp(LocationFrom(x0)); + + InvokeDexCallingConventionVisitor calling_convention_visitor; + for (size_t i = 0; i < invoke->InputCount(); i++) { + HInstruction* input = invoke->InputAt(i); + locations->SetInAt(i, calling_convention_visitor.GetNextLocation(input->GetType())); + } + + Primitive::Type return_type = invoke->GetType(); + if (return_type != Primitive::kPrimVoid) { + locations->SetOut(calling_convention_visitor.GetReturnLocation(return_type)); + } +} + +void InstructionCodeGeneratorARM64::VisitInvokeStatic(HInvokeStatic* invoke) { + Register temp = WRegisterFrom(invoke->GetLocations()->GetTemp(0)); + // Make sure that ArtMethod* is passed in W0 as per the calling convention + DCHECK(temp.Is(w0)); + size_t index_in_cache = mirror::Array::DataOffset(kHeapRefSize).SizeValue() + + invoke->GetIndexInDexCache() * kHeapRefSize; + + // TODO: Implement all kinds of calls: + // 1) boot -> boot + // 2) app -> boot + // 3) app -> app + // + // Currently we implement the app -> app logic, which looks up in the resolve cache. + + // temp = method; + __ Ldr(temp, MemOperand(sp, kCurrentMethodStackOffset)); + // temp = temp->dex_cache_resolved_methods_; + __ Ldr(temp, MemOperand(temp.X(), mirror::ArtMethod::DexCacheResolvedMethodsOffset().SizeValue())); + // temp = temp[index_in_cache]; + __ Ldr(temp, MemOperand(temp.X(), index_in_cache)); + // lr = temp->entry_point_from_quick_compiled_code_; + __ Ldr(lr, MemOperand(temp.X(), mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().SizeValue())); + // lr(); + __ Blr(lr); + + codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); + DCHECK(!codegen_->IsLeafMethod()); +} + +void InstructionCodeGeneratorARM64::VisitInvokeVirtual(HInvokeVirtual* invoke) { + LocationSummary* locations = invoke->GetLocations(); + Location receiver = locations->InAt(0); + Register temp = XRegisterFrom(invoke->GetLocations()->GetTemp(0)); + size_t method_offset = mirror::Class::EmbeddedVTableOffset().SizeValue() + + invoke->GetVTableIndex() * sizeof(mirror::Class::VTableEntry); + Offset class_offset = mirror::Object::ClassOffset(); + Offset entry_point = mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(); + + // temp = object->GetClass(); + if (receiver.IsStackSlot()) { + __ Ldr(temp.W(), MemOperand(sp, receiver.GetStackIndex())); + __ Ldr(temp.W(), MemOperand(temp, class_offset.SizeValue())); + } else { + DCHECK(receiver.IsRegister()); + __ Ldr(temp.W(), HeapOperandFrom(receiver, Primitive::kPrimNot, + class_offset)); + } + // temp = temp->GetMethodAt(method_offset); + __ Ldr(temp.W(), MemOperand(temp, method_offset)); + // lr = temp->GetEntryPoint(); + __ Ldr(lr, MemOperand(temp, entry_point.SizeValue())); + // lr(); + __ Blr(lr); + DCHECK(!codegen_->IsLeafMethod()); + codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); +} + +void LocationsBuilderARM64::VisitLoadLocal(HLoadLocal* load) { + load->SetLocations(nullptr); +} + +void InstructionCodeGeneratorARM64::VisitLoadLocal(HLoadLocal* load) { + // Nothing to do, this is driven by the code generator. +} + +void LocationsBuilderARM64::VisitLocal(HLocal* local) { + local->SetLocations(nullptr); +} + +void InstructionCodeGeneratorARM64::VisitLocal(HLocal* local) { + DCHECK_EQ(local->GetBlock(), GetGraph()->GetEntryBlock()); +} + +void LocationsBuilderARM64::VisitLongConstant(HLongConstant* constant) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant); + locations->SetOut(Location::ConstantLocation(constant)); +} + +void InstructionCodeGeneratorARM64::VisitLongConstant(HLongConstant* constant) { + // Will be generated at use site. +} + +void LocationsBuilderARM64::VisitNewInstance(HNewInstance* instruction) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall); + InvokeRuntimeCallingConvention calling_convention; + locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(0))); + locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(1))); + locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot)); +} + +void InstructionCodeGeneratorARM64::VisitNewInstance(HNewInstance* instruction) { + LocationSummary* locations = instruction->GetLocations(); + Register type_index = RegisterFrom(locations->GetTemp(0), Primitive::kPrimInt); + DCHECK(type_index.Is(w0)); + Register current_method = RegisterFrom(locations->GetTemp(1), Primitive::kPrimNot); + DCHECK(current_method.Is(w1)); + __ Ldr(current_method, MemOperand(sp, kCurrentMethodStackOffset)); + __ Mov(type_index, instruction->GetTypeIndex()); + __ Ldr(lr, MemOperand(tr, QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pAllocObjectWithAccessCheck).Int32Value())); + __ Blr(lr); + codegen_->RecordPcInfo(instruction, instruction->GetDexPc()); + DCHECK(!codegen_->IsLeafMethod()); +} + +void LocationsBuilderARM64::VisitNot(HNot* instruction) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister()); +} + +void InstructionCodeGeneratorARM64::VisitNot(HNot* instruction) { + switch (instruction->InputAt(0)->GetType()) { + case Primitive::kPrimBoolean: + __ Eor(OutputRegister(instruction), InputRegisterAt(instruction, 0), Operand(1)); + break; + + case Primitive::kPrimInt: + __ Mvn(OutputRegister(instruction), InputOperandAt(instruction, 0)); + break; + + case Primitive::kPrimLong: + LOG(FATAL) << "Not yet implemented type for not operation " << instruction->GetResultType(); + break; + + default: + LOG(FATAL) << "Unexpected type for not operation " << instruction->GetResultType(); + } +} + +void LocationsBuilderARM64::VisitNullCheck(HNullCheck* instruction) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + locations->SetInAt(0, Location::RequiresRegister()); + if (instruction->HasUses()) { + locations->SetOut(Location::SameAsFirstInput()); + } +} + +void InstructionCodeGeneratorARM64::VisitNullCheck(HNullCheck* instruction) { + SlowPathCodeARM64* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathARM64(instruction); + codegen_->AddSlowPath(slow_path); + + LocationSummary* locations = instruction->GetLocations(); + Location obj = locations->InAt(0); + if (obj.IsRegister()) { + __ Cbz(RegisterFrom(obj, instruction->InputAt(0)->GetType()), slow_path->GetEntryLabel()); + } else { + DCHECK(obj.IsConstant()) << obj; + DCHECK_EQ(obj.GetConstant()->AsIntConstant()->GetValue(), 0); + __ B(slow_path->GetEntryLabel()); + } +} + +void LocationsBuilderARM64::VisitParameterValue(HParameterValue* instruction) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); + Location location = parameter_visitor_.GetNextLocation(instruction->GetType()); + if (location.IsStackSlot()) { + location = Location::StackSlot(location.GetStackIndex() + codegen_->GetFrameSize()); + } else if (location.IsDoubleStackSlot()) { + location = Location::DoubleStackSlot(location.GetStackIndex() + codegen_->GetFrameSize()); + } + locations->SetOut(location); +} + +void InstructionCodeGeneratorARM64::VisitParameterValue(HParameterValue* instruction) { + // Nothing to do, the parameter is already at its location. +} + +void LocationsBuilderARM64::VisitPhi(HPhi* instruction) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); + for (size_t i = 0, e = instruction->InputCount(); i < e; ++i) { + locations->SetInAt(i, Location::Any()); + } + locations->SetOut(Location::Any()); +} + +void InstructionCodeGeneratorARM64::VisitPhi(HPhi* instruction) { + LOG(FATAL) << "Unreachable"; +} + +void LocationsBuilderARM64::VisitReturn(HReturn* instruction) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); + Primitive::Type return_type = instruction->InputAt(0)->GetType(); + + if (return_type == Primitive::kPrimFloat || return_type == Primitive::kPrimDouble) { + LOG(FATAL) << "Unimplemented return type " << return_type; + } + + locations->SetInAt(0, LocationFrom(x0)); +} + +void InstructionCodeGeneratorARM64::VisitReturn(HReturn* instruction) { + if (kIsDebugBuild) { + Primitive::Type type = instruction->InputAt(0)->GetType(); + switch (type) { + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimChar: + case Primitive::kPrimShort: + case Primitive::kPrimInt: + case Primitive::kPrimNot: + DCHECK(InputRegisterAt(instruction, 0).Is(w0)); + break; + + case Primitive::kPrimLong: + DCHECK(InputRegisterAt(instruction, 0).Is(x0)); + break; + + default: + LOG(FATAL) << "Unimplemented return type " << type; + } + } + codegen_->GenerateFrameExit(); + __ Br(lr); +} + +void LocationsBuilderARM64::VisitReturnVoid(HReturnVoid* instruction) { + instruction->SetLocations(nullptr); +} + +void InstructionCodeGeneratorARM64::VisitReturnVoid(HReturnVoid* instruction) { + codegen_->GenerateFrameExit(); + __ Br(lr); +} + +void LocationsBuilderARM64::VisitStoreLocal(HStoreLocal* store) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(store); + Primitive::Type field_type = store->InputAt(1)->GetType(); + switch (field_type) { + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimChar: + case Primitive::kPrimShort: + case Primitive::kPrimInt: + case Primitive::kPrimNot: + locations->SetInAt(1, Location::StackSlot(codegen_->GetStackSlot(store->GetLocal()))); + break; + + case Primitive::kPrimLong: + locations->SetInAt(1, Location::DoubleStackSlot(codegen_->GetStackSlot(store->GetLocal()))); + break; + + default: + LOG(FATAL) << "Unimplemented local type " << field_type; + } +} + +void InstructionCodeGeneratorARM64::VisitStoreLocal(HStoreLocal* store) { +} + +void LocationsBuilderARM64::VisitSub(HSub* instruction) { + HandleAddSub(instruction); +} + +void InstructionCodeGeneratorARM64::VisitSub(HSub* instruction) { + HandleAddSub(instruction); +} + +void LocationsBuilderARM64::VisitBoundsCheck(HBoundsCheck* instruction) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + if (instruction->HasUses()) { + locations->SetOut(Location::SameAsFirstInput()); + } +} + +void InstructionCodeGeneratorARM64::VisitBoundsCheck(HBoundsCheck* instruction) { + LocationSummary* locations = instruction->GetLocations(); + BoundsCheckSlowPathARM64* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathARM64( + instruction, locations->InAt(0), locations->InAt(1)); + codegen_->AddSlowPath(slow_path); + + __ Cmp(InputRegisterAt(instruction, 0), InputOperandAt(instruction, 1)); + __ B(slow_path->GetEntryLabel(), hs); +} + +void LocationsBuilderARM64::VisitSuspendCheck(HSuspendCheck* instruction) { + new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnSlowPath); +} + +void InstructionCodeGeneratorARM64::VisitSuspendCheck(HSuspendCheck* instruction) { + // TODO: Improve support for suspend checks. + SuspendCheckSlowPathARM64* slow_path = + new (GetGraph()->GetArena()) SuspendCheckSlowPathARM64(instruction, nullptr); + codegen_->AddSlowPath(slow_path); + + __ Subs(wSuspend, wSuspend, 1); + __ B(slow_path->GetEntryLabel(), le); + __ Bind(slow_path->GetReturnLabel()); +} + +void LocationsBuilderARM64::VisitTemporary(HTemporary* temp) { + temp->SetLocations(nullptr); +} + +void InstructionCodeGeneratorARM64::VisitTemporary(HTemporary* temp) { + // Nothing to do, this is driven by the code generator. +} + +} // namespace arm64 +} // namespace art diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h new file mode 100644 index 0000000000..a4003ffea5 --- /dev/null +++ b/compiler/optimizing/code_generator_arm64.h @@ -0,0 +1,236 @@ +/* + * 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_CODE_GENERATOR_ARM64_H_ +#define ART_COMPILER_OPTIMIZING_CODE_GENERATOR_ARM64_H_ + +#include "code_generator.h" +#include "nodes.h" +#include "parallel_move_resolver.h" +#include "utils/arm64/assembler_arm64.h" +#include "a64/disasm-a64.h" +#include "a64/macro-assembler-a64.h" +#include "arch/arm64/quick_method_frame_info_arm64.h" + +namespace art { +namespace arm64 { + +class CodeGeneratorARM64; + +static constexpr size_t kArm64WordSize = 8; +static const vixl::Register kParameterCoreRegisters[] = { + vixl::x1, vixl::x2, vixl::x3, vixl::x4, vixl::x5, vixl::x6, vixl::x7 +}; +static constexpr size_t kParameterCoreRegistersLength = arraysize(kParameterCoreRegisters); +static const vixl::FPRegister kParameterFPRegisters[] = { + vixl::d0, vixl::d1, vixl::d2, vixl::d3, vixl::d4, vixl::d5, vixl::d6, vixl::d7 +}; +static constexpr size_t kParameterFPRegistersLength = arraysize(kParameterFPRegisters); + +const vixl::Register tr = vixl::x18; // Thread Register +const vixl::Register wSuspend = vixl::w19; // Suspend Register +const vixl::Register xSuspend = vixl::x19; + +const vixl::CPURegList vixl_reserved_core_registers(vixl::ip0, vixl::ip1); +const vixl::CPURegList runtime_reserved_core_registers(tr, xSuspend, vixl::lr); +const vixl::CPURegList quick_callee_saved_registers(vixl::CPURegister::kRegister, + vixl::kXRegSize, + kArm64CalleeSaveRefSpills); + +class InvokeDexCallingConvention : public CallingConvention<vixl::Register, vixl::FPRegister> { + public: + InvokeDexCallingConvention() + : CallingConvention(kParameterCoreRegisters, + kParameterCoreRegistersLength, + kParameterFPRegisters, + kParameterFPRegistersLength) {} + + Location GetReturnLocation(Primitive::Type return_type) { + DCHECK_NE(return_type, Primitive::kPrimVoid); + if (return_type == Primitive::kPrimFloat || return_type == Primitive::kPrimDouble) { + LOG(FATAL) << "Unimplemented return type " << return_type; + } + return Location::RegisterLocation(X0); + } + + + private: + DISALLOW_COPY_AND_ASSIGN(InvokeDexCallingConvention); +}; + +class InvokeDexCallingConventionVisitor { + public: + InvokeDexCallingConventionVisitor() : gp_index_(0), stack_index_(0) {} + + Location GetNextLocation(Primitive::Type type); + Location GetReturnLocation(Primitive::Type return_type) { + return calling_convention.GetReturnLocation(return_type); + } + + private: + InvokeDexCallingConvention calling_convention; + // The current index for core registers. + uint32_t gp_index_; + // The current stack index. + uint32_t stack_index_; + + DISALLOW_COPY_AND_ASSIGN(InvokeDexCallingConventionVisitor); +}; + +class InstructionCodeGeneratorARM64 : public HGraphVisitor { + public: + InstructionCodeGeneratorARM64(HGraph* graph, CodeGeneratorARM64* codegen); + +#define DECLARE_VISIT_INSTRUCTION(name, super) \ + virtual void Visit##name(H##name* instr); + FOR_EACH_CONCRETE_INSTRUCTION(DECLARE_VISIT_INSTRUCTION) +#undef DECLARE_VISIT_INSTRUCTION + + void LoadCurrentMethod(XRegister reg); + + Arm64Assembler* GetAssembler() const { return assembler_; } + + private: + void HandleAddSub(HBinaryOperation* instr); + + Arm64Assembler* const assembler_; + CodeGeneratorARM64* const codegen_; + + DISALLOW_COPY_AND_ASSIGN(InstructionCodeGeneratorARM64); +}; + +class LocationsBuilderARM64 : public HGraphVisitor { + public: + explicit LocationsBuilderARM64(HGraph* graph, CodeGeneratorARM64* codegen) + : HGraphVisitor(graph), codegen_(codegen) {} + +#define DECLARE_VISIT_INSTRUCTION(name, super) \ + virtual void Visit##name(H##name* instr); + FOR_EACH_CONCRETE_INSTRUCTION(DECLARE_VISIT_INSTRUCTION) +#undef DECLARE_VISIT_INSTRUCTION + + private: + void HandleAddSub(HBinaryOperation* instr); + void HandleInvoke(HInvoke* instr); + + CodeGeneratorARM64* const codegen_; + InvokeDexCallingConventionVisitor parameter_visitor_; + + DISALLOW_COPY_AND_ASSIGN(LocationsBuilderARM64); +}; + +class CodeGeneratorARM64 : public CodeGenerator { + public: + explicit CodeGeneratorARM64(HGraph* graph); + virtual ~CodeGeneratorARM64() { } + + virtual void GenerateFrameEntry() OVERRIDE; + virtual void GenerateFrameExit() OVERRIDE; + + static const vixl::CPURegList& GetFramePreservedRegisters() { + static const vixl::CPURegList frame_preserved_regs = + vixl::CPURegList(vixl::CPURegister::kRegister, vixl::kXRegSize, vixl::lr.Bit()); + return frame_preserved_regs; + } + static int GetFramePreservedRegistersSize() { + return GetFramePreservedRegisters().TotalSizeInBytes(); + } + + virtual void Bind(HBasicBlock* block) OVERRIDE; + + vixl::Label* GetLabelOf(HBasicBlock* block) const { + return block_labels_ + block->GetBlockId(); + } + + virtual void Move(HInstruction* instruction, Location location, HInstruction* move_for) OVERRIDE; + + virtual size_t GetWordSize() const OVERRIDE { + return kArm64WordSize; + } + + virtual size_t FrameEntrySpillSize() const OVERRIDE; + + virtual HGraphVisitor* GetLocationBuilder() OVERRIDE { return &location_builder_; } + virtual HGraphVisitor* GetInstructionVisitor() OVERRIDE { return &instruction_visitor_; } + virtual Arm64Assembler* GetAssembler() OVERRIDE { return &assembler_; } + + // Emit a write barrier. + void MarkGCCard(vixl::Register object, vixl::Register value); + + // Register allocation. + + virtual void SetupBlockedRegisters() const OVERRIDE; + // AllocateFreeRegister() is only used when allocating registers locally + // during CompileBaseline(). + virtual Location AllocateFreeRegister(Primitive::Type type) const OVERRIDE; + + virtual Location GetStackLocation(HLoadLocal* load) const OVERRIDE; + + virtual size_t SaveCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE { + UNIMPLEMENTED(INFO) << "TODO: SaveCoreRegister"; + return 0; + } + + virtual size_t RestoreCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE { + UNIMPLEMENTED(INFO) << "TODO: RestoreCoreRegister"; + return 0; + } + + // The number of registers that can be allocated. The register allocator may + // decide to reserve and not use a few of them. + // We do not consider registers sp, xzr, wzr. They are either not allocatable + // (xzr, wzr), or make for poor allocatable registers (sp alignment + // requirements, etc.). This also facilitates our task as all other registers + // can easily be mapped via to or from their type and index or code. + static const int kNumberOfAllocatableCoreRegisters = vixl::kNumberOfRegisters - 1; + static const int kNumberOfAllocatableFloatingPointRegisters = vixl::kNumberOfFPRegisters; + static const int kNumberOfAllocatableRegisters = + kNumberOfAllocatableCoreRegisters + kNumberOfAllocatableFloatingPointRegisters; + static constexpr int kNumberOfAllocatableRegisterPairs = 0; + + virtual void DumpCoreRegister(std::ostream& stream, int reg) const OVERRIDE; + virtual void DumpFloatingPointRegister(std::ostream& stream, int reg) const OVERRIDE; + + virtual InstructionSet GetInstructionSet() const OVERRIDE { + return InstructionSet::kArm64; + } + + void MoveHelper(Location destination, Location source, Primitive::Type type); + + virtual void Initialize() OVERRIDE { + HGraph* graph = GetGraph(); + int length = graph->GetBlocks().Size(); + block_labels_ = graph->GetArena()->AllocArray<vixl::Label>(length); + for (int i = 0; i < length; ++i) { + new(block_labels_ + i) vixl::Label(); + } + } + + private: + // Labels for each block that will be compiled. + vixl::Label* block_labels_; + + LocationsBuilderARM64 location_builder_; + InstructionCodeGeneratorARM64 instruction_visitor_; + Arm64Assembler assembler_; + + DISALLOW_COPY_AND_ASSIGN(CodeGeneratorARM64); +}; + +} // namespace arm64 +} // namespace art + +#endif // ART_COMPILER_OPTIMIZING_CODE_GENERATOR_ARM64_H_ diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 73143d6ac9..61f0750c5c 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -56,7 +56,21 @@ class InvokeRuntimeCallingConvention : public CallingConvention<Register, XmmReg #define __ reinterpret_cast<X86Assembler*>(codegen->GetAssembler())-> -class NullCheckSlowPathX86 : public SlowPathCode { +class SlowPathCodeX86 : public SlowPathCode { + public: + SlowPathCodeX86() : entry_label_(), exit_label_() {} + + Label* GetEntryLabel() { return &entry_label_; } + Label* GetExitLabel() { return &exit_label_; } + + private: + Label entry_label_; + Label exit_label_; + + DISALLOW_COPY_AND_ASSIGN(SlowPathCodeX86); +}; + +class NullCheckSlowPathX86 : public SlowPathCodeX86 { public: explicit NullCheckSlowPathX86(HNullCheck* instruction) : instruction_(instruction) {} @@ -71,7 +85,7 @@ class NullCheckSlowPathX86 : public SlowPathCode { DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathX86); }; -class StackOverflowCheckSlowPathX86 : public SlowPathCode { +class StackOverflowCheckSlowPathX86 : public SlowPathCodeX86 { public: StackOverflowCheckSlowPathX86() {} @@ -86,7 +100,7 @@ class StackOverflowCheckSlowPathX86 : public SlowPathCode { DISALLOW_COPY_AND_ASSIGN(StackOverflowCheckSlowPathX86); }; -class BoundsCheckSlowPathX86 : public SlowPathCode { +class BoundsCheckSlowPathX86 : public SlowPathCodeX86 { public: BoundsCheckSlowPathX86(HBoundsCheck* instruction, Location index_location, @@ -94,7 +108,7 @@ class BoundsCheckSlowPathX86 : public SlowPathCode { : instruction_(instruction), index_location_(index_location), length_location_(length_location) {} virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { - CodeGeneratorX86* x86_codegen = reinterpret_cast<CodeGeneratorX86*>(codegen); + CodeGeneratorX86* x86_codegen = down_cast<CodeGeneratorX86*>(codegen); __ Bind(GetEntryLabel()); InvokeRuntimeCallingConvention calling_convention; x86_codegen->Move32(Location::RegisterLocation(calling_convention.GetRegisterAt(0)), index_location_); @@ -111,12 +125,13 @@ class BoundsCheckSlowPathX86 : public SlowPathCode { DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathX86); }; -class SuspendCheckSlowPathX86 : public SlowPathCode { +class SuspendCheckSlowPathX86 : public SlowPathCodeX86 { public: explicit SuspendCheckSlowPathX86(HSuspendCheck* instruction, HBasicBlock* successor) : instruction_(instruction), successor_(successor) {} virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + CodeGeneratorX86* x86_codegen = down_cast<CodeGeneratorX86*>(codegen); __ Bind(GetEntryLabel()); codegen->SaveLiveRegisters(instruction_->GetLocations()); __ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pTestSuspend))); @@ -125,7 +140,7 @@ class SuspendCheckSlowPathX86 : public SlowPathCode { if (successor_ == nullptr) { __ jmp(GetReturnLabel()); } else { - __ jmp(codegen->GetLabelOf(successor_)); + __ jmp(x86_codegen->GetLabelOf(successor_)); } } @@ -167,16 +182,19 @@ void CodeGeneratorX86::DumpFloatingPointRegister(std::ostream& stream, int reg) stream << X86ManagedRegister::FromXmmRegister(XmmRegister(reg)); } -void CodeGeneratorX86::SaveCoreRegister(Location stack_location, uint32_t reg_id) { - __ movl(Address(ESP, stack_location.GetStackIndex()), static_cast<Register>(reg_id)); +size_t CodeGeneratorX86::SaveCoreRegister(size_t stack_index, uint32_t reg_id) { + __ movl(Address(ESP, stack_index), static_cast<Register>(reg_id)); + return kX86WordSize; } -void CodeGeneratorX86::RestoreCoreRegister(Location stack_location, uint32_t reg_id) { - __ movl(static_cast<Register>(reg_id), Address(ESP, stack_location.GetStackIndex())); +size_t CodeGeneratorX86::RestoreCoreRegister(size_t stack_index, uint32_t reg_id) { + __ movl(static_cast<Register>(reg_id), Address(ESP, stack_index)); + return kX86WordSize; } CodeGeneratorX86::CodeGeneratorX86(HGraph* graph) : CodeGenerator(graph, kNumberOfCpuRegisters, kNumberOfXmmRegisters, kNumberOfRegisterPairs), + block_labels_(graph->GetArena(), 0), location_builder_(graph, this), instruction_visitor_(graph, this), move_resolver_(graph->GetArena(), this) {} @@ -191,19 +209,11 @@ Location CodeGeneratorX86::AllocateFreeRegister(Primitive::Type type) const { size_t reg = FindFreeEntry(blocked_register_pairs_, kNumberOfRegisterPairs); X86ManagedRegister pair = X86ManagedRegister::FromRegisterPair(static_cast<RegisterPair>(reg)); + DCHECK(!blocked_core_registers_[pair.AsRegisterPairLow()]); + DCHECK(!blocked_core_registers_[pair.AsRegisterPairHigh()]); blocked_core_registers_[pair.AsRegisterPairLow()] = true; blocked_core_registers_[pair.AsRegisterPairHigh()] = true; - // Block all other register pairs that share a register with `pair`. - for (int i = 0; i < kNumberOfRegisterPairs; i++) { - X86ManagedRegister current = - X86ManagedRegister::FromRegisterPair(static_cast<RegisterPair>(i)); - if (current.AsRegisterPairLow() == pair.AsRegisterPairLow() - || current.AsRegisterPairLow() == pair.AsRegisterPairHigh() - || current.AsRegisterPairHigh() == pair.AsRegisterPairLow() - || current.AsRegisterPairHigh() == pair.AsRegisterPairHigh()) { - blocked_register_pairs_[i] = true; - } - } + UpdateBlockedPairRegisters(); return Location::RegisterPairLocation(pair.AsRegisterPairLow(), pair.AsRegisterPairHigh()); } @@ -250,10 +260,19 @@ void CodeGeneratorX86::SetupBlockedRegisters() const { blocked_core_registers_[EBP] = true; blocked_core_registers_[ESI] = true; blocked_core_registers_[EDI] = true; - blocked_register_pairs_[EAX_EDI] = true; - blocked_register_pairs_[EDX_EDI] = true; - blocked_register_pairs_[ECX_EDI] = true; - blocked_register_pairs_[EBX_EDI] = true; + + UpdateBlockedPairRegisters(); +} + +void CodeGeneratorX86::UpdateBlockedPairRegisters() const { + for (int i = 0; i < kNumberOfRegisterPairs; i++) { + X86ManagedRegister current = + X86ManagedRegister::FromRegisterPair(static_cast<RegisterPair>(i)); + if (blocked_core_registers_[current.AsRegisterPairLow()] + || blocked_core_registers_[current.AsRegisterPairHigh()]) { + blocked_register_pairs_[i] = true; + } + } } InstructionCodeGeneratorX86::InstructionCodeGeneratorX86(HGraph* graph, CodeGeneratorX86* codegen) @@ -276,7 +295,7 @@ void CodeGeneratorX86::GenerateFrameEntry() { __ subl(ESP, Immediate(GetFrameSize() - kNumberOfPushedRegistersAtEntry * kX86WordSize)); if (!skip_overflow_check && kExplicitStackOverflowCheck) { - SlowPathCode* slow_path = new (GetGraph()->GetArena()) StackOverflowCheckSlowPathX86(); + SlowPathCodeX86* slow_path = new (GetGraph()->GetArena()) StackOverflowCheckSlowPathX86(); AddSlowPath(slow_path); __ fs()->cmpl(ESP, Address::Absolute(Thread::StackEndOffset<kX86WordSize>())); @@ -290,8 +309,8 @@ void CodeGeneratorX86::GenerateFrameExit() { __ addl(ESP, Immediate(GetFrameSize() - kNumberOfPushedRegistersAtEntry * kX86WordSize)); } -void CodeGeneratorX86::Bind(Label* label) { - __ Bind(label); +void CodeGeneratorX86::Bind(HBasicBlock* block) { + __ Bind(GetLabelOf(block)); } void InstructionCodeGeneratorX86::LoadCurrentMethod(Register reg) { @@ -468,23 +487,29 @@ void CodeGeneratorX86::Move64(Location destination, Location source) { } void CodeGeneratorX86::Move(HInstruction* instruction, Location location, HInstruction* move_for) { - if (instruction->AsIntConstant() != nullptr) { + if (instruction->IsIntConstant()) { Immediate imm(instruction->AsIntConstant()->GetValue()); if (location.IsRegister()) { __ movl(location.As<Register>(), imm); - } else { + } else if (location.IsStackSlot()) { __ movl(Address(ESP, location.GetStackIndex()), imm); + } else { + DCHECK(location.IsConstant()); + DCHECK_EQ(location.GetConstant(), instruction); } - } else if (instruction->AsLongConstant() != nullptr) { + } else if (instruction->IsLongConstant()) { int64_t value = instruction->AsLongConstant()->GetValue(); if (location.IsRegister()) { __ movl(location.AsRegisterPairLow<Register>(), Immediate(Low32Bits(value))); __ movl(location.AsRegisterPairHigh<Register>(), Immediate(High32Bits(value))); - } else { + } else if (location.IsDoubleStackSlot()) { __ movl(Address(ESP, location.GetStackIndex()), Immediate(Low32Bits(value))); __ movl(Address(ESP, location.GetHighStackIndex(kX86WordSize)), Immediate(High32Bits(value))); + } else { + DCHECK(location.IsConstant()); + DCHECK_EQ(location.GetConstant(), instruction); } - } else if (instruction->AsLoadLocal() != nullptr) { + } else if (instruction->IsLoadLocal()) { int slot = GetStackSlot(instruction->AsLoadLocal()->GetLocal()); switch (instruction->GetType()) { case Primitive::kPrimBoolean: @@ -571,48 +596,66 @@ void LocationsBuilderX86::VisitIf(HIf* if_instr) { new (GetGraph()->GetArena()) LocationSummary(if_instr, LocationSummary::kNoCall); HInstruction* cond = if_instr->InputAt(0); if (!cond->IsCondition() || cond->AsCondition()->NeedsMaterialization()) { - locations->SetInAt(0, Location::Any(), Location::kDiesAtEntry); + locations->SetInAt(0, Location::Any()); } } void InstructionCodeGeneratorX86::VisitIf(HIf* if_instr) { HInstruction* cond = if_instr->InputAt(0); - bool materialized = !cond->IsCondition() || cond->AsCondition()->NeedsMaterialization(); - // Moves do not affect the eflags register, so if the condition is evaluated - // just before the if, we don't need to evaluate it again. - bool eflags_set = cond->IsCondition() - && cond->AsCondition()->IsBeforeWhenDisregardMoves(if_instr); - if (materialized) { - if (!eflags_set) { - // Materialized condition, compare against 0. - Location lhs = if_instr->GetLocations()->InAt(0); - if (lhs.IsRegister()) { - __ cmpl(lhs.As<Register>(), Immediate(0)); - } else { - __ cmpl(Address(ESP, lhs.GetStackIndex()), Immediate(0)); + if (cond->IsIntConstant()) { + // Constant condition, statically compared against 1. + int32_t cond_value = cond->AsIntConstant()->GetValue(); + if (cond_value == 1) { + if (!codegen_->GoesToNextBlock(if_instr->GetBlock(), + if_instr->IfTrueSuccessor())) { + __ jmp(codegen_->GetLabelOf(if_instr->IfTrueSuccessor())); } - __ j(kNotEqual, codegen_->GetLabelOf(if_instr->IfTrueSuccessor())); + return; } else { - __ j(X86Condition(cond->AsCondition()->GetCondition()), - codegen_->GetLabelOf(if_instr->IfTrueSuccessor())); + DCHECK_EQ(cond_value, 0); } } else { - Location lhs = cond->GetLocations()->InAt(0); - Location rhs = cond->GetLocations()->InAt(1); - // LHS is guaranteed to be in a register (see LocationsBuilderX86::VisitCondition). - if (rhs.IsRegister()) { - __ cmpl(lhs.As<Register>(), rhs.As<Register>()); - } else if (rhs.IsConstant()) { - HIntConstant* instruction = rhs.GetConstant()->AsIntConstant(); - Immediate imm(instruction->AsIntConstant()->GetValue()); - __ cmpl(lhs.As<Register>(), imm); + bool materialized = + !cond->IsCondition() || cond->AsCondition()->NeedsMaterialization(); + // Moves do not affect the eflags register, so if the condition is + // evaluated just before the if, we don't need to evaluate it + // again. + bool eflags_set = cond->IsCondition() + && cond->AsCondition()->IsBeforeWhenDisregardMoves(if_instr); + if (materialized) { + if (!eflags_set) { + // Materialized condition, compare against 0. + Location lhs = if_instr->GetLocations()->InAt(0); + if (lhs.IsRegister()) { + __ cmpl(lhs.As<Register>(), Immediate(0)); + } else { + __ cmpl(Address(ESP, lhs.GetStackIndex()), Immediate(0)); + } + __ j(kNotEqual, codegen_->GetLabelOf(if_instr->IfTrueSuccessor())); + } else { + __ j(X86Condition(cond->AsCondition()->GetCondition()), + codegen_->GetLabelOf(if_instr->IfTrueSuccessor())); + } } else { - __ cmpl(lhs.As<Register>(), Address(ESP, rhs.GetStackIndex())); + Location lhs = cond->GetLocations()->InAt(0); + Location rhs = cond->GetLocations()->InAt(1); + // LHS is guaranteed to be in a register (see + // LocationsBuilderX86::VisitCondition). + if (rhs.IsRegister()) { + __ cmpl(lhs.As<Register>(), rhs.As<Register>()); + } else if (rhs.IsConstant()) { + HIntConstant* instruction = rhs.GetConstant()->AsIntConstant(); + Immediate imm(instruction->AsIntConstant()->GetValue()); + __ cmpl(lhs.As<Register>(), imm); + } else { + __ cmpl(lhs.As<Register>(), Address(ESP, rhs.GetStackIndex())); + } + __ j(X86Condition(cond->AsCondition()->GetCondition()), + codegen_->GetLabelOf(if_instr->IfTrueSuccessor())); } - __ j(X86Condition(cond->AsCondition()->GetCondition()), - codegen_->GetLabelOf(if_instr->IfTrueSuccessor())); } - if (!codegen_->GoesToNextBlock(if_instr->GetBlock(), if_instr->IfFalseSuccessor())) { + if (!codegen_->GoesToNextBlock(if_instr->GetBlock(), + if_instr->IfFalseSuccessor())) { __ jmp(codegen_->GetLabelOf(if_instr->IfFalseSuccessor())); } } @@ -664,8 +707,8 @@ void InstructionCodeGeneratorX86::VisitStoreLocal(HStoreLocal* store) { void LocationsBuilderX86::VisitCondition(HCondition* comp) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(comp, LocationSummary::kNoCall); - locations->SetInAt(0, Location::RequiresRegister(), Location::kDiesAtEntry); - locations->SetInAt(1, Location::Any(), Location::kDiesAtEntry); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::Any()); if (comp->NeedsMaterialization()) { locations->SetOut(Location::RequiresRegister()); } @@ -747,6 +790,7 @@ void LocationsBuilderX86::VisitIntConstant(HIntConstant* constant) { } void InstructionCodeGeneratorX86::VisitIntConstant(HIntConstant* constant) { + // Will be generated at use site. } void LocationsBuilderX86::VisitLongConstant(HLongConstant* constant) { @@ -759,6 +803,26 @@ void InstructionCodeGeneratorX86::VisitLongConstant(HLongConstant* constant) { // Will be generated at use site. } +void LocationsBuilderX86::VisitFloatConstant(HFloatConstant* constant) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall); + locations->SetOut(Location::ConstantLocation(constant)); +} + +void InstructionCodeGeneratorX86::VisitFloatConstant(HFloatConstant* constant) { + // Will be generated at use site. +} + +void LocationsBuilderX86::VisitDoubleConstant(HDoubleConstant* constant) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall); + locations->SetOut(Location::ConstantLocation(constant)); +} + +void InstructionCodeGeneratorX86::VisitDoubleConstant(HDoubleConstant* constant) { + // Will be generated at use site. +} + void LocationsBuilderX86::VisitReturnVoid(HReturnVoid* ret) { ret->SetLocations(nullptr); } @@ -921,6 +985,47 @@ void InstructionCodeGeneratorX86::VisitInvokeVirtual(HInvokeVirtual* invoke) { codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); } +void LocationsBuilderX86::VisitNeg(HNeg* neg) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(neg, LocationSummary::kNoCall); + switch (neg->GetResultType()) { + case Primitive::kPrimInt: + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::SameAsFirstInput()); + break; + + case Primitive::kPrimLong: + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + LOG(FATAL) << "Not yet implemented neg type " << neg->GetResultType(); + break; + + default: + LOG(FATAL) << "Unexpected neg type " << neg->GetResultType(); + } +} + +void InstructionCodeGeneratorX86::VisitNeg(HNeg* neg) { + LocationSummary* locations = neg->GetLocations(); + Location out = locations->Out(); + Location in = locations->InAt(0); + switch (neg->GetResultType()) { + case Primitive::kPrimInt: + DCHECK(in.IsRegister()); + __ negl(out.As<Register>()); + break; + + case Primitive::kPrimLong: + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + LOG(FATAL) << "Not yet implemented neg type " << neg->GetResultType(); + break; + + default: + LOG(FATAL) << "Unexpected neg type " << neg->GetResultType(); + } +} + void LocationsBuilderX86::VisitAdd(HAdd* add) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(add, LocationSummary::kNoCall); @@ -951,16 +1056,13 @@ void InstructionCodeGeneratorX86::VisitAdd(HAdd* add) { LocationSummary* locations = add->GetLocations(); Location first = locations->InAt(0); Location second = locations->InAt(1); - + DCHECK(first.Equals(locations->Out())); switch (add->GetResultType()) { case Primitive::kPrimInt: { - DCHECK_EQ(first.As<Register>(), locations->Out().As<Register>()); if (second.IsRegister()) { __ addl(first.As<Register>(), second.As<Register>()); } else if (second.IsConstant()) { - HConstant* instruction = second.GetConstant(); - Immediate imm(instruction->AsIntConstant()->GetValue()); - __ addl(first.As<Register>(), imm); + __ addl(first.As<Register>(), Immediate(second.GetConstant()->AsIntConstant()->GetValue())); } else { __ addl(first.As<Register>(), Address(ESP, second.GetStackIndex())); } @@ -968,10 +1070,6 @@ void InstructionCodeGeneratorX86::VisitAdd(HAdd* add) { } case Primitive::kPrimLong: { - DCHECK_EQ(first.AsRegisterPairLow<Register>(), - locations->Out().AsRegisterPairLow<Register>()); - DCHECK_EQ(first.AsRegisterPairHigh<Register>(), - locations->Out().AsRegisterPairHigh<Register>()); if (second.IsRegister()) { __ addl(first.AsRegisterPairLow<Register>(), second.AsRegisterPairLow<Register>()); __ adcl(first.AsRegisterPairHigh<Register>(), second.AsRegisterPairHigh<Register>()); @@ -1017,16 +1115,16 @@ void LocationsBuilderX86::VisitSub(HSub* sub) { locations->SetOut(Location::SameAsFirstInput()); break; } - - case Primitive::kPrimBoolean: - case Primitive::kPrimByte: - case Primitive::kPrimChar: - case Primitive::kPrimShort: - LOG(FATAL) << "Unexpected sub type " << sub->GetResultType(); + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: { + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetInAt(1, Location::RequiresFpuRegister()); + locations->SetOut(Location::SameAsFirstInput()); break; + } default: - LOG(FATAL) << "Unimplemented sub type " << sub->GetResultType(); + LOG(FATAL) << "Unexpected sub type " << sub->GetResultType(); } } @@ -1034,52 +1132,153 @@ void InstructionCodeGeneratorX86::VisitSub(HSub* sub) { LocationSummary* locations = sub->GetLocations(); Location first = locations->InAt(0); Location second = locations->InAt(1); + DCHECK(first.Equals(locations->Out())); switch (sub->GetResultType()) { case Primitive::kPrimInt: { - DCHECK_EQ(first.As<Register>(), - locations->Out().As<Register>()); if (second.IsRegister()) { - __ subl(first.As<Register>(), - second.As<Register>()); + __ subl(first.As<Register>(), second.As<Register>()); } else if (second.IsConstant()) { - HConstant* instruction = second.GetConstant(); - Immediate imm(instruction->AsIntConstant()->GetValue()); - __ subl(first.As<Register>(), imm); + __ subl(first.As<Register>(), Immediate(second.GetConstant()->AsIntConstant()->GetValue())); } else { - __ subl(first.As<Register>(), - Address(ESP, second.GetStackIndex())); + __ subl(first.As<Register>(), Address(ESP, second.GetStackIndex())); } break; } case Primitive::kPrimLong: { - DCHECK_EQ(first.AsRegisterPairLow<Register>(), - locations->Out().AsRegisterPairLow<Register>()); - DCHECK_EQ(first.AsRegisterPairHigh<Register>(), - locations->Out().AsRegisterPairHigh<Register>()); if (second.IsRegister()) { - __ subl(first.AsRegisterPairLow<Register>(), - second.AsRegisterPairLow<Register>()); - __ sbbl(first.AsRegisterPairHigh<Register>(), - second.AsRegisterPairHigh<Register>()); + __ subl(first.AsRegisterPairLow<Register>(), second.AsRegisterPairLow<Register>()); + __ sbbl(first.AsRegisterPairHigh<Register>(), second.AsRegisterPairHigh<Register>()); } else { - __ subl(first.AsRegisterPairLow<Register>(), - Address(ESP, second.GetStackIndex())); + __ subl(first.AsRegisterPairLow<Register>(), Address(ESP, second.GetStackIndex())); __ sbbl(first.AsRegisterPairHigh<Register>(), Address(ESP, second.GetHighStackIndex(kX86WordSize))); } break; } - case Primitive::kPrimBoolean: - case Primitive::kPrimByte: - case Primitive::kPrimChar: - case Primitive::kPrimShort: + case Primitive::kPrimFloat: { + __ subss(first.As<XmmRegister>(), second.As<XmmRegister>()); + break; + } + + case Primitive::kPrimDouble: { + __ subsd(first.As<XmmRegister>(), second.As<XmmRegister>()); + break; + } + + default: LOG(FATAL) << "Unexpected sub type " << sub->GetResultType(); + } +} + +void LocationsBuilderX86::VisitMul(HMul* mul) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(mul, LocationSummary::kNoCall); + switch (mul->GetResultType()) { + 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()); + // TODO: Currently this handles only stack operands: + // - we don't have enough registers because we currently use Quick ABI. + // - by the time we have a working register allocator we will probably change the ABI + // and fix the above. + // - we don't have a way yet to request operands on stack but the base line compiler + // will leave the operands on the stack with Any(). + locations->SetInAt(1, Location::Any()); + locations->SetOut(Location::SameAsFirstInput()); + // Needed for imul on 32bits with 64bits output. + locations->AddTemp(Location::RegisterLocation(EAX)); + locations->AddTemp(Location::RegisterLocation(EDX)); + break; + } + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: { + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetInAt(1, Location::RequiresFpuRegister()); + locations->SetOut(Location::SameAsFirstInput()); + break; + } + + default: + LOG(FATAL) << "Unexpected mul type " << mul->GetResultType(); + } +} + +void InstructionCodeGeneratorX86::VisitMul(HMul* mul) { + LocationSummary* locations = mul->GetLocations(); + Location first = locations->InAt(0); + Location second = locations->InAt(1); + DCHECK(first.Equals(locations->Out())); + + switch (mul->GetResultType()) { + case Primitive::kPrimInt: { + if (second.IsRegister()) { + __ imull(first.As<Register>(), second.As<Register>()); + } else if (second.IsConstant()) { + Immediate imm(second.GetConstant()->AsIntConstant()->GetValue()); + __ imull(first.As<Register>(), imm); + } else { + DCHECK(second.IsStackSlot()); + __ imull(first.As<Register>(), Address(ESP, second.GetStackIndex())); + } + break; + } + + case Primitive::kPrimLong: { + DCHECK(second.IsDoubleStackSlot()); + + Register in1_hi = first.AsRegisterPairHigh<Register>(); + Register in1_lo = first.AsRegisterPairLow<Register>(); + Address in2_hi(ESP, second.GetHighStackIndex(kX86WordSize)); + Address in2_lo(ESP, second.GetStackIndex()); + Register eax = locations->GetTemp(0).As<Register>(); + Register edx = locations->GetTemp(1).As<Register>(); + + DCHECK_EQ(EAX, eax); + DCHECK_EQ(EDX, edx); + + // input: in1 - 64 bits, in2 - 64 bits + // output: in1 + // formula: in1.hi : in1.lo = (in1.lo * in2.hi + in1.hi * in2.lo)* 2^32 + in1.lo * in2.lo + // parts: in1.hi = in1.lo * in2.hi + in1.hi * in2.lo + (in1.lo * in2.lo)[63:32] + // parts: in1.lo = (in1.lo * in2.lo)[31:0] + + __ movl(eax, in2_hi); + // eax <- in1.lo * in2.hi + __ imull(eax, in1_lo); + // in1.hi <- in1.hi * in2.lo + __ imull(in1_hi, in2_lo); + // in1.hi <- in1.lo * in2.hi + in1.hi * in2.lo + __ addl(in1_hi, eax); + // move in1_lo to eax to prepare for double precision + __ movl(eax, in1_lo); + // edx:eax <- in1.lo * in2.lo + __ mull(in2_lo); + // in1.hi <- in2.hi * in1.lo + in2.lo * in1.hi + (in1.lo * in2.lo)[63:32] + __ addl(in1_hi, edx); + // in1.lo <- (in1.lo * in2.lo)[31:0]; + __ movl(in1_lo, eax); + + break; + } + + case Primitive::kPrimFloat: { + __ mulss(first.As<XmmRegister>(), second.As<XmmRegister>()); + break; + } + + case Primitive::kPrimDouble: { + __ mulsd(first.As<XmmRegister>(), second.As<XmmRegister>()); break; + } default: - LOG(FATAL) << "Unimplemented sub type " << sub->GetResultType(); + LOG(FATAL) << "Unexpected mul type " << mul->GetResultType(); } } @@ -1104,6 +1303,28 @@ void InstructionCodeGeneratorX86::VisitNewInstance(HNewInstance* instruction) { DCHECK(!codegen_->IsLeafMethod()); } +void LocationsBuilderX86::VisitNewArray(HNewArray* instruction) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall); + locations->SetOut(Location::RegisterLocation(EAX)); + InvokeRuntimeCallingConvention calling_convention; + locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(1))); + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(2))); +} + +void InstructionCodeGeneratorX86::VisitNewArray(HNewArray* instruction) { + InvokeRuntimeCallingConvention calling_convention; + LoadCurrentMethod(calling_convention.GetRegisterAt(1)); + __ movl(calling_convention.GetRegisterAt(0), Immediate(instruction->GetTypeIndex())); + + __ fs()->call( + Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pAllocArrayWithAccessCheck))); + + codegen_->RecordPcInfo(instruction, instruction->GetDexPc()); + DCHECK(!codegen_->IsLeafMethod()); +} + void LocationsBuilderX86::VisitParameterValue(HParameterValue* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); @@ -1119,26 +1340,42 @@ void LocationsBuilderX86::VisitParameterValue(HParameterValue* instruction) { void InstructionCodeGeneratorX86::VisitParameterValue(HParameterValue* instruction) { } -void LocationsBuilderX86::VisitNot(HNot* instruction) { +void LocationsBuilderX86::VisitNot(HNot* not_) { LocationSummary* locations = - new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + new (GetGraph()->GetArena()) LocationSummary(not_, LocationSummary::kNoCall); locations->SetInAt(0, Location::RequiresRegister()); locations->SetOut(Location::SameAsFirstInput()); } -void InstructionCodeGeneratorX86::VisitNot(HNot* instruction) { - LocationSummary* locations = instruction->GetLocations(); +void InstructionCodeGeneratorX86::VisitNot(HNot* not_) { + LocationSummary* locations = not_->GetLocations(); + DCHECK_EQ(locations->InAt(0).As<Register>(), locations->Out().As<Register>()); Location out = locations->Out(); DCHECK_EQ(locations->InAt(0).As<Register>(), out.As<Register>()); - __ xorl(out.As<Register>(), Immediate(1)); + switch (not_->InputAt(0)->GetType()) { + case Primitive::kPrimBoolean: + __ xorl(out.As<Register>(), Immediate(1)); + break; + + case Primitive::kPrimInt: + __ notl(out.As<Register>()); + break; + + case Primitive::kPrimLong: + LOG(FATAL) << "Not yet implemented type for not operation " << not_->GetResultType(); + break; + + default: + LOG(FATAL) << "Unimplemented type for not operation " << not_->GetResultType(); + } } void LocationsBuilderX86::VisitCompare(HCompare* compare) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(compare, LocationSummary::kNoCall); - locations->SetInAt(0, Location::RequiresRegister(), Location::kDiesAtEntry); - locations->SetInAt(1, Location::Any(), Location::kDiesAtEntry); - locations->SetOut(Location::RequiresRegister()); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::Any()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); } void InstructionCodeGeneratorX86::VisitCompare(HCompare* compare) { @@ -1207,12 +1444,11 @@ void LocationsBuilderX86::VisitInstanceFieldSet(HInstanceFieldSet* instruction) || (field_type == Primitive::kPrimByte); // The register allocator does not support multiple // inputs that die at entry with one in a specific register. - bool dies_at_entry = !is_object_type && !is_byte_type; if (is_byte_type) { // Ensure the value is in a byte register. - locations->SetInAt(1, Location::RegisterLocation(EAX), dies_at_entry); + locations->SetInAt(1, Location::RegisterLocation(EAX)); } else { - locations->SetInAt(1, Location::RequiresRegister(), dies_at_entry); + locations->SetInAt(1, Location::RequiresRegister()); } // Temporary registers for the write barrier. if (is_object_type) { @@ -1288,8 +1524,8 @@ void CodeGeneratorX86::MarkGCCard(Register temp, Register card, Register object, void LocationsBuilderX86::VisitInstanceFieldGet(HInstanceFieldGet* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); - locations->SetInAt(0, Location::RequiresRegister(), Location::kDiesAtEntry); - locations->SetOut(Location::RequiresRegister()); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); } void InstructionCodeGeneratorX86::VisitInstanceFieldGet(HInstanceFieldGet* instruction) { @@ -1356,7 +1592,7 @@ void LocationsBuilderX86::VisitNullCheck(HNullCheck* instruction) { } void InstructionCodeGeneratorX86::VisitNullCheck(HNullCheck* instruction) { - SlowPathCode* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathX86(instruction); + SlowPathCodeX86* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathX86(instruction); codegen_->AddSlowPath(slow_path); LocationSummary* locations = instruction->GetLocations(); @@ -1378,10 +1614,9 @@ void InstructionCodeGeneratorX86::VisitNullCheck(HNullCheck* instruction) { void LocationsBuilderX86::VisitArrayGet(HArrayGet* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); - locations->SetInAt(0, Location::RequiresRegister(), Location::kDiesAtEntry); - locations->SetInAt( - 1, Location::RegisterOrConstant(instruction->InputAt(1)), Location::kDiesAtEntry); - locations->SetOut(Location::RequiresRegister()); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1))); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); } void InstructionCodeGeneratorX86::VisitArrayGet(HArrayGet* instruction) { @@ -1494,16 +1729,13 @@ void LocationsBuilderX86::VisitArraySet(HArraySet* instruction) { // We need the inputs to be different than the output in case of long operation. // In case of a byte operation, the register allocator does not support multiple // inputs that die at entry with one in a specific register. - bool dies_at_entry = value_type != Primitive::kPrimLong && !is_byte_type; - locations->SetInAt(0, Location::RequiresRegister(), dies_at_entry); - locations->SetInAt( - 1, Location::RegisterOrConstant(instruction->InputAt(1)), dies_at_entry); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1))); if (is_byte_type) { // Ensure the value is in a byte register. - locations->SetInAt(2, Location::ByteRegisterOrConstant( - EAX, instruction->InputAt(2)), dies_at_entry); + locations->SetInAt(2, Location::ByteRegisterOrConstant(EAX, instruction->InputAt(2))); } else { - locations->SetInAt(2, Location::RegisterOrConstant(instruction->InputAt(2)), dies_at_entry); + locations->SetInAt(2, Location::RegisterOrConstant(instruction->InputAt(2))); } } } @@ -1633,8 +1865,8 @@ void InstructionCodeGeneratorX86::VisitArraySet(HArraySet* instruction) { void LocationsBuilderX86::VisitArrayLength(HArrayLength* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); - locations->SetInAt(0, Location::RequiresRegister(), Location::kDiesAtEntry); - locations->SetOut(Location::RequiresRegister()); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); instruction->SetLocations(locations); } @@ -1658,7 +1890,7 @@ void LocationsBuilderX86::VisitBoundsCheck(HBoundsCheck* instruction) { void InstructionCodeGeneratorX86::VisitBoundsCheck(HBoundsCheck* instruction) { LocationSummary* locations = instruction->GetLocations(); - SlowPathCode* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathX86( + SlowPathCodeX86* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathX86( instruction, locations->InAt(0), locations->InAt(1)); codegen_->AddSlowPath(slow_path); diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h index a1a72a2bd7..fff91d179a 100644 --- a/compiler/optimizing/code_generator_x86.h +++ b/compiler/optimizing/code_generator_x86.h @@ -142,10 +142,10 @@ class CodeGeneratorX86 : public CodeGenerator { virtual void GenerateFrameEntry() OVERRIDE; virtual void GenerateFrameExit() OVERRIDE; - virtual void Bind(Label* label) OVERRIDE; + virtual void Bind(HBasicBlock* block) OVERRIDE; virtual void Move(HInstruction* instruction, Location location, HInstruction* move_for) OVERRIDE; - virtual void SaveCoreRegister(Location stack_location, uint32_t reg_id) OVERRIDE; - virtual void RestoreCoreRegister(Location stack_location, uint32_t reg_id) OVERRIDE; + virtual size_t SaveCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE; + virtual size_t RestoreCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE; virtual size_t GetWordSize() const OVERRIDE { return kX86WordSize; @@ -166,6 +166,7 @@ class CodeGeneratorX86 : public CodeGenerator { } virtual void SetupBlockedRegisters() const OVERRIDE; + virtual Location AllocateFreeRegister(Primitive::Type type) const OVERRIDE; virtual Location GetStackLocation(HLoadLocal* load) const OVERRIDE; @@ -173,6 +174,9 @@ class CodeGeneratorX86 : public CodeGenerator { virtual void DumpCoreRegister(std::ostream& stream, int reg) const OVERRIDE; virtual void DumpFloatingPointRegister(std::ostream& stream, int reg) const OVERRIDE; + // Blocks all register pairs made out of blocked core registers. + void UpdateBlockedPairRegisters() const; + ParallelMoveResolverX86* GetMoveResolver() { return &move_resolver_; } @@ -189,7 +193,17 @@ class CodeGeneratorX86 : public CodeGenerator { // Emit a write barrier. void MarkGCCard(Register temp, Register card, Register object, Register value); + Label* GetLabelOf(HBasicBlock* block) const { + return block_labels_.GetRawStorage() + block->GetBlockId(); + } + + virtual void Initialize() OVERRIDE { + block_labels_.SetSize(GetGraph()->GetBlocks().Size()); + } + private: + // Labels for each block that will be compiled. + GrowableArray<Label> block_labels_; 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 21b21f39d4..4a05b89892 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -60,7 +60,21 @@ class InvokeRuntimeCallingConvention : public CallingConvention<Register, FloatR #define __ reinterpret_cast<X86_64Assembler*>(codegen->GetAssembler())-> -class NullCheckSlowPathX86_64 : public SlowPathCode { +class SlowPathCodeX86_64 : public SlowPathCode { + public: + SlowPathCodeX86_64() : entry_label_(), exit_label_() {} + + Label* GetEntryLabel() { return &entry_label_; } + Label* GetExitLabel() { return &exit_label_; } + + private: + Label entry_label_; + Label exit_label_; + + DISALLOW_COPY_AND_ASSIGN(SlowPathCodeX86_64); +}; + +class NullCheckSlowPathX86_64 : public SlowPathCodeX86_64 { public: explicit NullCheckSlowPathX86_64(HNullCheck* instruction) : instruction_(instruction) {} @@ -76,7 +90,7 @@ class NullCheckSlowPathX86_64 : public SlowPathCode { DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathX86_64); }; -class StackOverflowCheckSlowPathX86_64 : public SlowPathCode { +class StackOverflowCheckSlowPathX86_64 : public SlowPathCodeX86_64 { public: StackOverflowCheckSlowPathX86_64() {} @@ -92,12 +106,13 @@ class StackOverflowCheckSlowPathX86_64 : public SlowPathCode { DISALLOW_COPY_AND_ASSIGN(StackOverflowCheckSlowPathX86_64); }; -class SuspendCheckSlowPathX86_64 : public SlowPathCode { +class SuspendCheckSlowPathX86_64 : public SlowPathCodeX86_64 { public: explicit SuspendCheckSlowPathX86_64(HSuspendCheck* instruction, HBasicBlock* successor) : instruction_(instruction), successor_(successor) {} virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + CodeGeneratorX86_64* x64_codegen = down_cast<CodeGeneratorX86_64*>(codegen); __ Bind(GetEntryLabel()); codegen->SaveLiveRegisters(instruction_->GetLocations()); __ gs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pTestSuspend), true)); @@ -106,7 +121,7 @@ class SuspendCheckSlowPathX86_64 : public SlowPathCode { if (successor_ == nullptr) { __ jmp(GetReturnLabel()); } else { - __ jmp(codegen->GetLabelOf(successor_)); + __ jmp(x64_codegen->GetLabelOf(successor_)); } } @@ -123,7 +138,7 @@ class SuspendCheckSlowPathX86_64 : public SlowPathCode { DISALLOW_COPY_AND_ASSIGN(SuspendCheckSlowPathX86_64); }; -class BoundsCheckSlowPathX86_64 : public SlowPathCode { +class BoundsCheckSlowPathX86_64 : public SlowPathCodeX86_64 { public: BoundsCheckSlowPathX86_64(HBoundsCheck* instruction, Location index_location, @@ -133,7 +148,7 @@ class BoundsCheckSlowPathX86_64 : public SlowPathCode { length_location_(length_location) {} virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { - CodeGeneratorX86_64* x64_codegen = reinterpret_cast<CodeGeneratorX86_64*>(codegen); + CodeGeneratorX86_64* x64_codegen = down_cast<CodeGeneratorX86_64*>(codegen); __ Bind(GetEntryLabel()); InvokeRuntimeCallingConvention calling_convention; x64_codegen->Move(Location::RegisterLocation(calling_convention.GetRegisterAt(0)), index_location_); @@ -176,16 +191,29 @@ void CodeGeneratorX86_64::DumpFloatingPointRegister(std::ostream& stream, int re stream << X86_64ManagedRegister::FromXmmRegister(FloatRegister(reg)); } -void CodeGeneratorX86_64::SaveCoreRegister(Location stack_location, uint32_t reg_id) { - __ movq(Address(CpuRegister(RSP), stack_location.GetStackIndex()), CpuRegister(reg_id)); +size_t CodeGeneratorX86_64::SaveCoreRegister(size_t stack_index, uint32_t reg_id) { + __ movq(Address(CpuRegister(RSP), stack_index), CpuRegister(reg_id)); + return kX86_64WordSize; +} + +size_t CodeGeneratorX86_64::RestoreCoreRegister(size_t stack_index, uint32_t reg_id) { + __ movq(CpuRegister(reg_id), Address(CpuRegister(RSP), stack_index)); + return kX86_64WordSize; } -void CodeGeneratorX86_64::RestoreCoreRegister(Location stack_location, uint32_t reg_id) { - __ movq(CpuRegister(reg_id), Address(CpuRegister(RSP), stack_location.GetStackIndex())); +size_t CodeGeneratorX86_64::SaveFloatingPointRegister(size_t stack_index, uint32_t reg_id) { + __ movsd(Address(CpuRegister(RSP), stack_index), XmmRegister(reg_id)); + return kX86_64WordSize; +} + +size_t CodeGeneratorX86_64::RestoreFloatingPointRegister(size_t stack_index, uint32_t reg_id) { + __ movsd(XmmRegister(reg_id), Address(CpuRegister(RSP), stack_index)); + return kX86_64WordSize; } CodeGeneratorX86_64::CodeGeneratorX86_64(HGraph* graph) : CodeGenerator(graph, kNumberOfCpuRegisters, kNumberOfFloatRegisters, 0), + block_labels_(graph->GetArena(), 0), location_builder_(graph, this), instruction_visitor_(graph, this), move_resolver_(graph->GetArena(), this) {} @@ -266,7 +294,7 @@ void CodeGeneratorX86_64::GenerateFrameEntry() { Immediate(GetFrameSize() - kNumberOfPushedRegistersAtEntry * kX86_64WordSize)); if (!skip_overflow_check && kExplicitStackOverflowCheck) { - SlowPathCode* slow_path = new (GetGraph()->GetArena()) StackOverflowCheckSlowPathX86_64(); + SlowPathCodeX86_64* slow_path = new (GetGraph()->GetArena()) StackOverflowCheckSlowPathX86_64(); AddSlowPath(slow_path); __ gs()->cmpq(CpuRegister(RSP), @@ -282,8 +310,8 @@ void CodeGeneratorX86_64::GenerateFrameExit() { Immediate(GetFrameSize() - kNumberOfPushedRegistersAtEntry * kX86_64WordSize)); } -void CodeGeneratorX86_64::Bind(Label* label) { - __ Bind(label); +void CodeGeneratorX86_64::Bind(HBasicBlock* block) { + __ Bind(GetLabelOf(block)); } void InstructionCodeGeneratorX86_64::LoadCurrentMethod(CpuRegister reg) { @@ -375,22 +403,28 @@ void CodeGeneratorX86_64::Move(Location destination, Location source) { void CodeGeneratorX86_64::Move(HInstruction* instruction, Location location, HInstruction* move_for) { - if (instruction->AsIntConstant() != nullptr) { + if (instruction->IsIntConstant()) { Immediate imm(instruction->AsIntConstant()->GetValue()); if (location.IsRegister()) { __ movl(location.As<CpuRegister>(), imm); - } else { + } else if (location.IsStackSlot()) { __ movl(Address(CpuRegister(RSP), location.GetStackIndex()), imm); + } else { + DCHECK(location.IsConstant()); + DCHECK_EQ(location.GetConstant(), instruction); } - } else if (instruction->AsLongConstant() != nullptr) { + } else if (instruction->IsLongConstant()) { int64_t value = instruction->AsLongConstant()->GetValue(); if (location.IsRegister()) { __ movq(location.As<CpuRegister>(), Immediate(value)); - } else { + } else if (location.IsDoubleStackSlot()) { __ movq(CpuRegister(TMP), Immediate(value)); __ movq(Address(CpuRegister(RSP), location.GetStackIndex()), CpuRegister(TMP)); + } else { + DCHECK(location.IsConstant()); + DCHECK_EQ(location.GetConstant(), instruction); } - } else if (instruction->AsLoadLocal() != nullptr) { + } else if (instruction->IsLoadLocal()) { switch (instruction->GetType()) { case Primitive::kPrimBoolean: case Primitive::kPrimByte: @@ -473,46 +507,65 @@ void LocationsBuilderX86_64::VisitIf(HIf* if_instr) { new (GetGraph()->GetArena()) LocationSummary(if_instr, LocationSummary::kNoCall); HInstruction* cond = if_instr->InputAt(0); if (!cond->IsCondition() || cond->AsCondition()->NeedsMaterialization()) { - locations->SetInAt(0, Location::Any(), Location::kDiesAtEntry); + locations->SetInAt(0, Location::Any()); } } void InstructionCodeGeneratorX86_64::VisitIf(HIf* if_instr) { HInstruction* cond = if_instr->InputAt(0); - bool materialized = !cond->IsCondition() || cond->AsCondition()->NeedsMaterialization(); - // Moves do not affect the eflags register, so if the condition is evaluated - // just before the if, we don't need to evaluate it again. - bool eflags_set = cond->IsCondition() - && cond->AsCondition()->IsBeforeWhenDisregardMoves(if_instr); - if (materialized) { - if (!eflags_set) { - // Materialized condition, compare against 0. - Location lhs = if_instr->GetLocations()->InAt(0); - if (lhs.IsRegister()) { - __ cmpl(lhs.As<CpuRegister>(), Immediate(0)); - } else { - __ cmpl(Address(CpuRegister(RSP), lhs.GetStackIndex()), Immediate(0)); + if (cond->IsIntConstant()) { + // Constant condition, statically compared against 1. + int32_t cond_value = cond->AsIntConstant()->GetValue(); + if (cond_value == 1) { + if (!codegen_->GoesToNextBlock(if_instr->GetBlock(), + if_instr->IfTrueSuccessor())) { + __ jmp(codegen_->GetLabelOf(if_instr->IfTrueSuccessor())); } - __ j(kNotEqual, codegen_->GetLabelOf(if_instr->IfTrueSuccessor())); + return; } else { - __ j(X86_64Condition(cond->AsCondition()->GetCondition()), - codegen_->GetLabelOf(if_instr->IfTrueSuccessor())); + DCHECK_EQ(cond_value, 0); } } else { - Location lhs = cond->GetLocations()->InAt(0); - Location rhs = cond->GetLocations()->InAt(1); - if (rhs.IsRegister()) { - __ cmpl(lhs.As<CpuRegister>(), rhs.As<CpuRegister>()); - } else if (rhs.IsConstant()) { - __ cmpl(lhs.As<CpuRegister>(), - Immediate(rhs.GetConstant()->AsIntConstant()->GetValue())); + bool materialized = + !cond->IsCondition() || cond->AsCondition()->NeedsMaterialization(); + // Moves do not affect the eflags register, so if the condition is + // evaluated just before the if, we don't need to evaluate it + // again. + bool eflags_set = cond->IsCondition() + && cond->AsCondition()->IsBeforeWhenDisregardMoves(if_instr); + if (materialized) { + if (!eflags_set) { + // Materialized condition, compare against 0. + Location lhs = if_instr->GetLocations()->InAt(0); + if (lhs.IsRegister()) { + __ cmpl(lhs.As<CpuRegister>(), Immediate(0)); + } else { + __ cmpl(Address(CpuRegister(RSP), lhs.GetStackIndex()), + Immediate(0)); + } + __ j(kNotEqual, codegen_->GetLabelOf(if_instr->IfTrueSuccessor())); + } else { + __ j(X86_64Condition(cond->AsCondition()->GetCondition()), + codegen_->GetLabelOf(if_instr->IfTrueSuccessor())); + } } else { - __ cmpl(lhs.As<CpuRegister>(), Address(CpuRegister(RSP), rhs.GetStackIndex())); + Location lhs = cond->GetLocations()->InAt(0); + Location rhs = cond->GetLocations()->InAt(1); + if (rhs.IsRegister()) { + __ cmpl(lhs.As<CpuRegister>(), rhs.As<CpuRegister>()); + } else if (rhs.IsConstant()) { + __ cmpl(lhs.As<CpuRegister>(), + Immediate(rhs.GetConstant()->AsIntConstant()->GetValue())); + } else { + __ cmpl(lhs.As<CpuRegister>(), + Address(CpuRegister(RSP), rhs.GetStackIndex())); + } + __ j(X86_64Condition(cond->AsCondition()->GetCondition()), + codegen_->GetLabelOf(if_instr->IfTrueSuccessor())); } - __ j(X86_64Condition(cond->AsCondition()->GetCondition()), - codegen_->GetLabelOf(if_instr->IfTrueSuccessor())); } - if (!codegen_->GoesToNextBlock(if_instr->GetBlock(), if_instr->IfFalseSuccessor())) { + if (!codegen_->GoesToNextBlock(if_instr->GetBlock(), + if_instr->IfFalseSuccessor())) { __ jmp(codegen_->GetLabelOf(if_instr->IfFalseSuccessor())); } } @@ -563,8 +616,8 @@ void InstructionCodeGeneratorX86_64::VisitStoreLocal(HStoreLocal* store) { void LocationsBuilderX86_64::VisitCondition(HCondition* comp) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(comp, LocationSummary::kNoCall); - locations->SetInAt(0, Location::RequiresRegister(), Location::kDiesAtEntry); - locations->SetInAt(1, Location::Any(), Location::kDiesAtEntry); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::Any()); if (comp->NeedsMaterialization()) { locations->SetOut(Location::RequiresRegister()); } @@ -641,9 +694,9 @@ void InstructionCodeGeneratorX86_64::VisitGreaterThanOrEqual(HGreaterThanOrEqual void LocationsBuilderX86_64::VisitCompare(HCompare* compare) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(compare, LocationSummary::kNoCall); - locations->SetInAt(0, Location::RequiresRegister(), Location::kDiesAtEntry); - locations->SetInAt(1, Location::RequiresRegister(), Location::kDiesAtEntry); - locations->SetOut(Location::RequiresRegister()); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); } void InstructionCodeGeneratorX86_64::VisitCompare(HCompare* compare) { @@ -679,6 +732,7 @@ void LocationsBuilderX86_64::VisitIntConstant(HIntConstant* constant) { } void InstructionCodeGeneratorX86_64::VisitIntConstant(HIntConstant* constant) { + // Will be generated at use site. } void LocationsBuilderX86_64::VisitLongConstant(HLongConstant* constant) { @@ -688,6 +742,27 @@ void LocationsBuilderX86_64::VisitLongConstant(HLongConstant* constant) { } void InstructionCodeGeneratorX86_64::VisitLongConstant(HLongConstant* constant) { + // Will be generated at use site. +} + +void LocationsBuilderX86_64::VisitFloatConstant(HFloatConstant* constant) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall); + locations->SetOut(Location::ConstantLocation(constant)); +} + +void InstructionCodeGeneratorX86_64::VisitFloatConstant(HFloatConstant* constant) { + // Will be generated at use site. +} + +void LocationsBuilderX86_64::VisitDoubleConstant(HDoubleConstant* constant) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall); + locations->SetOut(Location::ConstantLocation(constant)); +} + +void InstructionCodeGeneratorX86_64::VisitDoubleConstant(HDoubleConstant* constant) { + // Will be generated at use site. } void LocationsBuilderX86_64::VisitReturnVoid(HReturnVoid* ret) { @@ -896,6 +971,47 @@ void InstructionCodeGeneratorX86_64::VisitInvokeVirtual(HInvokeVirtual* invoke) codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); } +void LocationsBuilderX86_64::VisitNeg(HNeg* neg) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(neg, LocationSummary::kNoCall); + switch (neg->GetResultType()) { + case Primitive::kPrimInt: + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::SameAsFirstInput()); + break; + + case Primitive::kPrimLong: + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + LOG(FATAL) << "Not yet implemented neg type " << neg->GetResultType(); + break; + + default: + LOG(FATAL) << "Unexpected neg type " << neg->GetResultType(); + } +} + +void InstructionCodeGeneratorX86_64::VisitNeg(HNeg* neg) { + LocationSummary* locations = neg->GetLocations(); + Location out = locations->Out(); + Location in = locations->InAt(0); + switch (neg->GetResultType()) { + case Primitive::kPrimInt: + DCHECK(in.IsRegister()); + __ negl(out.As<CpuRegister>()); + break; + + case Primitive::kPrimLong: + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + LOG(FATAL) << "Not yet implemented neg type " << neg->GetResultType(); + break; + + default: + LOG(FATAL) << "Unexpected neg type " << neg->GetResultType(); + } +} + void LocationsBuilderX86_64::VisitAdd(HAdd* add) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(add, LocationSummary::kNoCall); @@ -917,7 +1033,7 @@ void LocationsBuilderX86_64::VisitAdd(HAdd* add) { case Primitive::kPrimDouble: case Primitive::kPrimFloat: { locations->SetInAt(0, Location::RequiresFpuRegister()); - locations->SetInAt(1, Location::Any()); + locations->SetInAt(1, Location::RequiresFpuRegister()); locations->SetOut(Location::SameAsFirstInput()); break; } @@ -931,19 +1047,17 @@ void InstructionCodeGeneratorX86_64::VisitAdd(HAdd* add) { LocationSummary* locations = add->GetLocations(); Location first = locations->InAt(0); Location second = locations->InAt(1); - DCHECK(first.Equals(locations->Out())); + switch (add->GetResultType()) { case Primitive::kPrimInt: { if (second.IsRegister()) { __ addl(first.As<CpuRegister>(), second.As<CpuRegister>()); } else if (second.IsConstant()) { - HConstant* instruction = second.GetConstant(); - Immediate imm(instruction->AsIntConstant()->GetValue()); + Immediate imm(second.GetConstant()->AsIntConstant()->GetValue()); __ addl(first.As<CpuRegister>(), imm); } else { - __ addl(first.As<CpuRegister>(), - Address(CpuRegister(RSP), second.GetStackIndex())); + __ addl(first.As<CpuRegister>(), Address(CpuRegister(RSP), second.GetStackIndex())); } break; } @@ -954,21 +1068,12 @@ void InstructionCodeGeneratorX86_64::VisitAdd(HAdd* add) { } case Primitive::kPrimFloat: { - if (second.IsFpuRegister()) { - __ addss(first.As<XmmRegister>(), second.As<XmmRegister>()); - } else { - __ addss(first.As<XmmRegister>(), - Address(CpuRegister(RSP), second.GetStackIndex())); - } + __ addss(first.As<XmmRegister>(), second.As<XmmRegister>()); break; } case Primitive::kPrimDouble: { - if (second.IsFpuRegister()) { - __ addsd(first.As<XmmRegister>(), second.As<XmmRegister>()); - } else { - __ addsd(first.As<XmmRegister>(), Address(CpuRegister(RSP), second.GetStackIndex())); - } + __ addsd(first.As<XmmRegister>(), second.As<XmmRegister>()); break; } @@ -993,53 +1098,119 @@ void LocationsBuilderX86_64::VisitSub(HSub* sub) { locations->SetOut(Location::SameAsFirstInput()); break; } - - case Primitive::kPrimBoolean: - case Primitive::kPrimByte: - case Primitive::kPrimChar: - case Primitive::kPrimShort: - LOG(FATAL) << "Unexpected sub type " << sub->GetResultType(); + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: { + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetInAt(1, Location::RequiresFpuRegister()); + locations->SetOut(Location::SameAsFirstInput()); break; - + } default: - LOG(FATAL) << "Unimplemented sub type " << sub->GetResultType(); + LOG(FATAL) << "Unexpected sub type " << sub->GetResultType(); } } void InstructionCodeGeneratorX86_64::VisitSub(HSub* sub) { LocationSummary* locations = sub->GetLocations(); - DCHECK_EQ(locations->InAt(0).As<CpuRegister>().AsRegister(), - locations->Out().As<CpuRegister>().AsRegister()); + Location first = locations->InAt(0); + Location second = locations->InAt(1); + DCHECK(first.Equals(locations->Out())); switch (sub->GetResultType()) { case Primitive::kPrimInt: { - if (locations->InAt(1).IsRegister()) { - __ subl(locations->InAt(0).As<CpuRegister>(), - locations->InAt(1).As<CpuRegister>()); - } else if (locations->InAt(1).IsConstant()) { - HConstant* instruction = locations->InAt(1).GetConstant(); - Immediate imm(instruction->AsIntConstant()->GetValue()); - __ subl(locations->InAt(0).As<CpuRegister>(), imm); + if (second.IsRegister()) { + __ subl(first.As<CpuRegister>(), second.As<CpuRegister>()); + } else if (second.IsConstant()) { + Immediate imm(second.GetConstant()->AsIntConstant()->GetValue()); + __ subl(first.As<CpuRegister>(), imm); } else { - __ subl(locations->InAt(0).As<CpuRegister>(), - Address(CpuRegister(RSP), locations->InAt(1).GetStackIndex())); + __ subl(first.As<CpuRegister>(), Address(CpuRegister(RSP), second.GetStackIndex())); } break; } case Primitive::kPrimLong: { - __ subq(locations->InAt(0).As<CpuRegister>(), - locations->InAt(1).As<CpuRegister>()); + __ subq(first.As<CpuRegister>(), second.As<CpuRegister>()); break; } - case Primitive::kPrimBoolean: - case Primitive::kPrimByte: - case Primitive::kPrimChar: - case Primitive::kPrimShort: + case Primitive::kPrimFloat: { + __ subss(first.As<XmmRegister>(), second.As<XmmRegister>()); + break; + } + + case Primitive::kPrimDouble: { + __ subsd(first.As<XmmRegister>(), second.As<XmmRegister>()); + break; + } + + default: LOG(FATAL) << "Unexpected sub type " << sub->GetResultType(); + } +} + +void LocationsBuilderX86_64::VisitMul(HMul* mul) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(mul, LocationSummary::kNoCall); + switch (mul->GetResultType()) { + 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()); + locations->SetOut(Location::SameAsFirstInput()); break; + } + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: { + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetInAt(1, Location::RequiresFpuRegister()); + locations->SetOut(Location::SameAsFirstInput()); + break; + } default: - LOG(FATAL) << "Unimplemented sub type " << sub->GetResultType(); + LOG(FATAL) << "Unexpected mul type " << mul->GetResultType(); + } +} + +void InstructionCodeGeneratorX86_64::VisitMul(HMul* mul) { + LocationSummary* locations = mul->GetLocations(); + Location first = locations->InAt(0); + Location second = locations->InAt(1); + DCHECK(first.Equals(locations->Out())); + switch (mul->GetResultType()) { + case Primitive::kPrimInt: { + if (second.IsRegister()) { + __ imull(first.As<CpuRegister>(), second.As<CpuRegister>()); + } else if (second.IsConstant()) { + Immediate imm(second.GetConstant()->AsIntConstant()->GetValue()); + __ imull(first.As<CpuRegister>(), imm); + } else { + DCHECK(second.IsStackSlot()); + __ imull(first.As<CpuRegister>(), Address(CpuRegister(RSP), second.GetStackIndex())); + } + break; + } + case Primitive::kPrimLong: { + __ imulq(first.As<CpuRegister>(), second.As<CpuRegister>()); + break; + } + + case Primitive::kPrimFloat: { + __ mulss(first.As<XmmRegister>(), second.As<XmmRegister>()); + break; + } + + case Primitive::kPrimDouble: { + __ mulsd(first.As<XmmRegister>(), second.As<XmmRegister>()); + break; + } + + default: + LOG(FATAL) << "Unexpected mul type " << mul->GetResultType(); } } @@ -1064,6 +1235,28 @@ void InstructionCodeGeneratorX86_64::VisitNewInstance(HNewInstance* instruction) codegen_->RecordPcInfo(instruction, instruction->GetDexPc()); } +void LocationsBuilderX86_64::VisitNewArray(HNewArray* instruction) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall); + InvokeRuntimeCallingConvention calling_convention; + locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(1))); + locations->SetOut(Location::RegisterLocation(RAX)); + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(2))); +} + +void InstructionCodeGeneratorX86_64::VisitNewArray(HNewArray* instruction) { + InvokeRuntimeCallingConvention calling_convention; + LoadCurrentMethod(CpuRegister(calling_convention.GetRegisterAt(1))); + __ movq(CpuRegister(calling_convention.GetRegisterAt(0)), Immediate(instruction->GetTypeIndex())); + + __ gs()->call(Address::Absolute( + QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pAllocArrayWithAccessCheck), true)); + + DCHECK(!codegen_->IsLeafMethod()); + codegen_->RecordPcInfo(instruction, instruction->GetDexPc()); +} + void LocationsBuilderX86_64::VisitParameterValue(HParameterValue* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); @@ -1080,18 +1273,34 @@ void InstructionCodeGeneratorX86_64::VisitParameterValue(HParameterValue* instru // Nothing to do, the parameter is already at its location. } -void LocationsBuilderX86_64::VisitNot(HNot* instruction) { +void LocationsBuilderX86_64::VisitNot(HNot* not_) { LocationSummary* locations = - new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + new (GetGraph()->GetArena()) LocationSummary(not_, LocationSummary::kNoCall); locations->SetInAt(0, Location::RequiresRegister()); locations->SetOut(Location::SameAsFirstInput()); } -void InstructionCodeGeneratorX86_64::VisitNot(HNot* instruction) { - LocationSummary* locations = instruction->GetLocations(); +void InstructionCodeGeneratorX86_64::VisitNot(HNot* not_) { + LocationSummary* locations = not_->GetLocations(); DCHECK_EQ(locations->InAt(0).As<CpuRegister>().AsRegister(), locations->Out().As<CpuRegister>().AsRegister()); - __ xorq(locations->Out().As<CpuRegister>(), Immediate(1)); + Location out = locations->Out(); + switch (not_->InputAt(0)->GetType()) { + case Primitive::kPrimBoolean: + __ xorq(out.As<CpuRegister>(), Immediate(1)); + break; + + case Primitive::kPrimInt: + __ notl(out.As<CpuRegister>()); + break; + + case Primitive::kPrimLong: + LOG(FATAL) << "Not yet implemented type for not operation " << not_->GetResultType(); + break; + + default: + LOG(FATAL) << "Unimplemented type for not operation " << not_->GetResultType(); + } } void LocationsBuilderX86_64::VisitPhi(HPhi* instruction) { @@ -1112,9 +1321,8 @@ void LocationsBuilderX86_64::VisitInstanceFieldSet(HInstanceFieldSet* instructio new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); Primitive::Type field_type = instruction->GetFieldType(); bool is_object_type = field_type == Primitive::kPrimNot; - bool dies_at_entry = !is_object_type; - locations->SetInAt(0, Location::RequiresRegister(), dies_at_entry); - locations->SetInAt(1, Location::RequiresRegister(), dies_at_entry); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); if (is_object_type) { // Temporary registers for the write barrier. locations->AddTemp(Location::RequiresRegister()); @@ -1171,8 +1379,8 @@ void InstructionCodeGeneratorX86_64::VisitInstanceFieldSet(HInstanceFieldSet* in void LocationsBuilderX86_64::VisitInstanceFieldGet(HInstanceFieldGet* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); - locations->SetInAt(0, Location::RequiresRegister(), Location::kDiesAtEntry); - locations->SetOut(Location::RequiresRegister()); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); } void InstructionCodeGeneratorX86_64::VisitInstanceFieldGet(HInstanceFieldGet* instruction) { @@ -1233,7 +1441,7 @@ void LocationsBuilderX86_64::VisitNullCheck(HNullCheck* instruction) { } void InstructionCodeGeneratorX86_64::VisitNullCheck(HNullCheck* instruction) { - SlowPathCode* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathX86_64(instruction); + SlowPathCodeX86_64* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathX86_64(instruction); codegen_->AddSlowPath(slow_path); LocationSummary* locations = instruction->GetLocations(); @@ -1255,10 +1463,10 @@ void InstructionCodeGeneratorX86_64::VisitNullCheck(HNullCheck* instruction) { void LocationsBuilderX86_64::VisitArrayGet(HArrayGet* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); - locations->SetInAt(0, Location::RequiresRegister(), Location::kDiesAtEntry); + locations->SetInAt(0, Location::RequiresRegister()); locations->SetInAt( - 1, Location::RegisterOrConstant(instruction->InputAt(1)), Location::kDiesAtEntry); - locations->SetOut(Location::RequiresRegister()); + 1, Location::RegisterOrConstant(instruction->InputAt(1))); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); } void InstructionCodeGeneratorX86_64::VisitArrayGet(HArrayGet* instruction) { @@ -1341,10 +1549,30 @@ void InstructionCodeGeneratorX86_64::VisitArrayGet(HArrayGet* instruction) { break; } - case Primitive::kPrimFloat: - case Primitive::kPrimDouble: - LOG(FATAL) << "Unimplemented register type " << instruction->GetType(); - UNREACHABLE(); + case Primitive::kPrimFloat: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(float)).Uint32Value(); + XmmRegister out = locations->Out().As<XmmRegister>(); + if (index.IsConstant()) { + __ movss(out, Address(obj, + (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset)); + } else { + __ movss(out, Address(obj, index.As<CpuRegister>(), TIMES_4, data_offset)); + } + break; + } + + case Primitive::kPrimDouble: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(double)).Uint32Value(); + XmmRegister out = locations->Out().As<XmmRegister>(); + if (index.IsConstant()) { + __ movsd(out, Address(obj, + (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset)); + } else { + __ movsd(out, Address(obj, index.As<CpuRegister>(), TIMES_8, data_offset)); + } + break; + } + case Primitive::kPrimVoid: LOG(FATAL) << "Unreachable type " << instruction->GetType(); UNREACHABLE(); @@ -1362,14 +1590,16 @@ void LocationsBuilderX86_64::VisitArraySet(HArraySet* instruction) { locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2))); } else { - locations->SetInAt(0, Location::RequiresRegister(), Location::kDiesAtEntry); + locations->SetInAt(0, Location::RequiresRegister()); locations->SetInAt( - 1, Location::RegisterOrConstant(instruction->InputAt(1)), Location::kDiesAtEntry); - locations->SetInAt(2, Location::RequiresRegister(), Location::kDiesAtEntry); + 1, Location::RegisterOrConstant(instruction->InputAt(1))); + locations->SetInAt(2, Location::RequiresRegister()); if (value_type == Primitive::kPrimLong) { - locations->SetInAt(2, Location::RequiresRegister(), Location::kDiesAtEntry); + locations->SetInAt(2, Location::RequiresRegister()); + } else if (value_type == Primitive::kPrimFloat || value_type == Primitive::kPrimDouble) { + locations->SetInAt(2, Location::RequiresFpuRegister()); } else { - locations->SetInAt(2, Location::RegisterOrConstant(instruction->InputAt(2)), Location::kDiesAtEntry); + locations->SetInAt(2, Location::RegisterOrConstant(instruction->InputAt(2))); } } } @@ -1440,6 +1670,7 @@ void InstructionCodeGeneratorX86_64::VisitArraySet(HArraySet* instruction) { __ movl(Address(obj, index.As<CpuRegister>(), TIMES_4, data_offset), value.As<CpuRegister>()); } else { + DCHECK(value.IsConstant()) << value; __ movl(Address(obj, index.As<CpuRegister>(), TIMES_4, data_offset), Immediate(value.GetConstant()->AsIntConstant()->GetValue())); } @@ -1468,10 +1699,34 @@ void InstructionCodeGeneratorX86_64::VisitArraySet(HArraySet* instruction) { break; } - case Primitive::kPrimFloat: - case Primitive::kPrimDouble: - LOG(FATAL) << "Unimplemented register type " << instruction->GetType(); - UNREACHABLE(); + case Primitive::kPrimFloat: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(float)).Uint32Value(); + if (index.IsConstant()) { + size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; + DCHECK(value.IsFpuRegister()); + __ movss(Address(obj, offset), value.As<XmmRegister>()); + } else { + DCHECK(value.IsFpuRegister()); + __ movss(Address(obj, index.As<CpuRegister>(), TIMES_4, data_offset), + value.As<XmmRegister>()); + } + break; + } + + case Primitive::kPrimDouble: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(double)).Uint32Value(); + if (index.IsConstant()) { + size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; + DCHECK(value.IsFpuRegister()); + __ movsd(Address(obj, offset), value.As<XmmRegister>()); + } else { + DCHECK(value.IsFpuRegister()); + __ movsd(Address(obj, index.As<CpuRegister>(), TIMES_8, data_offset), + value.As<XmmRegister>()); + } + break; + } + case Primitive::kPrimVoid: LOG(FATAL) << "Unreachable type " << instruction->GetType(); UNREACHABLE(); @@ -1481,8 +1736,8 @@ void InstructionCodeGeneratorX86_64::VisitArraySet(HArraySet* instruction) { void LocationsBuilderX86_64::VisitArrayLength(HArrayLength* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); - locations->SetInAt(0, Location::RequiresRegister(), Location::kDiesAtEntry); - locations->SetOut(Location::RequiresRegister()); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); } void InstructionCodeGeneratorX86_64::VisitArrayLength(HArrayLength* instruction) { @@ -1505,7 +1760,7 @@ void LocationsBuilderX86_64::VisitBoundsCheck(HBoundsCheck* instruction) { void InstructionCodeGeneratorX86_64::VisitBoundsCheck(HBoundsCheck* instruction) { LocationSummary* locations = instruction->GetLocations(); - SlowPathCode* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathX86_64( + SlowPathCodeX86_64* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathX86_64( instruction, locations->InAt(0), locations->InAt(1)); codegen_->AddSlowPath(slow_path); @@ -1605,6 +1860,9 @@ void ParallelMoveResolverX86_64::EmitMove(size_t index) { if (destination.IsRegister()) { __ movl(destination.As<CpuRegister>(), Address(CpuRegister(RSP), source.GetStackIndex())); + } else if (destination.IsFpuRegister()) { + __ movss(destination.As<XmmRegister>(), + Address(CpuRegister(RSP), source.GetStackIndex())); } else { DCHECK(destination.IsStackSlot()); __ movl(CpuRegister(TMP), Address(CpuRegister(RSP), source.GetStackIndex())); @@ -1614,8 +1872,10 @@ void ParallelMoveResolverX86_64::EmitMove(size_t index) { if (destination.IsRegister()) { __ movq(destination.As<CpuRegister>(), Address(CpuRegister(RSP), source.GetStackIndex())); + } else if (destination.IsFpuRegister()) { + __ movsd(destination.As<XmmRegister>(), Address(CpuRegister(RSP), source.GetStackIndex())); } else { - DCHECK(destination.IsDoubleStackSlot()); + DCHECK(destination.IsDoubleStackSlot()) << destination; __ movq(CpuRegister(TMP), Address(CpuRegister(RSP), source.GetStackIndex())); __ movq(Address(CpuRegister(RSP), destination.GetStackIndex()), CpuRegister(TMP)); } @@ -1626,6 +1886,7 @@ void ParallelMoveResolverX86_64::EmitMove(size_t index) { if (destination.IsRegister()) { __ movl(destination.As<CpuRegister>(), imm); } else { + DCHECK(destination.IsStackSlot()) << destination; __ movl(Address(CpuRegister(RSP), destination.GetStackIndex()), imm); } } else if (constant->IsLongConstant()) { @@ -1633,14 +1894,42 @@ void ParallelMoveResolverX86_64::EmitMove(size_t index) { if (destination.IsRegister()) { __ movq(destination.As<CpuRegister>(), Immediate(value)); } else { + DCHECK(destination.IsDoubleStackSlot()) << destination; __ movq(CpuRegister(TMP), Immediate(value)); __ movq(Address(CpuRegister(RSP), destination.GetStackIndex()), CpuRegister(TMP)); } + } else if (constant->IsFloatConstant()) { + Immediate imm(bit_cast<float, int32_t>(constant->AsFloatConstant()->GetValue())); + if (destination.IsFpuRegister()) { + __ movl(CpuRegister(TMP), imm); + __ movd(destination.As<XmmRegister>(), CpuRegister(TMP)); + } else { + DCHECK(destination.IsStackSlot()) << destination; + __ movl(Address(CpuRegister(RSP), destination.GetStackIndex()), imm); + } + } else { + DCHECK(constant->IsDoubleConstant()) << constant->DebugName(); + Immediate imm(bit_cast<double, int64_t>(constant->AsDoubleConstant()->GetValue())); + if (destination.IsFpuRegister()) { + __ movq(CpuRegister(TMP), imm); + __ movd(destination.As<XmmRegister>(), CpuRegister(TMP)); + } else { + DCHECK(destination.IsDoubleStackSlot()) << destination; + __ movq(CpuRegister(TMP), imm); + __ movq(Address(CpuRegister(RSP), destination.GetStackIndex()), CpuRegister(TMP)); + } + } + } else if (source.IsFpuRegister()) { + if (destination.IsFpuRegister()) { + __ movaps(destination.As<XmmRegister>(), source.As<XmmRegister>()); + } else if (destination.IsStackSlot()) { + __ movss(Address(CpuRegister(RSP), destination.GetStackIndex()), + source.As<XmmRegister>()); } else { - LOG(FATAL) << "Unimplemented constant type"; + DCHECK(destination.IsDoubleStackSlot()); + __ movsd(Address(CpuRegister(RSP), destination.GetStackIndex()), + source.As<XmmRegister>()); } - } else { - LOG(FATAL) << "Unimplemented"; } } @@ -1682,6 +1971,18 @@ void ParallelMoveResolverX86_64::Exchange64(int mem1, int mem2) { CpuRegister(ensure_scratch.GetRegister())); } +void ParallelMoveResolverX86_64::Exchange32(XmmRegister reg, int mem) { + __ movl(CpuRegister(TMP), Address(CpuRegister(RSP), mem)); + __ movss(Address(CpuRegister(RSP), mem), reg); + __ movd(reg, CpuRegister(TMP)); +} + +void ParallelMoveResolverX86_64::Exchange64(XmmRegister reg, int mem) { + __ movq(CpuRegister(TMP), Address(CpuRegister(RSP), mem)); + __ movsd(Address(CpuRegister(RSP), mem), reg); + __ movd(reg, CpuRegister(TMP)); +} + void ParallelMoveResolverX86_64::EmitSwap(size_t index) { MoveOperands* move = moves_.Get(index); Location source = move->GetSource(); @@ -1701,8 +2002,20 @@ void ParallelMoveResolverX86_64::EmitSwap(size_t index) { Exchange64(destination.As<CpuRegister>(), source.GetStackIndex()); } else if (source.IsDoubleStackSlot() && destination.IsDoubleStackSlot()) { Exchange64(destination.GetStackIndex(), source.GetStackIndex()); + } else if (source.IsFpuRegister() && destination.IsFpuRegister()) { + __ movd(CpuRegister(TMP), source.As<XmmRegister>()); + __ movaps(source.As<XmmRegister>(), destination.As<XmmRegister>()); + __ movd(destination.As<XmmRegister>(), CpuRegister(TMP)); + } else if (source.IsFpuRegister() && destination.IsStackSlot()) { + Exchange32(source.As<XmmRegister>(), destination.GetStackIndex()); + } else if (source.IsStackSlot() && destination.IsFpuRegister()) { + Exchange32(destination.As<XmmRegister>(), source.GetStackIndex()); + } else if (source.IsFpuRegister() && destination.IsDoubleStackSlot()) { + Exchange64(source.As<XmmRegister>(), destination.GetStackIndex()); + } else if (source.IsDoubleStackSlot() && destination.IsFpuRegister()) { + Exchange64(destination.As<XmmRegister>(), source.GetStackIndex()); } else { - LOG(FATAL) << "Unimplemented"; + LOG(FATAL) << "Unimplemented swap between " << source << " and " << destination; } } diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h index 288f3f61f9..e04a8d8ab9 100644 --- a/compiler/optimizing/code_generator_x86_64.h +++ b/compiler/optimizing/code_generator_x86_64.h @@ -80,8 +80,10 @@ class ParallelMoveResolverX86_64 : public ParallelMoveResolver { private: void Exchange32(CpuRegister reg, int mem); + void Exchange32(XmmRegister reg, int mem); void Exchange32(int mem1, int mem2); void Exchange64(CpuRegister reg, int mem); + void Exchange64(XmmRegister reg, int mem); void Exchange64(int mem1, int mem2); CodeGeneratorX86_64* const codegen_; @@ -144,10 +146,12 @@ class CodeGeneratorX86_64 : public CodeGenerator { virtual void GenerateFrameEntry() OVERRIDE; virtual void GenerateFrameExit() OVERRIDE; - virtual void Bind(Label* label) OVERRIDE; + virtual void Bind(HBasicBlock* block) OVERRIDE; virtual void Move(HInstruction* instruction, Location location, HInstruction* move_for) OVERRIDE; - virtual void SaveCoreRegister(Location stack_location, uint32_t reg_id) OVERRIDE; - virtual void RestoreCoreRegister(Location stack_location, uint32_t reg_id) OVERRIDE; + virtual size_t SaveCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE; + virtual size_t RestoreCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE; + virtual size_t SaveFloatingPointRegister(size_t stack_index, uint32_t reg_id) OVERRIDE; + virtual size_t RestoreFloatingPointRegister(size_t stack_index, uint32_t reg_id) OVERRIDE; virtual size_t GetWordSize() const OVERRIDE { return kX86_64WordSize; @@ -188,7 +192,17 @@ class CodeGeneratorX86_64 : public CodeGenerator { // Helper method to move a value between two locations. void Move(Location destination, Location source); + Label* GetLabelOf(HBasicBlock* block) const { + return block_labels_.GetRawStorage() + block->GetBlockId(); + } + + virtual void Initialize() OVERRIDE { + block_labels_.SetSize(GetGraph()->GetBlocks().Size()); + } + private: + // Labels for each block that will be compiled. + GrowableArray<Label> block_labels_; 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 3037f1c2e8..03951e29dd 100644 --- a/compiler/optimizing/codegen_test.cc +++ b/compiler/optimizing/codegen_test.cc @@ -16,8 +16,10 @@ #include <functional> +#include "base/macros.h" #include "builder.h" #include "code_generator_arm.h" +#include "code_generator_arm64.h" #include "code_generator_x86.h" #include "code_generator_x86_64.h" #include "common_compiler_test.h" @@ -93,6 +95,12 @@ static void RunCodeBaseline(HGraph* graph, bool has_result, int32_t expected) { if (kRuntimeISA == kX86_64) { Run(allocator, codegenX86_64, has_result, expected); } + + arm64::CodeGeneratorARM64 codegenARM64(graph); + codegenARM64.CompileBaseline(&allocator, true); + if (kRuntimeISA == kArm64) { + Run(allocator, codegenARM64, has_result, expected); + } } static void RunCodeOptimized(CodeGenerator* codegen, @@ -134,9 +142,9 @@ static void TestCode(const uint16_t* data, bool has_result = false, int32_t expe HGraphBuilder builder(&arena); const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data); HGraph* graph = builder.BuildGraph(*item); + ASSERT_NE(graph, nullptr); // Remove suspend checks, they cannot be executed in this context. RemoveSuspendChecks(graph); - ASSERT_NE(graph, nullptr); RunCodeBaseline(graph, has_result, expected); } @@ -260,6 +268,31 @@ TEST(CodegenTest, ReturnIf2) { TestCode(data, true, 0); } +// Exercise bit-wise (one's complement) not-int instruction. +#define NOT_INT_TEST(TEST_NAME, INPUT, EXPECTED_OUTPUT) \ +TEST(CodegenTest, TEST_NAME) { \ + const int32_t input = INPUT; \ + const uint16_t input_lo = input & 0x0000FFFF; \ + const uint16_t input_hi = input >> 16; \ + const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( \ + Instruction::CONST | 0 << 8, input_lo, input_hi, \ + Instruction::NOT_INT | 1 << 8 | 0 << 12 , \ + Instruction::RETURN | 1 << 8); \ + \ + TestCode(data, true, EXPECTED_OUTPUT); \ +} + +NOT_INT_TEST(ReturnNotIntMinus2, -2, 1) +NOT_INT_TEST(ReturnNotIntMinus1, -1, 0) +NOT_INT_TEST(ReturnNotInt0, 0, -1) +NOT_INT_TEST(ReturnNotInt1, 1, -2) +NOT_INT_TEST(ReturnNotIntINT_MIN, -2147483648, 2147483647) // (2^31) - 1 +NOT_INT_TEST(ReturnNotIntINT_MINPlus1, -2147483647, 2147483646) // (2^31) - 2 +NOT_INT_TEST(ReturnNotIntINT_MAXMinus1, 2147483646, -2147483647) // -(2^31) - 1 +NOT_INT_TEST(ReturnNotIntINT_MAX, 2147483647, -2147483648) // -(2^31) + +#undef NOT_INT_TEST + TEST(CodegenTest, ReturnAdd1) { const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 3 << 12 | 0, @@ -349,4 +382,173 @@ TEST(CodegenTest, NonMaterializedCondition) { RunCodeOptimized(graph, hook_before_codegen, true, 0); } +#define MUL_TEST(TYPE, TEST_NAME) \ + TEST(CodegenTest, Return ## TEST_NAME) { \ + const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( \ + Instruction::CONST_4 | 3 << 12 | 0, \ + Instruction::CONST_4 | 4 << 12 | 1 << 8, \ + Instruction::MUL_ ## TYPE, 1 << 8 | 0, \ + Instruction::RETURN); \ + \ + TestCode(data, true, 12); \ + } \ + \ + TEST(CodegenTest, Return ## TEST_NAME ## 2addr) { \ + const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( \ + Instruction::CONST_4 | 3 << 12 | 0, \ + Instruction::CONST_4 | 4 << 12 | 1 << 8, \ + Instruction::MUL_ ## TYPE ## _2ADDR | 1 << 12, \ + Instruction::RETURN); \ + \ + TestCode(data, true, 12); \ + } + +#if !defined(__aarch64__) +MUL_TEST(INT, MulInt); +MUL_TEST(LONG, MulLong); +#endif + +#if defined(__aarch64__) +TEST(CodegenTest, DISABLED_ReturnMulIntLit8) { +#else +TEST(CodegenTest, ReturnMulIntLit8) { +#endif + const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + Instruction::CONST_4 | 4 << 12 | 0 << 8, + Instruction::MUL_INT_LIT8, 3 << 8 | 0, + Instruction::RETURN); + + TestCode(data, true, 12); +} + +#if defined(__aarch64__) +TEST(CodegenTest, DISABLED_ReturnMulIntLit16) { +#else +TEST(CodegenTest, ReturnMulIntLit16) { +#endif + const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + Instruction::CONST_4 | 4 << 12 | 0 << 8, + Instruction::MUL_INT_LIT16, 3, + Instruction::RETURN); + + TestCode(data, true, 12); +} + +TEST(CodegenTest, MaterializedCondition1) { + // Check that condition are materialized correctly. A materialized condition + // should yield `1` if it evaluated to true, and `0` otherwise. + // We force the materialization of comparisons for different combinations of + // inputs and check the results. + + int lhs[] = {1, 2, -1, 2, 0xabc}; + int rhs[] = {2, 1, 2, -1, 0xabc}; + + for (size_t i = 0; i < arraysize(lhs); i++) { + ArenaPool pool; + ArenaAllocator allocator(&pool); + HGraph* graph = new (&allocator) HGraph(&allocator); + + HBasicBlock* entry_block = new (&allocator) HBasicBlock(graph); + graph->AddBlock(entry_block); + graph->SetEntryBlock(entry_block); + entry_block->AddInstruction(new (&allocator) HGoto()); + HBasicBlock* code_block = new (&allocator) HBasicBlock(graph); + graph->AddBlock(code_block); + HBasicBlock* exit_block = new (&allocator) HBasicBlock(graph); + graph->AddBlock(exit_block); + exit_block->AddInstruction(new (&allocator) HExit()); + + entry_block->AddSuccessor(code_block); + code_block->AddSuccessor(exit_block); + graph->SetExitBlock(exit_block); + + HIntConstant cst_lhs(lhs[i]); + code_block->AddInstruction(&cst_lhs); + HIntConstant cst_rhs(rhs[i]); + code_block->AddInstruction(&cst_rhs); + HLessThan cmp_lt(&cst_lhs, &cst_rhs); + code_block->AddInstruction(&cmp_lt); + HReturn ret(&cmp_lt); + code_block->AddInstruction(&ret); + + auto hook_before_codegen = [](HGraph* graph) { + HBasicBlock* block = graph->GetEntryBlock()->GetSuccessors().Get(0); + HParallelMove* move = new (graph->GetArena()) HParallelMove(graph->GetArena()); + block->InsertInstructionBefore(move, block->GetLastInstruction()); + }; + + RunCodeOptimized(graph, hook_before_codegen, true, lhs[i] < rhs[i]); + } +} + +TEST(CodegenTest, MaterializedCondition2) { + // Check that HIf correctly interprets a materialized condition. + // We force the materialization of comparisons for different combinations of + // inputs. An HIf takes the materialized combination as input and returns a + // value that we verify. + + int lhs[] = {1, 2, -1, 2, 0xabc}; + int rhs[] = {2, 1, 2, -1, 0xabc}; + + + for (size_t i = 0; i < arraysize(lhs); i++) { + ArenaPool pool; + ArenaAllocator allocator(&pool); + HGraph* graph = new (&allocator) HGraph(&allocator); + + HBasicBlock* entry_block = new (&allocator) HBasicBlock(graph); + graph->AddBlock(entry_block); + graph->SetEntryBlock(entry_block); + entry_block->AddInstruction(new (&allocator) HGoto()); + + HBasicBlock* if_block = new (&allocator) HBasicBlock(graph); + graph->AddBlock(if_block); + HBasicBlock* if_true_block = new (&allocator) HBasicBlock(graph); + graph->AddBlock(if_true_block); + HBasicBlock* if_false_block = new (&allocator) HBasicBlock(graph); + graph->AddBlock(if_false_block); + HBasicBlock* exit_block = new (&allocator) HBasicBlock(graph); + graph->AddBlock(exit_block); + exit_block->AddInstruction(new (&allocator) HExit()); + + graph->SetEntryBlock(entry_block); + entry_block->AddSuccessor(if_block); + if_block->AddSuccessor(if_true_block); + if_block->AddSuccessor(if_false_block); + if_true_block->AddSuccessor(exit_block); + if_false_block->AddSuccessor(exit_block); + graph->SetExitBlock(exit_block); + + HIntConstant cst_lhs(lhs[i]); + if_block->AddInstruction(&cst_lhs); + HIntConstant cst_rhs(rhs[i]); + if_block->AddInstruction(&cst_rhs); + HLessThan cmp_lt(&cst_lhs, &cst_rhs); + if_block->AddInstruction(&cmp_lt); + // We insert a temporary to separate the HIf from the HLessThan and force + // the materialization of the condition. + HTemporary force_materialization(0); + if_block->AddInstruction(&force_materialization); + HIf if_lt(&cmp_lt); + if_block->AddInstruction(&if_lt); + + HIntConstant cst_lt(1); + if_true_block->AddInstruction(&cst_lt); + HReturn ret_lt(&cst_lt); + if_true_block->AddInstruction(&ret_lt); + HIntConstant cst_ge(0); + if_false_block->AddInstruction(&cst_ge); + HReturn ret_ge(&cst_ge); + if_false_block->AddInstruction(&ret_ge); + + auto hook_before_codegen = [](HGraph* graph) { + HBasicBlock* block = graph->GetEntryBlock()->GetSuccessors().Get(0); + HParallelMove* move = new (graph->GetArena()) HParallelMove(graph->GetArena()); + block->InsertInstructionBefore(move, block->GetLastInstruction()); + }; + + RunCodeOptimized(graph, hook_before_codegen, true, lhs[i] < rhs[i]); + } +} + } // namespace art diff --git a/compiler/optimizing/constant_propagation.cc b/compiler/optimizing/constant_folding.cc index d675164fa4..10a7e46299 100644 --- a/compiler/optimizing/constant_propagation.cc +++ b/compiler/optimizing/constant_folding.cc @@ -14,11 +14,11 @@ * limitations under the License. */ -#include "constant_propagation.h" +#include "constant_folding.h" namespace art { -void ConstantPropagation::Run() { +void HConstantFolding::Run() { // Process basic blocks in reverse post-order in the dominator tree, // so that an instruction turned into a constant, used as input of // another instruction, may possibly be used to turn that second @@ -31,11 +31,19 @@ void ConstantPropagation::Run() { for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) { HInstruction* inst = it.Current(); - // Constant folding: replace `c <- a op b' with a compile-time - // evaluation of `a op b' if `a' and `b' are constant. if (inst->IsBinaryOperation()) { + // Constant folding: replace `op(a, b)' with a constant at + // compile time if `a' and `b' are both constants. HConstant* constant = - inst->AsBinaryOperation()->TryStaticEvaluation(graph_->GetArena()); + inst->AsBinaryOperation()->TryStaticEvaluation(); + if (constant != nullptr) { + inst->GetBlock()->ReplaceAndRemoveInstructionWith(inst, constant); + } + } else if (inst->IsUnaryOperation()) { + // Constant folding: replace `op(a)' with a constant at compile + // time if `a' is a constant. + HConstant* constant = + inst->AsUnaryOperation()->TryStaticEvaluation(); if (constant != nullptr) { inst->GetBlock()->ReplaceAndRemoveInstructionWith(inst, constant); } diff --git a/compiler/optimizing/constant_folding.h b/compiler/optimizing/constant_folding.h new file mode 100644 index 0000000000..d2acfa6973 --- /dev/null +++ b/compiler/optimizing/constant_folding.h @@ -0,0 +1,48 @@ +/* + * 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_CONSTANT_FOLDING_H_ +#define ART_COMPILER_OPTIMIZING_CONSTANT_FOLDING_H_ + +#include "nodes.h" +#include "optimization.h" + +namespace art { + +/** + * Optimization pass performing a simple constant-expression + * evaluation on the SSA form. + * + * This class is named art::HConstantFolding to avoid name + * clashes with the art::ConstantPropagation class defined in + * compiler/dex/post_opt_passes.h. + */ +class HConstantFolding : public HOptimization { + public: + HConstantFolding(HGraph* graph, const HGraphVisualizer& visualizer) + : HOptimization(graph, true, kConstantFoldingPassName, visualizer) {} + + virtual void Run() OVERRIDE; + + static constexpr const char* kConstantFoldingPassName = "constant_folding"; + + private: + DISALLOW_COPY_AND_ASSIGN(HConstantFolding); +}; + +} // namespace art + +#endif // ART_COMPILER_OPTIMIZING_CONSTANT_FOLDING_H_ diff --git a/compiler/optimizing/constant_propagation_test.cc b/compiler/optimizing/constant_folding_test.cc index 342777a49c..09bf2c8d7d 100644 --- a/compiler/optimizing/constant_propagation_test.cc +++ b/compiler/optimizing/constant_folding_test.cc @@ -16,11 +16,12 @@ #include <functional> -#include "constant_propagation.h" +#include "code_generator_x86.h" +#include "constant_folding.h" #include "dead_code_elimination.h" -#include "pretty_printer.h" #include "graph_checker.h" #include "optimizing_unit_test.h" +#include "pretty_printer.h" #include "gtest/gtest.h" @@ -28,9 +29,9 @@ namespace art { static void TestCode(const uint16_t* data, const std::string& expected_before, - const std::string& expected_after_cp, + const std::string& expected_after_cf, const std::string& expected_after_dce, - std::function<void(HGraph*)> check_after_cp, + std::function<void(HGraph*)> check_after_cf, Primitive::Type return_type = Primitive::kPrimInt) { ArenaPool pool; ArenaAllocator allocator(&pool); @@ -45,29 +46,87 @@ static void TestCode(const uint16_t* data, std::string actual_before = printer_before.str(); ASSERT_EQ(expected_before, actual_before); - ConstantPropagation(graph).Run(); + x86::CodeGeneratorX86 codegen(graph); + HGraphVisualizer visualizer(nullptr, graph, codegen, ""); + HConstantFolding(graph, visualizer).Run(); + SSAChecker ssa_checker(&allocator, graph); + ssa_checker.Run(); + ASSERT_TRUE(ssa_checker.IsValid()); - StringPrettyPrinter printer_after_cp(graph); - printer_after_cp.VisitInsertionOrder(); - std::string actual_after_cp = printer_after_cp.str(); - ASSERT_EQ(expected_after_cp, actual_after_cp); + StringPrettyPrinter printer_after_cf(graph); + printer_after_cf.VisitInsertionOrder(); + std::string actual_after_cf = printer_after_cf.str(); + ASSERT_EQ(expected_after_cf, actual_after_cf); - check_after_cp(graph); + check_after_cf(graph); - DeadCodeElimination(graph).Run(); + HDeadCodeElimination(graph, visualizer).Run(); + ssa_checker.Run(); + ASSERT_TRUE(ssa_checker.IsValid()); StringPrettyPrinter printer_after_dce(graph); printer_after_dce.VisitInsertionOrder(); std::string actual_after_dce = printer_after_dce.str(); ASSERT_EQ(expected_after_dce, actual_after_dce); - - SSAChecker ssa_checker(&allocator, graph); - ssa_checker.VisitInsertionOrder(); - ASSERT_TRUE(ssa_checker.IsValid()); } /** + * Tiny three-register program exercising int constant folding on negation. + * + * 16-bit + * offset + * ------ + * v0 <- 1 0. const/4 v0, #+1 + * v1 <- -v0 1. neg-int v0, v1 + * return v1 2. return v1 + */ +TEST(ConstantFolding, IntConstantFoldingNegation) { + const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( + Instruction::CONST_4 | 0 << 8 | 1 << 12, + Instruction::NEG_INT | 1 << 8 | 0 << 12, + Instruction::RETURN | 1 << 8); + + std::string expected_before = + "BasicBlock 0, succ: 1\n" + " 2: IntConstant [5]\n" + " 10: SuspendCheck\n" + " 11: Goto 1\n" + "BasicBlock 1, pred: 0, succ: 2\n" + " 5: Neg(2) [8]\n" + " 8: Return(5)\n" + "BasicBlock 2, pred: 1\n" + " 9: Exit\n"; + + // Expected difference after constant folding. + diff_t expected_cf_diff = { + { " 2: IntConstant [5]\n", " 2: IntConstant\n" }, + { " 5: Neg(2) [8]\n", " 12: IntConstant [8]\n" }, + { " 8: Return(5)\n", " 8: Return(12)\n" } + }; + std::string expected_after_cf = Patch(expected_before, expected_cf_diff); + + // Check the value of the computed constant. + auto check_after_cf = [](HGraph* graph) { + HInstruction* inst = graph->GetBlock(1)->GetFirstInstruction(); + ASSERT_TRUE(inst->IsIntConstant()); + ASSERT_EQ(inst->AsIntConstant()->GetValue(), -1); + }; + + // Expected difference after dead code elimination. + diff_t expected_dce_diff = { + { " 2: IntConstant\n", removed }, + }; + std::string expected_after_dce = Patch(expected_after_cf, expected_dce_diff); + + TestCode(data, + expected_before, + expected_after_cf, + expected_after_dce, + check_after_cf); +} + +/** * Tiny three-register program exercising int constant folding on addition. * * 16-bit @@ -78,7 +137,7 @@ static void TestCode(const uint16_t* data, * v2 <- v0 + v1 2. add-int v2, v0, v1 * return v2 4. return v2 */ -TEST(ConstantPropagation, IntConstantFoldingOnAddition1) { +TEST(ConstantFolding, IntConstantFoldingOnAddition1) { const uint16_t data[] = THREE_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 0 << 8 | 1 << 12, Instruction::CONST_4 | 1 << 8 | 2 << 12, @@ -97,17 +156,17 @@ TEST(ConstantPropagation, IntConstantFoldingOnAddition1) { "BasicBlock 2, pred: 1\n" " 13: Exit\n"; - // Expected difference after constant propagation. - diff_t expected_cp_diff = { + // Expected difference after constant folding. + diff_t expected_cf_diff = { { " 3: IntConstant [9]\n", " 3: IntConstant\n" }, { " 5: IntConstant [9]\n", " 5: IntConstant\n" }, { " 9: Add(3, 5) [12]\n", " 16: IntConstant [12]\n" }, { " 12: Return(9)\n", " 12: Return(16)\n" } }; - std::string expected_after_cp = Patch(expected_before, expected_cp_diff); + std::string expected_after_cf = Patch(expected_before, expected_cf_diff); // Check the value of the computed constant. - auto check_after_cp = [](HGraph* graph) { + auto check_after_cf = [](HGraph* graph) { HInstruction* inst = graph->GetBlock(1)->GetFirstInstruction(); ASSERT_TRUE(inst->IsIntConstant()); ASSERT_EQ(inst->AsIntConstant()->GetValue(), 3); @@ -118,13 +177,13 @@ TEST(ConstantPropagation, IntConstantFoldingOnAddition1) { { " 3: IntConstant\n", removed }, { " 5: IntConstant\n", removed } }; - std::string expected_after_dce = Patch(expected_after_cp, expected_dce_diff); + std::string expected_after_dce = Patch(expected_after_cf, expected_dce_diff); TestCode(data, expected_before, - expected_after_cp, + expected_after_cf, expected_after_dce, - check_after_cp); + check_after_cf); } /** @@ -142,7 +201,7 @@ TEST(ConstantPropagation, IntConstantFoldingOnAddition1) { * v2 <- v0 + v1 6. add-int v2, v0, v1 * return v2 8. return v2 */ -TEST(ConstantPropagation, IntConstantFoldingOnAddition2) { +TEST(ConstantFolding, IntConstantFoldingOnAddition2) { const uint16_t data[] = THREE_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 0 << 8 | 1 << 12, Instruction::CONST_4 | 1 << 8 | 2 << 12, @@ -169,8 +228,8 @@ TEST(ConstantPropagation, IntConstantFoldingOnAddition2) { "BasicBlock 2, pred: 1\n" " 25: Exit\n"; - // Expected difference after constant propagation. - diff_t expected_cp_diff = { + // Expected difference after constant folding. + diff_t expected_cf_diff = { { " 3: IntConstant [9]\n", " 3: IntConstant\n" }, { " 5: IntConstant [9]\n", " 5: IntConstant\n" }, { " 11: IntConstant [17]\n", " 11: IntConstant\n" }, @@ -180,10 +239,10 @@ TEST(ConstantPropagation, IntConstantFoldingOnAddition2) { { " 21: Add(9, 17) [24]\n", " 30: IntConstant [24]\n" }, { " 24: Return(21)\n", " 24: Return(30)\n" } }; - std::string expected_after_cp = Patch(expected_before, expected_cp_diff); + std::string expected_after_cf = Patch(expected_before, expected_cf_diff); // Check the values of the computed constants. - auto check_after_cp = [](HGraph* graph) { + auto check_after_cf = [](HGraph* graph) { HInstruction* inst1 = graph->GetBlock(1)->GetFirstInstruction(); ASSERT_TRUE(inst1->IsIntConstant()); ASSERT_EQ(inst1->AsIntConstant()->GetValue(), 3); @@ -204,13 +263,13 @@ TEST(ConstantPropagation, IntConstantFoldingOnAddition2) { { " 28: IntConstant\n", removed }, { " 29: IntConstant\n", removed } }; - std::string expected_after_dce = Patch(expected_after_cp, expected_dce_diff); + std::string expected_after_dce = Patch(expected_after_cf, expected_dce_diff); TestCode(data, expected_before, - expected_after_cp, + expected_after_cf, expected_after_dce, - check_after_cp); + check_after_cf); } /** @@ -224,7 +283,7 @@ TEST(ConstantPropagation, IntConstantFoldingOnAddition2) { * v2 <- v0 - v1 2. sub-int v2, v0, v1 * return v2 4. return v2 */ -TEST(ConstantPropagation, IntConstantFoldingOnSubtraction) { +TEST(ConstantFolding, IntConstantFoldingOnSubtraction) { const uint16_t data[] = THREE_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 0 << 8 | 3 << 12, Instruction::CONST_4 | 1 << 8 | 2 << 12, @@ -243,17 +302,17 @@ TEST(ConstantPropagation, IntConstantFoldingOnSubtraction) { "BasicBlock 2, pred: 1\n" " 13: Exit\n"; - // Expected difference after constant propagation. - diff_t expected_cp_diff = { + // Expected difference after constant folding. + diff_t expected_cf_diff = { { " 3: IntConstant [9]\n", " 3: IntConstant\n" }, { " 5: IntConstant [9]\n", " 5: IntConstant\n" }, { " 9: Sub(3, 5) [12]\n", " 16: IntConstant [12]\n" }, { " 12: Return(9)\n", " 12: Return(16)\n" } }; - std::string expected_after_cp = Patch(expected_before, expected_cp_diff); + std::string expected_after_cf = Patch(expected_before, expected_cf_diff); // Check the value of the computed constant. - auto check_after_cp = [](HGraph* graph) { + auto check_after_cf = [](HGraph* graph) { HInstruction* inst = graph->GetBlock(1)->GetFirstInstruction(); ASSERT_TRUE(inst->IsIntConstant()); ASSERT_EQ(inst->AsIntConstant()->GetValue(), 1); @@ -264,13 +323,13 @@ TEST(ConstantPropagation, IntConstantFoldingOnSubtraction) { { " 3: IntConstant\n", removed }, { " 5: IntConstant\n", removed } }; - std::string expected_after_dce = Patch(expected_after_cp, expected_dce_diff); + std::string expected_after_dce = Patch(expected_after_cf, expected_dce_diff); TestCode(data, expected_before, - expected_after_cp, + expected_after_cf, expected_after_dce, - check_after_cp); + check_after_cf); } #define SIX_REGISTERS_CODE_ITEM(...) \ @@ -289,7 +348,7 @@ TEST(ConstantPropagation, IntConstantFoldingOnSubtraction) { * (v0, v1) + (v1, v2) 4. add-long v4, v0, v2 * return (v4, v5) 6. return-wide v4 */ -TEST(ConstantPropagation, LongConstantFoldingOnAddition) { +TEST(ConstantFolding, LongConstantFoldingOnAddition) { const uint16_t data[] = SIX_REGISTERS_CODE_ITEM( Instruction::CONST_WIDE_16 | 0 << 8, 1, Instruction::CONST_WIDE_16 | 2 << 8, 2, @@ -308,17 +367,17 @@ TEST(ConstantPropagation, LongConstantFoldingOnAddition) { "BasicBlock 2, pred: 1\n" " 16: Exit\n"; - // Expected difference after constant propagation. - diff_t expected_cp_diff = { + // Expected difference after constant folding. + diff_t expected_cf_diff = { { " 6: LongConstant [12]\n", " 6: LongConstant\n" }, { " 8: LongConstant [12]\n", " 8: LongConstant\n" }, { " 12: Add(6, 8) [15]\n", " 19: LongConstant [15]\n" }, { " 15: Return(12)\n", " 15: Return(19)\n" } }; - std::string expected_after_cp = Patch(expected_before, expected_cp_diff); + std::string expected_after_cf = Patch(expected_before, expected_cf_diff); // Check the value of the computed constant. - auto check_after_cp = [](HGraph* graph) { + auto check_after_cf = [](HGraph* graph) { HInstruction* inst = graph->GetBlock(1)->GetFirstInstruction(); ASSERT_TRUE(inst->IsLongConstant()); ASSERT_EQ(inst->AsLongConstant()->GetValue(), 3); @@ -329,13 +388,13 @@ TEST(ConstantPropagation, LongConstantFoldingOnAddition) { { " 6: LongConstant\n", removed }, { " 8: LongConstant\n", removed } }; - std::string expected_after_dce = Patch(expected_after_cp, expected_dce_diff); + std::string expected_after_dce = Patch(expected_after_cf, expected_dce_diff); TestCode(data, expected_before, - expected_after_cp, + expected_after_cf, expected_after_dce, - check_after_cp, + check_after_cf, Primitive::kPrimLong); } @@ -352,7 +411,7 @@ TEST(ConstantPropagation, LongConstantFoldingOnAddition) { * (v0, v1) - (v1, v2) 4. sub-long v4, v0, v2 * return (v4, v5) 6. return-wide v4 */ -TEST(ConstantPropagation, LongConstantFoldingOnSubtraction) { +TEST(ConstantFolding, LongConstantFoldingOnSubtraction) { const uint16_t data[] = SIX_REGISTERS_CODE_ITEM( Instruction::CONST_WIDE_16 | 0 << 8, 3, Instruction::CONST_WIDE_16 | 2 << 8, 2, @@ -371,17 +430,17 @@ TEST(ConstantPropagation, LongConstantFoldingOnSubtraction) { "BasicBlock 2, pred: 1\n" " 16: Exit\n"; - // Expected difference after constant propagation. - diff_t expected_cp_diff = { + // Expected difference after constant folding. + diff_t expected_cf_diff = { { " 6: LongConstant [12]\n", " 6: LongConstant\n" }, { " 8: LongConstant [12]\n", " 8: LongConstant\n" }, { " 12: Sub(6, 8) [15]\n", " 19: LongConstant [15]\n" }, { " 15: Return(12)\n", " 15: Return(19)\n" } }; - std::string expected_after_cp = Patch(expected_before, expected_cp_diff); + std::string expected_after_cf = Patch(expected_before, expected_cf_diff); // Check the value of the computed constant. - auto check_after_cp = [](HGraph* graph) { + auto check_after_cf = [](HGraph* graph) { HInstruction* inst = graph->GetBlock(1)->GetFirstInstruction(); ASSERT_TRUE(inst->IsLongConstant()); ASSERT_EQ(inst->AsLongConstant()->GetValue(), 1); @@ -392,13 +451,13 @@ TEST(ConstantPropagation, LongConstantFoldingOnSubtraction) { { " 6: LongConstant\n", removed }, { " 8: LongConstant\n", removed } }; - std::string expected_after_dce = Patch(expected_after_cp, expected_dce_diff); + std::string expected_after_dce = Patch(expected_after_cf, expected_dce_diff); TestCode(data, expected_before, - expected_after_cp, + expected_after_cf, expected_after_dce, - check_after_cp, + check_after_cf, Primitive::kPrimLong); } @@ -424,7 +483,7 @@ TEST(ConstantPropagation, LongConstantFoldingOnSubtraction) { * L3: v2 <- v1 + 4 11. add-int/lit16 v2, v1, #+4 * return v2 13. return v2 */ -TEST(ConstantPropagation, IntConstantFoldingAndJumps) { +TEST(ConstantFolding, IntConstantFoldingAndJumps) { const uint16_t data[] = THREE_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 0 << 8 | 0 << 12, Instruction::CONST_4 | 1 << 8 | 1 << 12, @@ -462,8 +521,8 @@ TEST(ConstantPropagation, IntConstantFoldingAndJumps) { "BasicBlock 5, pred: 4\n" " 29: Exit\n"; - // Expected difference after constant propagation. - diff_t expected_cp_diff = { + // Expected difference after constant folding. + diff_t expected_cf_diff = { { " 3: IntConstant [9]\n", " 3: IntConstant\n" }, { " 5: IntConstant [9]\n", " 5: IntConstant []\n" }, { " 13: IntConstant [14]\n", " 13: IntConstant\n" }, @@ -475,10 +534,10 @@ TEST(ConstantPropagation, IntConstantFoldingAndJumps) { { " 25: Add(14, 24) [28]\n", " 35: IntConstant [28]\n" }, { " 28: Return(25)\n", " 28: Return(35)\n"} }; - std::string expected_after_cp = Patch(expected_before, expected_cp_diff); + std::string expected_after_cf = Patch(expected_before, expected_cf_diff); // Check the values of the computed constants. - auto check_after_cp = [](HGraph* graph) { + auto check_after_cf = [](HGraph* graph) { HInstruction* inst1 = graph->GetBlock(1)->GetFirstInstruction(); ASSERT_TRUE(inst1->IsIntConstant()); ASSERT_EQ(inst1->AsIntConstant()->GetValue(), 1); @@ -501,13 +560,13 @@ TEST(ConstantPropagation, IntConstantFoldingAndJumps) { { " 24: IntConstant\n", removed }, { " 34: IntConstant\n", removed }, }; - std::string expected_after_dce = Patch(expected_after_cp, expected_dce_diff); + std::string expected_after_dce = Patch(expected_after_cf, expected_dce_diff); TestCode(data, expected_before, - expected_after_cp, + expected_after_cf, expected_after_dce, - check_after_cp); + check_after_cf); } @@ -524,7 +583,7 @@ TEST(ConstantPropagation, IntConstantFoldingAndJumps) { * L1: v2 <- v0 + v1 5. add-int v2, v0, v1 * return-void 7. return */ -TEST(ConstantPropagation, ConstantCondition) { +TEST(ConstantFolding, ConstantCondition) { const uint16_t data[] = THREE_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 1 << 8 | 1 << 12, Instruction::CONST_4 | 0 << 8 | 0 << 12, @@ -553,17 +612,17 @@ TEST(ConstantPropagation, ConstantCondition) { "BasicBlock 5, pred: 1, succ: 3\n" " 21: Goto 3\n"; - // Expected difference after constant propagation. - diff_t expected_cp_diff = { + // Expected difference after constant folding. + diff_t expected_cf_diff = { { " 3: IntConstant [15, 22, 8]\n", " 3: IntConstant [15, 22]\n" }, { " 5: IntConstant [22, 8]\n", " 5: IntConstant [22]\n" }, { " 8: GreaterThanOrEqual(3, 5) [9]\n", " 23: IntConstant [9]\n" }, { " 9: If(8)\n", " 9: If(23)\n" } }; - std::string expected_after_cp = Patch(expected_before, expected_cp_diff); + std::string expected_after_cf = Patch(expected_before, expected_cf_diff); // Check the values of the computed constants. - auto check_after_cp = [](HGraph* graph) { + auto check_after_cf = [](HGraph* graph) { HInstruction* inst = graph->GetBlock(1)->GetFirstInstruction(); ASSERT_TRUE(inst->IsIntConstant()); ASSERT_EQ(inst->AsIntConstant()->GetValue(), 1); @@ -575,13 +634,13 @@ TEST(ConstantPropagation, ConstantCondition) { { " 22: Phi(3, 5) [15]\n", " 22: Phi(3, 5)\n" }, { " 15: Add(22, 3)\n", removed } }; - std::string expected_after_dce = Patch(expected_after_cp, expected_dce_diff); + std::string expected_after_dce = Patch(expected_after_cf, expected_dce_diff); TestCode(data, expected_before, - expected_after_cp, + expected_after_cf, expected_after_dce, - check_after_cp); + check_after_cf); } } // namespace art diff --git a/compiler/optimizing/dead_code_elimination.cc b/compiler/optimizing/dead_code_elimination.cc index fe2adc77d0..fc3dd01ef5 100644 --- a/compiler/optimizing/dead_code_elimination.cc +++ b/compiler/optimizing/dead_code_elimination.cc @@ -20,7 +20,7 @@ namespace art { -void DeadCodeElimination::Run() { +void HDeadCodeElimination::Run() { // Process basic blocks in post-order in the dominator tree, so that // a dead instruction depending on another dead instruction is // removed. @@ -35,7 +35,10 @@ void DeadCodeElimination::Run() { for (i.Advance(); !i.Done(); i.Advance()) { HInstruction* inst = i.Current(); DCHECK(!inst->IsControlFlow()); - if (!inst->HasSideEffects() && !inst->HasUses() && !inst->IsSuspendCheck()) { + if (!inst->HasSideEffects() + && !inst->CanThrow() + && !inst->IsSuspendCheck() + && !inst->HasUses()) { block->RemoveInstruction(inst); } } diff --git a/compiler/optimizing/dead_code_elimination.h b/compiler/optimizing/dead_code_elimination.h index 48739be494..a4446ae04d 100644 --- a/compiler/optimizing/dead_code_elimination.h +++ b/compiler/optimizing/dead_code_elimination.h @@ -18,6 +18,7 @@ #define ART_COMPILER_OPTIMIZING_DEAD_CODE_ELIMINATION_H_ #include "nodes.h" +#include "optimization.h" namespace art { @@ -25,17 +26,18 @@ namespace art { * Optimization pass performing dead code elimination (removal of * unused variables/instructions) on the SSA form. */ -class DeadCodeElimination : public ValueObject { +class HDeadCodeElimination : public HOptimization { public: - explicit DeadCodeElimination(HGraph* graph) - : graph_(graph) {} + HDeadCodeElimination(HGraph* graph, const HGraphVisualizer& visualizer) + : HOptimization(graph, true, kDeadCodeEliminationPassName, visualizer) {} - void Run(); + virtual void Run() OVERRIDE; - private: - HGraph* const graph_; + static constexpr const char* kDeadCodeEliminationPassName = + "dead_code_elimination"; - DISALLOW_COPY_AND_ASSIGN(DeadCodeElimination); + private: + DISALLOW_COPY_AND_ASSIGN(HDeadCodeElimination); }; } // namespace art diff --git a/compiler/optimizing/dead_code_elimination_test.cc b/compiler/optimizing/dead_code_elimination_test.cc index 245bcb21d5..0c6807482a 100644 --- a/compiler/optimizing/dead_code_elimination_test.cc +++ b/compiler/optimizing/dead_code_elimination_test.cc @@ -14,10 +14,11 @@ * limitations under the License. */ +#include "code_generator_x86.h" #include "dead_code_elimination.h" -#include "pretty_printer.h" #include "graph_checker.h" #include "optimizing_unit_test.h" +#include "pretty_printer.h" #include "gtest/gtest.h" @@ -39,16 +40,17 @@ static void TestCode(const uint16_t* data, std::string actual_before = printer_before.str(); ASSERT_EQ(actual_before, expected_before); - DeadCodeElimination(graph).Run(); + x86::CodeGeneratorX86 codegen(graph); + HGraphVisualizer visualizer(nullptr, graph, codegen, ""); + HDeadCodeElimination(graph, visualizer).Run(); + SSAChecker ssa_checker(&allocator, graph); + ssa_checker.Run(); + ASSERT_TRUE(ssa_checker.IsValid()); StringPrettyPrinter printer_after(graph); printer_after.VisitInsertionOrder(); std::string actual_after = printer_after.str(); ASSERT_EQ(actual_after, expected_after); - - SSAChecker ssa_checker(&allocator, graph); - ssa_checker.VisitInsertionOrder(); - ASSERT_TRUE(ssa_checker.IsValid()); } @@ -94,6 +96,7 @@ TEST(DeadCodeElimination, AdditionAndConditionalJump) { "BasicBlock 5, pred: 1, succ: 3\n" " 21: Goto 3\n"; + // Expected difference after dead code elimination. diff_t expected_diff = { { " 3: IntConstant [15, 22, 8]\n", " 3: IntConstant [22, 8]\n" }, { " 22: Phi(3, 5) [15]\n", " 22: Phi(3, 5)\n" }, @@ -164,7 +167,7 @@ TEST(DeadCodeElimination, AdditionsAndInconditionalJumps) { "BasicBlock 5, pred: 4\n" " 28: Exit\n"; - // Expected difference after constant propagation. + // Expected difference after dead code elimination. diff_t expected_diff = { { " 13: IntConstant [14]\n", removed }, { " 24: IntConstant [25]\n", removed }, diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc index 589b44a167..743ffc46bf 100644 --- a/compiler/optimizing/graph_checker.cc +++ b/compiler/optimizing/graph_checker.cc @@ -264,21 +264,38 @@ void SSAChecker::CheckLoop(HBasicBlock* loop_header) { void SSAChecker::VisitInstruction(HInstruction* instruction) { super_type::VisitInstruction(instruction); - // Ensure an instruction dominates all its uses (or in the present - // case, that all uses of an instruction (used as input) are - // dominated by its definition). - for (HInputIterator input_it(instruction); !input_it.Done(); - input_it.Advance()) { - HInstruction* input = input_it.Current(); - if (!input->Dominates(instruction)) { + // Ensure an instruction dominates all its uses. + for (HUseIterator<HInstruction> use_it(instruction->GetUses()); + !use_it.Done(); use_it.Advance()) { + HInstruction* use = use_it.Current()->GetUser(); + if (!use->IsPhi() && !instruction->StrictlyDominates(use)) { std::stringstream error; - error << "Instruction " << input->GetId() - << " in block " << input->GetBlock()->GetBlockId() - << " does not dominate use " << instruction->GetId() - << " in block " << current_block_->GetBlockId() << "."; + error << "Instruction " << instruction->GetId() + << " in block " << current_block_->GetBlockId() + << " does not dominate use " << use->GetId() + << " in block " << use->GetBlock()->GetBlockId() << "."; errors_.Insert(error.str()); } } + + // Ensure an instruction having an environment is dominated by the + // instructions contained in the environment. + HEnvironment* environment = instruction->GetEnvironment(); + if (environment != nullptr) { + for (size_t i = 0, e = environment->Size(); i < e; ++i) { + HInstruction* env_instruction = environment->GetInstructionAt(i); + if (env_instruction != nullptr + && !env_instruction->StrictlyDominates(instruction)) { + std::stringstream error; + error << "Instruction " << env_instruction->GetId() + << " in environment of instruction " << instruction->GetId() + << " from block " << current_block_->GetBlockId() + << " does not dominate instruction " << instruction->GetId() + << "."; + errors_.Insert(error.str()); + } + } + } } void SSAChecker::VisitPhi(HPhi* phi) { diff --git a/compiler/optimizing/graph_checker.h b/compiler/optimizing/graph_checker.h index 34a770b5f3..badf21d946 100644 --- a/compiler/optimizing/graph_checker.h +++ b/compiler/optimizing/graph_checker.h @@ -19,15 +19,22 @@ #include "nodes.h" +#include <ostream> + namespace art { // A control-flow graph visitor performing various checks. class GraphChecker : public HGraphVisitor { public: - GraphChecker(ArenaAllocator* allocator, HGraph* graph) + GraphChecker(ArenaAllocator* allocator, HGraph* graph, + const char* dump_prefix = "art::GraphChecker: ") : HGraphVisitor(graph), allocator_(allocator), - errors_(allocator, 0) {} + errors_(allocator, 0), + dump_prefix_(dump_prefix) {} + + // Check the whole graph (in insertion order). + virtual void Run() { VisitInsertionOrder(); } // Check `block`. virtual void VisitBasicBlock(HBasicBlock* block) OVERRIDE; @@ -45,6 +52,13 @@ class GraphChecker : public HGraphVisitor { return errors_; } + // Print detected errors on output stream `os`. + void Dump(std::ostream& os) const { + for (size_t i = 0, e = errors_.Size(); i < e; ++i) { + os << dump_prefix_ << errors_.Get(i) << std::endl; + } + } + protected: ArenaAllocator* const allocator_; // The block currently visited. @@ -53,6 +67,9 @@ class GraphChecker : public HGraphVisitor { GrowableArray<std::string> errors_; private: + // String displayed before dumped errors. + const char* const dump_prefix_; + DISALLOW_COPY_AND_ASSIGN(GraphChecker); }; @@ -63,7 +80,15 @@ class SSAChecker : public GraphChecker { typedef GraphChecker super_type; SSAChecker(ArenaAllocator* allocator, HGraph* graph) - : GraphChecker(allocator, graph) {} + : GraphChecker(allocator, graph, "art::SSAChecker: ") {} + + // Check the whole graph (in reverse post-order). + virtual void Run() { + // VisitReversePostOrder is used instead of VisitInsertionOrder, + // as the latter might visit dead blocks removed by the dominator + // computation. + VisitReversePostOrder(); + } // Perform SSA form checks on `block`. virtual void VisitBasicBlock(HBasicBlock* block) OVERRIDE; diff --git a/compiler/optimizing/graph_checker_test.cc b/compiler/optimizing/graph_checker_test.cc index ea0692088d..39def82007 100644 --- a/compiler/optimizing/graph_checker_test.cc +++ b/compiler/optimizing/graph_checker_test.cc @@ -51,7 +51,7 @@ static void TestCode(const uint16_t* data) { ASSERT_NE(graph, nullptr); GraphChecker graph_checker(&allocator, graph); - graph_checker.VisitInsertionOrder(); + graph_checker.Run(); ASSERT_TRUE(graph_checker.IsValid()); } @@ -65,7 +65,7 @@ static void TestCodeSSA(const uint16_t* data) { graph->TransformToSSA(); SSAChecker ssa_checker(&allocator, graph); - ssa_checker.VisitInsertionOrder(); + ssa_checker.Run(); ASSERT_TRUE(ssa_checker.IsValid()); } @@ -113,13 +113,13 @@ TEST(GraphChecker, InconsistentPredecessorsAndSuccessors) { HGraph* graph = CreateSimpleCFG(&allocator); GraphChecker graph_checker(&allocator, graph); - graph_checker.VisitInsertionOrder(); + graph_checker.Run(); ASSERT_TRUE(graph_checker.IsValid()); // Remove the entry block from the exit block's predecessors, to create an // inconsistent successor/predecessor relation. graph->GetExitBlock()->RemovePredecessor(graph->GetEntryBlock()); - graph_checker.VisitInsertionOrder(); + graph_checker.Run(); ASSERT_FALSE(graph_checker.IsValid()); } @@ -131,7 +131,7 @@ TEST(GraphChecker, BlockEndingWithNonBranchInstruction) { HGraph* graph = CreateSimpleCFG(&allocator); GraphChecker graph_checker(&allocator, graph); - graph_checker.VisitInsertionOrder(); + graph_checker.Run(); ASSERT_TRUE(graph_checker.IsValid()); // Remove the sole instruction of the exit block (composed of a @@ -141,7 +141,7 @@ TEST(GraphChecker, BlockEndingWithNonBranchInstruction) { HInstruction* last_inst = exit_block->GetLastInstruction(); exit_block->RemoveInstruction(last_inst); - graph_checker.VisitInsertionOrder(); + graph_checker.Run(); ASSERT_FALSE(graph_checker.IsValid()); } diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc index b4eb89d30b..4ed2156241 100644 --- a/compiler/optimizing/graph_visualizer.cc +++ b/compiler/optimizing/graph_visualizer.cc @@ -120,13 +120,11 @@ class HGraphVisualizerPrinter : public HGraphVisitor { output_<< std::endl; } - void DumpLocation(Location location, Primitive::Type type) { + void DumpLocation(Location location) { if (location.IsRegister()) { - if (type == Primitive::kPrimDouble || type == Primitive::kPrimFloat) { - codegen_.DumpFloatingPointRegister(output_, location.reg()); - } else { - codegen_.DumpCoreRegister(output_, location.reg()); - } + codegen_.DumpCoreRegister(output_, location.reg()); + } else if (location.IsFpuRegister()) { + codegen_.DumpFloatingPointRegister(output_, location.reg()); } else if (location.IsConstant()) { output_ << "constant"; HConstant* constant = location.GetConstant(); @@ -150,9 +148,9 @@ class HGraphVisualizerPrinter : public HGraphVisitor { output_ << " ("; for (size_t i = 0, e = instruction->NumMoves(); i < e; ++i) { MoveOperands* move = instruction->MoveOperandsAt(i); - DumpLocation(move->GetSource(), Primitive::kPrimInt); + DumpLocation(move->GetSource()); output_ << " -> "; - DumpLocation(move->GetDestination(), Primitive::kPrimInt); + DumpLocation(move->GetDestination()); if (i + 1 != e) { output_ << ", "; } @@ -183,13 +181,13 @@ class HGraphVisualizerPrinter : public HGraphVisitor { if (locations != nullptr) { output_ << " ( "; for (size_t i = 0; i < instruction->InputCount(); ++i) { - DumpLocation(locations->InAt(i), instruction->InputAt(i)->GetType()); + DumpLocation(locations->InAt(i)); output_ << " "; } output_ << ")"; if (locations->Out().IsValid()) { output_ << " -> "; - DumpLocation(locations->Out(), instruction->GetType()); + DumpLocation(locations->Out()); } } output_ << " (liveness: " << instruction->GetLifetimePosition() << ")"; @@ -309,7 +307,7 @@ HGraphVisualizer::HGraphVisualizer(std::ostream* output, printer.EndTag("compilation"); } -void HGraphVisualizer::DumpGraph(const char* pass_name) { +void HGraphVisualizer::DumpGraph(const char* pass_name) const { if (!is_enabled_) { return; } diff --git a/compiler/optimizing/graph_visualizer.h b/compiler/optimizing/graph_visualizer.h index f17ba3bbac..4d8bec2422 100644 --- a/compiler/optimizing/graph_visualizer.h +++ b/compiler/optimizing/graph_visualizer.h @@ -17,6 +17,8 @@ #ifndef ART_COMPILER_OPTIMIZING_GRAPH_VISUALIZER_H_ #define ART_COMPILER_OPTIMIZING_GRAPH_VISUALIZER_H_ +#include <ostream> + #include "base/value_object.h" namespace art { @@ -61,7 +63,7 @@ class HGraphVisualizer : public ValueObject { * If this visualizer is enabled, emit the compilation information * in `output_`. */ - void DumpGraph(const char* pass_name); + void DumpGraph(const char* pass_name) const; private: std::ostream* const output_; diff --git a/compiler/optimizing/gvn.h b/compiler/optimizing/gvn.h index 41b3ceb509..a98d714476 100644 --- a/compiler/optimizing/gvn.h +++ b/compiler/optimizing/gvn.h @@ -17,7 +17,6 @@ #ifndef ART_COMPILER_OPTIMIZING_GVN_H_ #define ART_COMPILER_OPTIMIZING_GVN_H_ -#include <gtest/gtest.h> #include "nodes.h" namespace art { @@ -221,7 +220,7 @@ class GlobalValueNumberer : public ValueObject { // Mark visisted blocks. Only used for debugging. GrowableArray<bool> visited_; - FRIEND_TEST(GVNTest, LoopSideEffects); + ART_FRIEND_TEST(GVNTest, LoopSideEffects); DISALLOW_COPY_AND_ASSIGN(GlobalValueNumberer); }; diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index 2d9e35c3b6..29eabe7e29 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -50,7 +50,7 @@ void InstructionSimplifier::VisitEqual(HEqual* equal) { // Replace (bool_value == 0) with !bool_value DCHECK_EQ(input2->AsIntConstant()->GetValue(), 0); equal->GetBlock()->ReplaceAndRemoveInstructionWith( - equal, new (GetGraph()->GetArena()) HNot(input1)); + equal, new (GetGraph()->GetArena()) HNot(Primitive::kPrimBoolean, input1)); } } } diff --git a/compiler/optimizing/live_ranges_test.cc b/compiler/optimizing/live_ranges_test.cc index eafc3e1483..89c949563b 100644 --- a/compiler/optimizing/live_ranges_test.cc +++ b/compiler/optimizing/live_ranges_test.cc @@ -73,9 +73,9 @@ TEST(LiveRangesTest, CFG1) { LiveRange* range = interval->GetFirstRange(); ASSERT_EQ(2u, range->GetStart()); // Last use is the return instruction. - ASSERT_EQ(9u, range->GetEnd()); + ASSERT_EQ(8u, range->GetEnd()); HBasicBlock* block = graph->GetBlocks().Get(1); - ASSERT_TRUE(block->GetLastInstruction()->AsReturn() != nullptr); + ASSERT_TRUE(block->GetLastInstruction()->IsReturn()); ASSERT_EQ(8u, block->GetLastInstruction()->GetLifetimePosition()); ASSERT_TRUE(range->GetNext() == nullptr); } @@ -119,9 +119,9 @@ TEST(LiveRangesTest, CFG2) { LiveRange* range = interval->GetFirstRange(); ASSERT_EQ(2u, range->GetStart()); // Last use is the return instruction. - ASSERT_EQ(23u, range->GetEnd()); + ASSERT_EQ(22u, range->GetEnd()); HBasicBlock* block = graph->GetBlocks().Get(3); - ASSERT_TRUE(block->GetLastInstruction()->AsReturn() != nullptr); + ASSERT_TRUE(block->GetLastInstruction()->IsReturn()); ASSERT_EQ(22u, block->GetLastInstruction()->GetLifetimePosition()); ASSERT_TRUE(range->GetNext() == nullptr); } @@ -193,7 +193,7 @@ TEST(LiveRangesTest, CFG3) { range = interval->GetFirstRange(); ASSERT_EQ(22u, liveness.GetInstructionFromSsaIndex(2)->GetLifetimePosition()); ASSERT_EQ(22u, range->GetStart()); - ASSERT_EQ(25u, range->GetEnd()); + ASSERT_EQ(24u, range->GetEnd()); ASSERT_TRUE(range->GetNext() == nullptr); } @@ -263,7 +263,7 @@ TEST(LiveRangesTest, Loop1) { range = interval->GetFirstRange(); // The instruction is live until the return instruction after the loop. ASSERT_EQ(6u, range->GetStart()); - ASSERT_EQ(27u, range->GetEnd()); + ASSERT_EQ(26u, range->GetEnd()); ASSERT_TRUE(range->GetNext() == nullptr); // Test for the phi. @@ -271,7 +271,7 @@ TEST(LiveRangesTest, Loop1) { range = interval->GetFirstRange(); // Instruction is consumed by the if. ASSERT_EQ(14u, range->GetStart()); - ASSERT_EQ(16u, range->GetEnd()); + ASSERT_EQ(17u, range->GetEnd()); ASSERT_TRUE(range->GetNext() == nullptr); } @@ -338,7 +338,7 @@ TEST(LiveRangesTest, Loop2) { range = range->GetNext(); ASSERT_TRUE(range != nullptr); ASSERT_EQ(24u, range->GetStart()); - ASSERT_EQ(27u, range->GetEnd()); + ASSERT_EQ(26u, range->GetEnd()); // Test for the add instruction. HAdd* add = liveness.GetInstructionFromSsaIndex(2)->AsAdd(); @@ -410,7 +410,7 @@ TEST(LiveRangesTest, CFG4) { interval = liveness.GetInstructionFromSsaIndex(1)->GetLiveInterval(); range = interval->GetFirstRange(); ASSERT_EQ(4u, range->GetStart()); - ASSERT_EQ(29u, range->GetEnd()); + ASSERT_EQ(28u, range->GetEnd()); ASSERT_TRUE(range->GetNext() == nullptr); // Test for the first add. diff --git a/compiler/optimizing/locations.cc b/compiler/optimizing/locations.cc index 1637484799..ed5e260a5b 100644 --- a/compiler/optimizing/locations.cc +++ b/compiler/optimizing/locations.cc @@ -25,16 +25,14 @@ LocationSummary::LocationSummary(HInstruction* instruction, CallKind call_kind) temps_(instruction->GetBlock()->GetGraph()->GetArena(), 0), environment_(instruction->GetBlock()->GetGraph()->GetArena(), instruction->EnvironmentSize()), - dies_at_entry_(instruction->GetBlock()->GetGraph()->GetArena(), instruction->InputCount()), + output_overlaps_(true), call_kind_(call_kind), stack_mask_(nullptr), register_mask_(0), live_registers_() { inputs_.SetSize(instruction->InputCount()); - dies_at_entry_.SetSize(instruction->InputCount()); for (size_t i = 0; i < instruction->InputCount(); ++i) { inputs_.Put(i, Location()); - dies_at_entry_.Put(i, false); } environment_.SetSize(instruction->EnvironmentSize()); for (size_t i = 0; i < instruction->EnvironmentSize(); ++i) { diff --git a/compiler/optimizing/locations.h b/compiler/optimizing/locations.h index dcf70f27b0..11bcd78521 100644 --- a/compiler/optimizing/locations.h +++ b/compiler/optimizing/locations.h @@ -34,7 +34,7 @@ class HInstruction; */ class Location : public ValueObject { public: - static constexpr bool kDiesAtEntry = true; + static constexpr bool kNoOutputOverlap = false; enum Kind { kInvalid = 0, @@ -373,8 +373,7 @@ class LocationSummary : public ArenaObject { LocationSummary(HInstruction* instruction, CallKind call_kind = kNoCall); - void SetInAt(uint32_t at, Location location, bool dies_at_entry = false) { - dies_at_entry_.Put(at, dies_at_entry); + void SetInAt(uint32_t at, Location location) { inputs_.Put(at, location); } @@ -386,7 +385,8 @@ class LocationSummary : public ArenaObject { return inputs_.Size(); } - void SetOut(Location location) { + void SetOut(Location location, bool overlaps = true) { + output_overlaps_ = overlaps; output_ = Location(location); } @@ -449,23 +449,30 @@ class LocationSummary : public ArenaObject { return &live_registers_; } - bool InputOverlapsWithOutputOrTemp(uint32_t input, bool is_environment) const { + bool InputOverlapsWithOutputOrTemp(uint32_t input_index, bool is_environment) const { if (is_environment) return true; - Location location = Out(); - if (input == 0 && location.IsUnallocated() && location.GetPolicy() == Location::kSameAsFirstInput) { + if ((input_index == 0) + && output_.IsUnallocated() + && (output_.GetPolicy() == Location::kSameAsFirstInput)) { return false; } - if (dies_at_entry_.Get(input)) { + if (inputs_.Get(input_index).IsRegister() || inputs_.Get(input_index).IsFpuRegister()) { return false; } return true; } + bool OutputOverlapsWithInputs() const { + return output_overlaps_; + } + private: GrowableArray<Location> inputs_; GrowableArray<Location> temps_; GrowableArray<Location> environment_; - GrowableArray<bool> dies_at_entry_; + // Whether the output overlaps with any of the inputs. If it overlaps, then it cannot + // share the same register as the inputs. + bool output_overlaps_; Location output_; const CallKind call_kind_; diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index 3a0b40c5de..d624ad5e5e 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -317,8 +317,8 @@ static void UpdateInputsUsers(HInstruction* instruction) { } void HBasicBlock::InsertInstructionBefore(HInstruction* instruction, HInstruction* cursor) { - DCHECK(cursor->AsPhi() == nullptr); - DCHECK(instruction->AsPhi() == nullptr); + DCHECK(!cursor->IsPhi()); + DCHECK(!instruction->IsPhi()); DCHECK_EQ(instruction->GetId(), -1); DCHECK_NE(cursor->GetId(), -1); DCHECK_EQ(cursor->GetBlock(), this); @@ -363,6 +363,25 @@ void HBasicBlock::AddPhi(HPhi* phi) { Add(&phis_, this, phi); } +void HBasicBlock::InsertPhiAfter(HPhi* phi, HPhi* cursor) { + DCHECK_EQ(phi->GetId(), -1); + DCHECK_NE(cursor->GetId(), -1); + DCHECK_EQ(cursor->GetBlock(), this); + if (cursor->next_ == nullptr) { + cursor->next_ = phi; + phi->previous_ = cursor; + DCHECK(phi->next_ == nullptr); + } else { + phi->next_ = cursor->next_; + phi->previous_ = cursor; + cursor->next_ = phi; + phi->next_->previous_ = phi; + } + phi->SetBlock(this); + phi->SetId(GetGraph()->GetNextInstructionId()); + UpdateInputsUsers(phi); +} + static void Remove(HInstructionList* instruction_list, HBasicBlock* block, HInstruction* instruction) { @@ -472,7 +491,11 @@ bool HInstructionList::FoundBefore(const HInstruction* instruction1, return true; } -bool HInstruction::Dominates(HInstruction* other_instruction) const { +bool HInstruction::StrictlyDominates(HInstruction* other_instruction) const { + if (other_instruction == this) { + // An instruction does not strictly dominate itself. + return false; + } HBasicBlock* block = GetBlock(); HBasicBlock* other_block = other_instruction->GetBlock(); if (block != other_block) { @@ -527,6 +550,12 @@ void HInstruction::ReplaceWith(HInstruction* other) { env_uses_ = nullptr; } +void HInstruction::ReplaceInput(HInstruction* replacement, size_t index) { + InputAt(index)->RemoveUser(this, index); + SetRawInputAt(index, replacement); + replacement->AddUseAt(this, index); +} + size_t HInstruction::EnvironmentSize() const { return HasEnvironment() ? environment_->Size() : 0; } @@ -553,6 +582,12 @@ void HGraphVisitor::VisitInsertionOrder() { } } +void HGraphVisitor::VisitReversePostOrder() { + for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) { + VisitBasicBlock(it.Current()); + } +} + void HGraphVisitor::VisitBasicBlock(HBasicBlock* block) { for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) { it.Current()->Accept(this); @@ -562,15 +597,30 @@ void HGraphVisitor::VisitBasicBlock(HBasicBlock* block) { } } -HConstant* HBinaryOperation::TryStaticEvaluation(ArenaAllocator* allocator) const { +HConstant* HUnaryOperation::TryStaticEvaluation() const { + if (GetInput()->IsIntConstant()) { + int32_t value = Evaluate(GetInput()->AsIntConstant()->GetValue()); + return new(GetBlock()->GetGraph()->GetArena()) HIntConstant(value); + } else if (GetInput()->IsLongConstant()) { + // TODO: Implement static evaluation of long unary operations. + // + // Do not exit with a fatal condition here. Instead, simply + // return `nullptr' to notify the caller that this instruction + // cannot (yet) be statically evaluated. + return nullptr; + } + return nullptr; +} + +HConstant* HBinaryOperation::TryStaticEvaluation() const { if (GetLeft()->IsIntConstant() && GetRight()->IsIntConstant()) { int32_t value = Evaluate(GetLeft()->AsIntConstant()->GetValue(), GetRight()->AsIntConstant()->GetValue()); - return new(allocator) HIntConstant(value); + return new(GetBlock()->GetGraph()->GetArena()) HIntConstant(value); } else if (GetLeft()->IsLongConstant() && GetRight()->IsLongConstant()) { int64_t value = Evaluate(GetLeft()->AsLongConstant()->GetValue(), GetRight()->AsLongConstant()->GetValue()); - return new(allocator) HLongConstant(value); + return new(GetBlock()->GetGraph()->GetArena()) HLongConstant(value); } return nullptr; } diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 677a4f8591..7adb84008a 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -399,6 +399,7 @@ class HBasicBlock : public ArenaObject { void ReplaceAndRemoveInstructionWith(HInstruction* initial, HInstruction* replacement); void AddPhi(HPhi* phi); + void InsertPhiAfter(HPhi* instruction, HPhi* cursor); void RemovePhi(HPhi* phi); bool IsLoopHeader() const { @@ -484,7 +485,7 @@ class HBasicBlock : public ArenaObject { M(Local, Instruction) \ M(LongConstant, Constant) \ M(NewInstance, Instruction) \ - M(Not, Instruction) \ + M(Not, UnaryOperation) \ M(ParameterValue, Instruction) \ M(ParallelMove, Instruction) \ M(Phi, Instruction) \ @@ -502,11 +503,17 @@ class HBasicBlock : public ArenaObject { M(NullCheck, Instruction) \ M(Temporary, Instruction) \ M(SuspendCheck, Instruction) \ + M(Mul, BinaryOperation) \ + M(Neg, UnaryOperation) \ + M(FloatConstant, Constant) \ + M(DoubleConstant, Constant) \ + M(NewArray, Instruction) \ #define FOR_EACH_INSTRUCTION(M) \ FOR_EACH_CONCRETE_INSTRUCTION(M) \ M(Constant, Instruction) \ - M(BinaryOperation, Instruction) \ + M(UnaryOperation, Instruction) \ + M(BinaryOperation, Instruction) \ M(Invoke, Instruction) #define FORWARD_DECLARATION(type, super) class H##type; @@ -650,6 +657,7 @@ class HInstruction : public ArenaObject { virtual bool NeedsEnvironment() const { return false; } virtual bool IsControlFlow() const { return false; } + virtual bool CanThrow() const { return false; } bool HasSideEffects() const { return side_effects_.HasSideEffects(); } void AddUseAt(HInstruction* user, size_t index) { @@ -682,9 +690,10 @@ class HInstruction : public ArenaObject { return result; } - // Does this instruction dominate `other_instruction`? Aborts if - // this instruction and `other_instruction` are both phis. - bool Dominates(HInstruction* other_instruction) const; + // Does this instruction strictly dominate `other_instruction`? + // Returns false if this instruction and `other_instruction` are the same. + // Aborts if this instruction and `other_instruction` are both phis. + bool StrictlyDominates(HInstruction* other_instruction) const; int GetId() const { return id_; } void SetId(int id) { id_ = id; } @@ -705,6 +714,7 @@ class HInstruction : public ArenaObject { void SetLocations(LocationSummary* locations) { locations_ = locations; } void ReplaceWith(HInstruction* instruction); + void ReplaceInput(HInstruction* replacement, size_t index); bool HasOnlyOneUse() const { return uses_ != nullptr && uses_->GetTail() == nullptr; @@ -990,8 +1000,8 @@ class HExpression : public HTemplateInstruction<N> { virtual Primitive::Type GetType() const { return type_; } - private: - const Primitive::Type type_; + protected: + Primitive::Type type_; }; // Represents dex's RETURN_VOID opcode. A HReturnVoid is a control flow @@ -1083,6 +1093,34 @@ class HIf : public HTemplateInstruction<1> { DISALLOW_COPY_AND_ASSIGN(HIf); }; +class HUnaryOperation : public HExpression<1> { + public: + HUnaryOperation(Primitive::Type result_type, HInstruction* input) + : HExpression(result_type, SideEffects::None()) { + SetRawInputAt(0, input); + } + + HInstruction* GetInput() const { return InputAt(0); } + Primitive::Type GetResultType() const { return GetType(); } + + virtual bool CanBeMoved() const { return true; } + virtual bool InstructionDataEquals(HInstruction* other) const { return true; } + + // Try to statically evaluate `operation` and return a HConstant + // containing the result of this evaluation. If `operation` cannot + // be evaluated as a constant, return nullptr. + HConstant* TryStaticEvaluation() const; + + // Apply this operation to `x`. + virtual int32_t Evaluate(int32_t x) const = 0; + virtual int64_t Evaluate(int64_t x) const = 0; + + DECLARE_INSTRUCTION(UnaryOperation); + + private: + DISALLOW_COPY_AND_ASSIGN(HUnaryOperation); +}; + class HBinaryOperation : public HExpression<2> { public: HBinaryOperation(Primitive::Type result_type, @@ -1101,10 +1139,10 @@ class HBinaryOperation : public HExpression<2> { virtual bool CanBeMoved() const { return true; } virtual bool InstructionDataEquals(HInstruction* other) const { return true; } - // Try to statically evaluate `operation` and return an HConstant + // Try to statically evaluate `operation` and return a HConstant // containing the result of this evaluation. If `operation` cannot // be evaluated as a constant, return nullptr. - HConstant* TryStaticEvaluation(ArenaAllocator* allocator) const; + HConstant* TryStaticEvaluation() const; // Apply this operation to `x` and `y`. virtual int32_t Evaluate(int32_t x, int32_t y) const = 0; @@ -1368,6 +1406,48 @@ class HConstant : public HExpression<0> { DISALLOW_COPY_AND_ASSIGN(HConstant); }; +class HFloatConstant : public HConstant { + public: + explicit HFloatConstant(float value) : HConstant(Primitive::kPrimFloat), value_(value) {} + + float GetValue() const { return value_; } + + virtual bool InstructionDataEquals(HInstruction* other) const { + return bit_cast<float, int32_t>(other->AsFloatConstant()->value_) == + bit_cast<float, int32_t>(value_); + } + + virtual size_t ComputeHashCode() const { return static_cast<size_t>(GetValue()); } + + DECLARE_INSTRUCTION(FloatConstant); + + private: + const float value_; + + DISALLOW_COPY_AND_ASSIGN(HFloatConstant); +}; + +class HDoubleConstant : public HConstant { + public: + explicit HDoubleConstant(double value) : HConstant(Primitive::kPrimDouble), value_(value) {} + + double GetValue() const { return value_; } + + virtual bool InstructionDataEquals(HInstruction* other) const { + return bit_cast<double, int64_t>(other->AsDoubleConstant()->value_) == + bit_cast<double, int64_t>(value_); + } + + virtual size_t ComputeHashCode() const { return static_cast<size_t>(GetValue()); } + + DECLARE_INSTRUCTION(DoubleConstant); + + private: + const double value_; + + DISALLOW_COPY_AND_ASSIGN(HDoubleConstant); +}; + // Constants of the type int. Those can be from Dex instructions, or // synthesized (for example with the if-eqz instruction). class HIntConstant : public HConstant { @@ -1515,6 +1595,44 @@ class HNewInstance : public HExpression<0> { DISALLOW_COPY_AND_ASSIGN(HNewInstance); }; +class HNeg : public HUnaryOperation { + public: + explicit HNeg(Primitive::Type result_type, HInstruction* input) + : HUnaryOperation(result_type, input) {} + + virtual int32_t Evaluate(int32_t x) const OVERRIDE { return -x; } + virtual int64_t Evaluate(int64_t x) const OVERRIDE { return -x; } + + DECLARE_INSTRUCTION(Neg); + + private: + DISALLOW_COPY_AND_ASSIGN(HNeg); +}; + +class HNewArray : public HExpression<1> { + public: + HNewArray(HInstruction* length, uint32_t dex_pc, uint16_t type_index) + : HExpression(Primitive::kPrimNot, SideEffects::None()), + dex_pc_(dex_pc), + type_index_(type_index) { + SetRawInputAt(0, length); + } + + uint32_t GetDexPc() const { return dex_pc_; } + uint16_t GetTypeIndex() const { return type_index_; } + + // Calls runtime so needs an environment. + virtual bool NeedsEnvironment() const { return true; } + + DECLARE_INSTRUCTION(NewArray); + + private: + const uint32_t dex_pc_; + const uint16_t type_index_; + + DISALLOW_COPY_AND_ASSIGN(HNewArray); +}; + class HAdd : public HBinaryOperation { public: HAdd(Primitive::Type result_type, HInstruction* left, HInstruction* right) @@ -1555,6 +1673,22 @@ class HSub : public HBinaryOperation { DISALLOW_COPY_AND_ASSIGN(HSub); }; +class HMul : public HBinaryOperation { + public: + HMul(Primitive::Type result_type, HInstruction* left, HInstruction* right) + : HBinaryOperation(result_type, left, right) {} + + virtual bool IsCommutative() { return true; } + + virtual int32_t Evaluate(int32_t x, int32_t y) const { return x * y; } + virtual int64_t Evaluate(int64_t x, int64_t y) const { return x * y; } + + DECLARE_INSTRUCTION(Mul); + + private: + DISALLOW_COPY_AND_ASSIGN(HMul); +}; + // The value of a parameter in this method. Its location depends on // the calling convention. class HParameterValue : public HExpression<0> { @@ -1574,15 +1708,17 @@ class HParameterValue : public HExpression<0> { DISALLOW_COPY_AND_ASSIGN(HParameterValue); }; -class HNot : public HExpression<1> { +class HNot : public HUnaryOperation { public: - explicit HNot(HInstruction* input) : HExpression(Primitive::kPrimBoolean, SideEffects::None()) { - SetRawInputAt(0, input); - } + explicit HNot(Primitive::Type result_type, HInstruction* input) + : HUnaryOperation(result_type, input) {} virtual bool CanBeMoved() const { return true; } virtual bool InstructionDataEquals(HInstruction* other) const { return true; } + virtual int32_t Evaluate(int32_t x) const OVERRIDE { return ~x; } + virtual int64_t Evaluate(int64_t x) const OVERRIDE { return ~x; } + DECLARE_INSTRUCTION(Not); private: @@ -1642,6 +1778,8 @@ class HNullCheck : public HExpression<1> { virtual bool NeedsEnvironment() const { return true; } + virtual bool CanThrow() const { return true; } + uint32_t GetDexPc() const { return dex_pc_; } DECLARE_INSTRUCTION(NullCheck); @@ -1729,6 +1867,7 @@ class HArrayGet : public HExpression<2> { virtual bool CanBeMoved() const { return true; } virtual bool InstructionDataEquals(HInstruction* other) const { return true; } + void SetType(Primitive::Type type) { type_ = type; } DECLARE_INSTRUCTION(ArrayGet); @@ -1741,11 +1880,11 @@ class HArraySet : public HTemplateInstruction<3> { HArraySet(HInstruction* array, HInstruction* index, HInstruction* value, - Primitive::Type component_type, + Primitive::Type expected_component_type, uint32_t dex_pc) : HTemplateInstruction(SideEffects::ChangesSomething()), dex_pc_(dex_pc), - component_type_(component_type) { + expected_component_type_(expected_component_type) { SetRawInputAt(0, array); SetRawInputAt(1, index); SetRawInputAt(2, value); @@ -1759,13 +1898,24 @@ class HArraySet : public HTemplateInstruction<3> { uint32_t GetDexPc() const { return dex_pc_; } - Primitive::Type GetComponentType() const { return component_type_; } + HInstruction* GetValue() const { return InputAt(2); } + + Primitive::Type GetComponentType() const { + // The Dex format does not type floating point index operations. Since the + // `expected_component_type_` is set during building and can therefore not + // be correct, we also check what is the value type. If it is a floating + // point type, we must use that type. + Primitive::Type value_type = GetValue()->GetType(); + return ((value_type == Primitive::kPrimFloat) || (value_type == Primitive::kPrimDouble)) + ? value_type + : expected_component_type_; + } DECLARE_INSTRUCTION(ArraySet); private: const uint32_t dex_pc_; - const Primitive::Type component_type_; + const Primitive::Type expected_component_type_; DISALLOW_COPY_AND_ASSIGN(HArraySet); }; @@ -1802,6 +1952,8 @@ class HBoundsCheck : public HExpression<2> { virtual bool NeedsEnvironment() const { return true; } + virtual bool CanThrow() const { return true; } + uint32_t GetDexPc() const { return dex_pc_; } DECLARE_INSTRUCTION(BoundsCheck); @@ -1956,8 +2108,12 @@ class HGraphVisitor : public ValueObject { virtual void VisitInstruction(HInstruction* instruction) {} virtual void VisitBasicBlock(HBasicBlock* block); + // Visit the graph following basic block insertion order. void VisitInsertionOrder(); + // Visit the graph following dominator tree reverse post-order. + void VisitReversePostOrder(); + HGraph* GetGraph() const { return graph_; } // Visit functions for instruction classes. @@ -1969,7 +2125,7 @@ class HGraphVisitor : public ValueObject { #undef DECLARE_VISIT_INSTRUCTION private: - HGraph* graph_; + HGraph* const graph_; DISALLOW_COPY_AND_ASSIGN(HGraphVisitor); }; diff --git a/compiler/optimizing/constant_propagation.h b/compiler/optimizing/optimization.cc index 0729881888..ea98186d11 100644 --- a/compiler/optimizing/constant_propagation.h +++ b/compiler/optimizing/optimization.cc @@ -14,30 +14,35 @@ * limitations under the License. */ -#ifndef ART_COMPILER_OPTIMIZING_CONSTANT_PROPAGATION_H_ -#define ART_COMPILER_OPTIMIZING_CONSTANT_PROPAGATION_H_ +#include "optimization.h" -#include "nodes.h" +#include "base/dumpable.h" +#include "graph_checker.h" namespace art { -/** - * Optimization pass performing a simple constant propagation on the - * SSA form. - */ -class ConstantPropagation : public ValueObject { - public: - explicit ConstantPropagation(HGraph* graph) - : graph_(graph) {} - - void Run(); - - private: - HGraph* const graph_; - - DISALLOW_COPY_AND_ASSIGN(ConstantPropagation); -}; +void HOptimization::Execute() { + Run(); + visualizer_.DumpGraph(pass_name_); + Check(); +} + +void HOptimization::Check() { + if (kIsDebugBuild) { + if (is_in_ssa_form_) { + SSAChecker checker(graph_->GetArena(), graph_); + checker.Run(); + if (!checker.IsValid()) { + LOG(FATAL) << Dumpable<SSAChecker>(checker); + } + } else { + GraphChecker checker(graph_->GetArena(), graph_); + checker.Run(); + if (!checker.IsValid()) { + LOG(FATAL) << Dumpable<GraphChecker>(checker); + } + } + } +} } // namespace art - -#endif // ART_COMPILER_OPTIMIZING_CONSTANT_PROPAGATION_H_ diff --git a/compiler/optimizing/optimization.h b/compiler/optimizing/optimization.h new file mode 100644 index 0000000000..59683e2075 --- /dev/null +++ b/compiler/optimizing/optimization.h @@ -0,0 +1,71 @@ +/* + * 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_OPTIMIZATION_H_ +#define ART_COMPILER_OPTIMIZING_OPTIMIZATION_H_ + +#include "graph_visualizer.h" +#include "nodes.h" + +namespace art { + +/** + * Abstraction to implement an optimization pass. + */ +class HOptimization : public ValueObject { + public: + HOptimization(HGraph* graph, + bool is_in_ssa_form, + const char* pass_name, + const HGraphVisualizer& visualizer) + : graph_(graph), + is_in_ssa_form_(is_in_ssa_form), + pass_name_(pass_name), + visualizer_(visualizer) {} + + virtual ~HOptimization() {} + + // Execute the optimization pass. + void Execute(); + + // Return the name of the pass. + const char* GetPassName() const { return pass_name_; } + + // Peform the analysis itself. + virtual void Run() = 0; + + private: + // Verify the graph; abort if it is not valid. + void Check(); + + protected: + HGraph* const graph_; + + private: + // Does the analyzed graph use the SSA form? + const bool is_in_ssa_form_; + // Optimization pass name. + const char* pass_name_; + // A graph visualiser invoked after the execution of the optimization + // pass if enabled. + const HGraphVisualizer& visualizer_; + + DISALLOW_COPY_AND_ASSIGN(HOptimization); +}; + +} // namespace art + +#endif // ART_COMPILER_OPTIMIZING_OPTIMIZATION_H_ diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index 3cf5a0b291..80e9cdb16f 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -22,6 +22,8 @@ #include "builder.h" #include "code_generator.h" #include "compiler.h" +#include "constant_folding.h" +#include "dead_code_elimination.h" #include "driver/compiler_driver.h" #include "driver/dex_compilation_unit.h" #include "graph_visualizer.h" @@ -213,7 +215,10 @@ CompiledMethod* OptimizingCompiler::TryCompile(const DexFile::CodeItem* code_ite } // Do not attempt to compile on architectures we do not support. - if (instruction_set != kX86 && instruction_set != kX86_64 && instruction_set != kThumb2) { + if (instruction_set != kArm64 && + instruction_set != kThumb2 && + instruction_set != kX86 && + instruction_set != kX86_64) { return nullptr; } @@ -261,6 +266,9 @@ CompiledMethod* OptimizingCompiler::TryCompile(const DexFile::CodeItem* code_ite visualizer.DumpGraph("ssa"); graph->FindNaturalLoops(); + HDeadCodeElimination(graph, visualizer).Execute(); + HConstantFolding(graph, visualizer).Execute(); + SsaRedundantPhiElimination(graph).Run(); SsaDeadPhiElimination(graph).Run(); InstructionSimplifier(graph).Run(); diff --git a/compiler/optimizing/register_allocator.cc b/compiler/optimizing/register_allocator.cc index 8b32262ab2..f95c4a47e3 100644 --- a/compiler/optimizing/register_allocator.cc +++ b/compiler/optimizing/register_allocator.cc @@ -16,6 +16,8 @@ #include "register_allocator.h" +#include <sstream> + #include "base/bit_vector-inl.h" #include "code_generator.h" #include "ssa_liveness_analysis.h" @@ -37,18 +39,21 @@ RegisterAllocator::RegisterAllocator(ArenaAllocator* allocator, handled_(allocator, 0), active_(allocator, 0), inactive_(allocator, 0), - physical_register_intervals_(allocator, codegen->GetNumberOfCoreRegisters()), + physical_core_register_intervals_(allocator, codegen->GetNumberOfCoreRegisters()), + physical_fp_register_intervals_(allocator, codegen->GetNumberOfFloatingPointRegisters()), temp_intervals_(allocator, 4), spill_slots_(allocator, kDefaultNumberOfSpillSlots), safepoints_(allocator, 0), processing_core_registers_(false), number_of_registers_(-1), registers_array_(nullptr), - blocked_registers_(codegen->GetBlockedCoreRegisters()), + blocked_core_registers_(codegen->GetBlockedCoreRegisters()), + blocked_fp_registers_(codegen->GetBlockedFloatingPointRegisters()), reserved_out_slots_(0), maximum_number_of_live_registers_(0) { codegen->SetupBlockedRegisters(); - physical_register_intervals_.SetSize(codegen->GetNumberOfCoreRegisters()); + physical_core_register_intervals_.SetSize(codegen->GetNumberOfCoreRegisters()); + physical_fp_register_intervals_.SetSize(codegen->GetNumberOfFloatingPointRegisters()); // Always reserve for the current method and the graph's max out registers. // TODO: compute it instead. reserved_out_slots_ = 1 + codegen->GetGraph()->GetMaximumNumberOfOutVRegs(); @@ -65,8 +70,10 @@ bool RegisterAllocator::CanAllocateRegistersFor(const HGraph& graph, it.Advance()) { HInstruction* current = it.Current(); if (current->GetType() == Primitive::kPrimLong && instruction_set != kX86_64) return false; - if (current->GetType() == Primitive::kPrimFloat) return false; - if (current->GetType() == Primitive::kPrimDouble) return false; + if ((current->GetType() == Primitive::kPrimFloat || current->GetType() == Primitive::kPrimDouble) + && instruction_set != kX86_64) { + return false; + } } } return true; @@ -93,14 +100,22 @@ void RegisterAllocator::AllocateRegisters() { void RegisterAllocator::BlockRegister(Location location, size_t start, - size_t end, - Primitive::Type type) { + size_t end) { int reg = location.reg(); - LiveInterval* interval = physical_register_intervals_.Get(reg); + DCHECK(location.IsRegister() || location.IsFpuRegister()); + LiveInterval* interval = location.IsRegister() + ? physical_core_register_intervals_.Get(reg) + : physical_fp_register_intervals_.Get(reg); + Primitive::Type type = location.IsRegister() + ? Primitive::kPrimInt + : Primitive::kPrimDouble; if (interval == nullptr) { interval = LiveInterval::MakeFixedInterval(allocator_, reg, type); - physical_register_intervals_.Put(reg, interval); - inactive_.Add(interval); + if (location.IsRegister()) { + physical_core_register_intervals_.Put(reg, interval); + } else { + physical_fp_register_intervals_.Put(reg, interval); + } } DCHECK(interval->GetRegister() == reg); interval->AddRange(start, end); @@ -123,8 +138,17 @@ void RegisterAllocator::AllocateRegistersInternal() { registers_array_ = allocator_->AllocArray<size_t>(number_of_registers_); processing_core_registers_ = true; unhandled_ = &unhandled_core_intervals_; + for (size_t i = 0, e = physical_core_register_intervals_.Size(); i < e; ++i) { + LiveInterval* fixed = physical_core_register_intervals_.Get(i); + if (fixed != nullptr) { + inactive_.Add(fixed); + } + } LinearScan(); + size_t saved_maximum_number_of_live_registers = maximum_number_of_live_registers_; + maximum_number_of_live_registers_ = 0; + inactive_.Reset(); active_.Reset(); handled_.Reset(); @@ -133,9 +157,14 @@ void RegisterAllocator::AllocateRegistersInternal() { registers_array_ = allocator_->AllocArray<size_t>(number_of_registers_); processing_core_registers_ = false; unhandled_ = &unhandled_fp_intervals_; - // TODO: Enable FP register allocation. - DCHECK(unhandled_->IsEmpty()); + for (size_t i = 0, e = physical_fp_register_intervals_.Size(); i < e; ++i) { + LiveInterval* fixed = physical_fp_register_intervals_.Get(i); + if (fixed != nullptr) { + inactive_.Add(fixed); + } + } LinearScan(); + maximum_number_of_live_registers_ += saved_maximum_number_of_live_registers; } void RegisterAllocator::ProcessInstruction(HInstruction* instruction) { @@ -148,8 +177,9 @@ void RegisterAllocator::ProcessInstruction(HInstruction* instruction) { for (size_t i = 0; i < locations->GetTempCount(); ++i) { Location temp = locations->GetTemp(i); if (temp.IsRegister()) { - BlockRegister(temp, position, position + 1, Primitive::kPrimInt); + BlockRegister(temp, position, position + 1); } else { + DCHECK(temp.IsUnallocated()); LiveInterval* interval = LiveInterval::MakeTempInterval(allocator_, Primitive::kPrimInt); temp_intervals_.Add(interval); interval->AddRange(position, position + 1); @@ -160,10 +190,6 @@ void RegisterAllocator::ProcessInstruction(HInstruction* instruction) { bool core_register = (instruction->GetType() != Primitive::kPrimDouble) && (instruction->GetType() != Primitive::kPrimFloat); - GrowableArray<LiveInterval*>& unhandled = core_register - ? unhandled_core_intervals_ - : unhandled_fp_intervals_; - if (locations->CanCall()) { if (!instruction->IsSuspendCheck()) { codegen_->MarkNotLeaf(); @@ -180,7 +206,8 @@ void RegisterAllocator::ProcessInstruction(HInstruction* instruction) { // maximum before updating locations. LiveInterval* interval = LiveInterval::MakeSlowPathInterval(allocator_, instruction); interval->AddRange(position, position + 1); - unhandled.Add(interval); + unhandled_core_intervals_.Add(interval); + unhandled_fp_intervals_.Add(interval); } } @@ -189,21 +216,29 @@ void RegisterAllocator::ProcessInstruction(HInstruction* instruction) { for (size_t i = 0; i < codegen_->GetNumberOfCoreRegisters(); ++i) { BlockRegister(Location::RegisterLocation(i), position, - position + 1, - Primitive::kPrimInt); + position + 1); + } + for (size_t i = 0; i < codegen_->GetNumberOfFloatingPointRegisters(); ++i) { + BlockRegister(Location::FpuRegisterLocation(i), + position, + position + 1); } } for (size_t i = 0; i < instruction->InputCount(); ++i) { Location input = locations->InAt(i); - if (input.IsRegister()) { - BlockRegister(input, position, position + 1, instruction->InputAt(i)->GetType()); + if (input.IsRegister() || input.IsFpuRegister()) { + BlockRegister(input, position, position + 1); } } LiveInterval* current = instruction->GetLiveInterval(); if (current == nullptr) return; + GrowableArray<LiveInterval*>& unhandled = core_register + ? unhandled_core_intervals_ + : unhandled_fp_intervals_; + DCHECK(unhandled.IsEmpty() || current->StartsBeforeOrAt(unhandled.Peek())); // Some instructions define their output in fixed register/stack slot. We need // to ensure we know these locations before doing register allocation. For a @@ -213,21 +248,24 @@ void RegisterAllocator::ProcessInstruction(HInstruction* instruction) { // // The backwards walking ensures the ranges are ordered on increasing start positions. Location output = locations->Out(); - if (output.IsRegister()) { + if (output.IsRegister() || output.IsFpuRegister()) { // Shift the interval's start by one to account for the blocked register. current->SetFrom(position + 1); current->SetRegister(output.reg()); - BlockRegister(output, position, position + 1, instruction->GetType()); + BlockRegister(output, position, position + 1); + } else if (!locations->OutputOverlapsWithInputs()) { + // Shift the interval's start by one to not interfere with the inputs. + current->SetFrom(position + 1); } else if (output.IsStackSlot() || output.IsDoubleStackSlot()) { current->SetSpillSlot(output.GetStackIndex()); } // If needed, add interval to the list of unhandled intervals. if (current->HasSpillSlot() || instruction->IsConstant()) { - // Split before first register use. + // Split just before first register use. size_t first_register_use = current->FirstRegisterUse(); if (first_register_use != kNoLifetime) { - LiveInterval* split = Split(current, first_register_use); + LiveInterval* split = Split(current, first_register_use - 1); // 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. AddSorted(&unhandled, split); @@ -278,10 +316,19 @@ bool RegisterAllocator::ValidateInternal(bool log_fatal_on_failure) const { } } - for (size_t i = 0, e = physical_register_intervals_.Size(); i < e; ++i) { - LiveInterval* fixed = physical_register_intervals_.Get(i); - if (fixed != nullptr && ShouldProcess(processing_core_registers_, fixed)) { - intervals.Add(fixed); + if (processing_core_registers_) { + for (size_t i = 0, e = physical_core_register_intervals_.Size(); i < e; ++i) { + LiveInterval* fixed = physical_core_register_intervals_.Get(i); + if (fixed != nullptr) { + intervals.Add(fixed); + } + } + } else { + for (size_t i = 0, e = physical_fp_register_intervals_.Size(); i < e; ++i) { + LiveInterval* fixed = physical_fp_register_intervals_.Get(i); + if (fixed != nullptr) { + intervals.Add(fixed); + } } } @@ -374,10 +421,10 @@ void RegisterAllocator::DumpInterval(std::ostream& stream, LiveInterval* interva interval->Dump(stream); stream << ": "; if (interval->HasRegister()) { - if (processing_core_registers_) { - codegen_->DumpCoreRegister(stream, interval->GetRegister()); - } else { + if (interval->IsFloatingPoint()) { codegen_->DumpFloatingPointRegister(stream, interval->GetRegister()); + } else { + codegen_->DumpCoreRegister(stream, interval->GetRegister()); } } else { stream << "spilled"; @@ -391,6 +438,7 @@ void RegisterAllocator::LinearScan() { // (1) Remove interval with the lowest start position from unhandled. LiveInterval* current = unhandled_->Pop(); DCHECK(!current->IsFixed() && !current->HasSpillSlot()); + DCHECK(unhandled_->IsEmpty() || unhandled_->Peek()->GetStart() >= current->GetStart()); size_t position = current->GetStart(); // (2) Remove currently active intervals that are dead at this position. @@ -519,10 +567,9 @@ bool RegisterAllocator::TryAllocateFreeReg(LiveInterval* current) { } bool RegisterAllocator::IsBlocked(int reg) const { - // TODO: This only works for core registers and needs to be adjusted for - // floating point registers. - DCHECK(processing_core_registers_); - return blocked_registers_[reg]; + return processing_core_registers_ + ? blocked_core_registers_[reg] + : blocked_fp_registers_[reg]; } // Find the register that is used the last, and spill the interval @@ -591,7 +638,9 @@ bool RegisterAllocator::AllocateBlockedReg(LiveInterval* current) { // If the first use of that instruction is after the last use of the found // register, we split this interval just before its first register use. AllocateSpillSlotFor(current); - LiveInterval* split = Split(current, first_register_use); + LiveInterval* split = Split(current, first_register_use - 1); + DCHECK_NE(current, split) << "There is not enough registers available for " + << split->GetParent()->GetDefinedBy()->DebugName(); AddSorted(unhandled_, split); return false; } else { @@ -635,6 +684,7 @@ bool RegisterAllocator::AllocateBlockedReg(LiveInterval* current) { } void RegisterAllocator::AddSorted(GrowableArray<LiveInterval*>* array, LiveInterval* interval) { + DCHECK(!interval->IsFixed() && !interval->HasSpillSlot()); size_t insert_at = 0; for (size_t i = array->Size(); i > 0; --i) { LiveInterval* current = array->Get(i - 1); @@ -723,17 +773,11 @@ void RegisterAllocator::AllocateSpillSlotFor(LiveInterval* interval) { parent->SetSpillSlot((slot + reserved_out_slots_) * kVRegSize); } -// We create a special marker for inputs moves to differentiate them from -// moves created during resolution. They must be different instructions -// because the input moves work on the assumption that the interval moves -// have been executed. -static constexpr size_t kInputMoveLifetimePosition = 0; -static bool IsInputMove(HInstruction* instruction) { - return instruction->GetLifetimePosition() == kInputMoveLifetimePosition; -} - static bool IsValidDestination(Location destination) { - return destination.IsRegister() || destination.IsStackSlot() || destination.IsDoubleStackSlot(); + return destination.IsRegister() + || destination.IsFpuRegister() + || destination.IsStackSlot() + || destination.IsDoubleStackSlot(); } void RegisterAllocator::AddInputMoveFor(HInstruction* user, @@ -742,20 +786,20 @@ void RegisterAllocator::AddInputMoveFor(HInstruction* user, DCHECK(IsValidDestination(destination)); if (source.Equals(destination)) return; - DCHECK(user->AsPhi() == nullptr); + DCHECK(!user->IsPhi()); HInstruction* previous = user->GetPrevious(); HParallelMove* move = nullptr; if (previous == nullptr - || previous->AsParallelMove() == nullptr - || !IsInputMove(previous)) { + || !previous->IsParallelMove() + || previous->GetLifetimePosition() < user->GetLifetimePosition()) { move = new (allocator_) HParallelMove(allocator_); - move->SetLifetimePosition(kInputMoveLifetimePosition); + move->SetLifetimePosition(user->GetLifetimePosition()); user->GetBlock()->InsertInstructionBefore(move, user); } else { move = previous->AsParallelMove(); } - DCHECK(IsInputMove(move)); + DCHECK_EQ(move->GetLifetimePosition(), user->GetLifetimePosition()); move->AddMove(new (allocator_) MoveOperands(source, destination, nullptr)); } @@ -778,7 +822,7 @@ void RegisterAllocator::InsertParallelMoveAt(size_t position, move = at->GetNext()->AsParallelMove(); // This is a parallel move for connecting siblings in a same block. We need to // differentiate it with moves for connecting blocks, and input moves. - if (move == nullptr || IsInputMove(move) || move->GetLifetimePosition() > position) { + if (move == nullptr || move->GetLifetimePosition() > position) { move = new (allocator_) HParallelMove(allocator_); move->SetLifetimePosition(position); at->GetBlock()->InsertInstructionBefore(move, at->GetNext()); @@ -786,12 +830,6 @@ void RegisterAllocator::InsertParallelMoveAt(size_t position, } else { // Move must happen before the instruction. HInstruction* previous = at->GetPrevious(); - if (previous != nullptr && previous->IsParallelMove() && IsInputMove(previous)) { - // This is a parallel move for connecting siblings in a same block. We need to - // differentiate it with input moves. - at = previous; - previous = previous->GetPrevious(); - } if (previous == nullptr || !previous->IsParallelMove() || previous->GetLifetimePosition() != position) { @@ -866,7 +904,7 @@ void RegisterAllocator::InsertMoveAfter(HInstruction* instruction, DCHECK(IsValidDestination(destination)); if (source.Equals(destination)) return; - if (instruction->AsPhi() != nullptr) { + if (instruction->IsPhi()) { InsertParallelMoveAtEntryOf(instruction->GetBlock(), instruction, source, destination); return; } @@ -889,7 +927,9 @@ void RegisterAllocator::ConnectSiblings(LiveInterval* interval) { if (current->HasSpillSlot() && current->HasRegister()) { // We spill eagerly, so move must be at definition. InsertMoveAfter(interval->GetDefinedBy(), - Location::RegisterLocation(interval->GetRegister()), + interval->IsFloatingPoint() + ? Location::FpuRegisterLocation(interval->GetRegister()) + : Location::RegisterLocation(interval->GetRegister()), interval->NeedsTwoSpillSlots() ? Location::DoubleStackSlot(interval->GetParent()->GetSpillSlot()) : Location::StackSlot(interval->GetParent()->GetSpillSlot())); @@ -947,6 +987,10 @@ void RegisterAllocator::ConnectSiblings(LiveInterval* interval) { } break; } + case Location::kFpuRegister: { + locations->AddLiveRegister(source); + break; + } case Location::kStackSlot: // Fall-through case Location::kDoubleStackSlot: // Fall-through case Location::kConstant: { @@ -1036,7 +1080,7 @@ void RegisterAllocator::Resolve() { LiveInterval* current = instruction->GetLiveInterval(); LocationSummary* locations = instruction->GetLocations(); Location location = locations->Out(); - if (instruction->AsParameterValue() != nullptr) { + if (instruction->IsParameterValue()) { // Now that we know the frame size, adjust the parameter's location. if (location.IsStackSlot()) { location = Location::StackSlot(location.GetStackIndex() + codegen_->GetFrameSize()); @@ -1110,6 +1154,7 @@ void RegisterAllocator::Resolve() { current = at; } LocationSummary* locations = at->GetLocations(); + DCHECK(temp->GetType() == Primitive::kPrimInt); locations->SetTempAt( temp_index++, Location::RegisterLocation(temp->GetRegister())); } diff --git a/compiler/optimizing/register_allocator.h b/compiler/optimizing/register_allocator.h index d4c233a7f8..b88153969b 100644 --- a/compiler/optimizing/register_allocator.h +++ b/compiler/optimizing/register_allocator.h @@ -21,8 +21,6 @@ #include "primitive.h" #include "utils/growable_array.h" -#include "gtest/gtest.h" - namespace art { class CodeGenerator; @@ -96,7 +94,7 @@ class RegisterAllocator { bool IsBlocked(int reg) const; // Update the interval for the register in `location` to cover [start, end). - void BlockRegister(Location location, size_t start, size_t end, Primitive::Type type); + void BlockRegister(Location location, size_t start, size_t end); // Allocate a spill slot for the given interval. void AllocateSpillSlotFor(LiveInterval* interval); @@ -158,7 +156,8 @@ class RegisterAllocator { // Fixed intervals for physical registers. Such intervals cover the positions // where an instruction requires a specific register. - GrowableArray<LiveInterval*> physical_register_intervals_; + GrowableArray<LiveInterval*> physical_core_register_intervals_; + GrowableArray<LiveInterval*> physical_fp_register_intervals_; // Intervals for temporaries. Such intervals cover the positions // where an instruction requires a temporary. @@ -181,7 +180,8 @@ class RegisterAllocator { size_t* registers_array_; // Blocked registers, as decided by the code generator. - bool* const blocked_registers_; + bool* const blocked_core_registers_; + bool* const blocked_fp_registers_; // Slots reserved for out arguments. size_t reserved_out_slots_; @@ -189,7 +189,7 @@ class RegisterAllocator { // The maximum live registers at safepoints. size_t maximum_number_of_live_registers_; - FRIEND_TEST(RegisterAllocatorTest, FreeUntil); + ART_FRIEND_TEST(RegisterAllocatorTest, FreeUntil); DISALLOW_COPY_AND_ASSIGN(RegisterAllocator); }; diff --git a/compiler/optimizing/register_allocator_test.cc b/compiler/optimizing/register_allocator_test.cc index 7517a6b003..2d84a9d335 100644 --- a/compiler/optimizing/register_allocator_test.cc +++ b/compiler/optimizing/register_allocator_test.cc @@ -348,14 +348,14 @@ TEST(RegisterAllocatorTest, FirstRegisterUse) { // Split at the next instruction. interval = interval->SplitAt(first_add->GetLifetimePosition() + 2); // The user of the split is the last add. - ASSERT_EQ(interval->FirstRegisterUse(), last_add->GetLifetimePosition() - 1); + ASSERT_EQ(interval->FirstRegisterUse(), last_add->GetLifetimePosition()); // Split before the last add. LiveInterval* new_interval = interval->SplitAt(last_add->GetLifetimePosition() - 1); // Ensure the current interval has no register use... ASSERT_EQ(interval->FirstRegisterUse(), kNoLifetime); // And the new interval has it for the last add. - ASSERT_EQ(new_interval->FirstRegisterUse(), last_add->GetLifetimePosition() - 1); + ASSERT_EQ(new_interval->FirstRegisterUse(), last_add->GetLifetimePosition()); } TEST(RegisterAllocatorTest, DeadPhi) { diff --git a/compiler/optimizing/ssa_builder.cc b/compiler/optimizing/ssa_builder.cc index 471307ec31..a0cc8a94ee 100644 --- a/compiler/optimizing/ssa_builder.cc +++ b/compiler/optimizing/ssa_builder.cc @@ -51,7 +51,7 @@ void SsaBuilder::BuildSsa() { !it.Done(); it.Advance()) { HInstruction* current = it.Current(); - if (current->AsLocal() != nullptr) { + if (current->IsLocal()) { current->GetBlock()->RemoveInstruction(current); } } @@ -129,8 +129,112 @@ void SsaBuilder::VisitBasicBlock(HBasicBlock* block) { } } +/** + * Constants in the Dex format are not typed. So the builder types them as + * integers, but when doing the SSA form, we might realize the constant + * is used for floating point operations. We create a floating-point equivalent + * constant to make the operations correctly typed. + */ +static HFloatConstant* GetFloatEquivalent(HIntConstant* constant) { + // We place the floating point constant next to this constant. + HFloatConstant* result = constant->GetNext()->AsFloatConstant(); + if (result == nullptr) { + HGraph* graph = constant->GetBlock()->GetGraph(); + ArenaAllocator* allocator = graph->GetArena(); + result = new (allocator) HFloatConstant(bit_cast<int32_t, float>(constant->GetValue())); + constant->GetBlock()->InsertInstructionBefore(result, constant->GetNext()); + } else { + // If there is already a constant with the expected type, we know it is + // the floating point equivalent of this constant. + DCHECK_EQ((bit_cast<float, int32_t>(result->GetValue())), constant->GetValue()); + } + return result; +} + +/** + * Wide constants in the Dex format are not typed. So the builder types them as + * longs, but when doing the SSA form, we might realize the constant + * is used for floating point operations. We create a floating-point equivalent + * constant to make the operations correctly typed. + */ +static HDoubleConstant* GetDoubleEquivalent(HLongConstant* constant) { + // We place the floating point constant next to this constant. + HDoubleConstant* result = constant->GetNext()->AsDoubleConstant(); + if (result == nullptr) { + HGraph* graph = constant->GetBlock()->GetGraph(); + ArenaAllocator* allocator = graph->GetArena(); + result = new (allocator) HDoubleConstant(bit_cast<int64_t, double>(constant->GetValue())); + constant->GetBlock()->InsertInstructionBefore(result, constant->GetNext()); + } else { + // If there is already a constant with the expected type, we know it is + // the floating point equivalent of this constant. + DCHECK_EQ((bit_cast<double, int64_t>(result->GetValue())), constant->GetValue()); + } + return result; +} + +/** + * Because of Dex format, we might end up having the same phi being + * used for non floating point operations and floating point operations. Because + * we want the graph to be correctly typed (and thereafter avoid moves between + * floating point registers and core registers), we need to create a copy of the + * phi with a floating point type. + */ +static HPhi* GetFloatOrDoubleEquivalentOfPhi(HPhi* phi, Primitive::Type type) { + // We place the floating point phi next to this phi. + HInstruction* next = phi->GetNext(); + if (next == nullptr + || (next->GetType() != Primitive::kPrimDouble && next->GetType() != Primitive::kPrimFloat)) { + ArenaAllocator* allocator = phi->GetBlock()->GetGraph()->GetArena(); + HPhi* new_phi = new (allocator) HPhi(allocator, phi->GetRegNumber(), phi->InputCount(), type); + for (size_t i = 0, e = phi->InputCount(); i < e; ++i) { + // Copy the inputs. Note that the graph may not be correctly typed by doing this copy, + // but the type propagation phase will fix it. + new_phi->SetRawInputAt(i, phi->InputAt(i)); + } + phi->GetBlock()->InsertPhiAfter(new_phi, phi); + return new_phi; + } else { + // If there is already a phi with the expected type, we know it is the floating + // point equivalent of this phi. + DCHECK_EQ(next->AsPhi()->GetRegNumber(), phi->GetRegNumber()); + return next->AsPhi(); + } +} + +HInstruction* SsaBuilder::GetFloatOrDoubleEquivalent(HInstruction* user, + HInstruction* value, + Primitive::Type type) { + if (value->IsArrayGet()) { + // The verifier has checked that values in arrays cannot be used for both + // floating point and non-floating point operations. It is therefore safe to just + // change the type of the operation. + value->AsArrayGet()->SetType(type); + return value; + } else if (value->IsLongConstant()) { + return GetDoubleEquivalent(value->AsLongConstant()); + } else if (value->IsIntConstant()) { + return GetFloatEquivalent(value->AsIntConstant()); + } else if (value->IsPhi()) { + return GetFloatOrDoubleEquivalentOfPhi(value->AsPhi(), type); + } else { + // For other instructions, we assume the verifier has checked that the dex format is correctly + // typed and the value in a dex register will not be used for both floating point and + // non-floating point operations. So the only reason an instruction would want a floating + // point equivalent is for an unused phi that will be removed by the dead phi elimination phase. + DCHECK(user->IsPhi()); + return value; + } +} + void SsaBuilder::VisitLoadLocal(HLoadLocal* load) { - load->ReplaceWith(current_locals_->Get(load->GetLocal()->GetRegNumber())); + HInstruction* value = current_locals_->Get(load->GetLocal()->GetRegNumber()); + if (load->GetType() != value->GetType() + && (load->GetType() == Primitive::kPrimFloat || load->GetType() == Primitive::kPrimDouble)) { + // If the operation requests a specific type, we make sure its input is of that type. + value = GetFloatOrDoubleEquivalent(load, value, load->GetType()); + } + load->ReplaceWith(value); load->GetBlock()->RemoveInstruction(load); } diff --git a/compiler/optimizing/ssa_builder.h b/compiler/optimizing/ssa_builder.h index 9d8c0729ae..24f5ac55f7 100644 --- a/compiler/optimizing/ssa_builder.h +++ b/compiler/optimizing/ssa_builder.h @@ -52,6 +52,10 @@ class SsaBuilder : public HGraphVisitor { void VisitStoreLocal(HStoreLocal* store); void VisitInstruction(HInstruction* instruction); + static HInstruction* GetFloatOrDoubleEquivalent(HInstruction* user, + HInstruction* instruction, + Primitive::Type type); + private: // Locals for the current block being visited. GrowableArray<HInstruction*>* current_locals_; diff --git a/compiler/optimizing/ssa_liveness_analysis.cc b/compiler/optimizing/ssa_liveness_analysis.cc index f0edc6422c..1e34670d76 100644 --- a/compiler/optimizing/ssa_liveness_analysis.cc +++ b/compiler/optimizing/ssa_liveness_analysis.cc @@ -319,7 +319,7 @@ int LiveInterval::FindFirstRegisterHint(size_t* free_until) const { if (user->IsPhi()) { // If the phi has a register, try to use the same. Location phi_location = user->GetLiveInterval()->ToLocation(); - if (phi_location.IsRegister() && free_until[phi_location.reg()] >= use_position) { + if (SameRegisterKind(phi_location) && free_until[phi_location.reg()] >= use_position) { return phi_location.reg(); } const GrowableArray<HBasicBlock*>& predecessors = user->GetBlock()->GetPredecessors(); @@ -345,7 +345,7 @@ int LiveInterval::FindFirstRegisterHint(size_t* free_until) const { // We use the user's lifetime position - 1 (and not `use_position`) because the // register is blocked at the beginning of the user. size_t position = user->GetLifetimePosition() - 1; - if (expected.IsRegister() && free_until[expected.reg()] >= position) { + if (SameRegisterKind(expected) && free_until[expected.reg()] >= position) { return expected.reg(); } } @@ -368,7 +368,7 @@ int LiveInterval::FindHintAtDefinition() const { // If the input dies at the end of the predecessor, we know its register can // be reused. Location input_location = input_interval.ToLocation(); - if (input_location.IsRegister()) { + if (SameRegisterKind(input_location)) { return input_location.reg(); } } @@ -384,7 +384,7 @@ int LiveInterval::FindHintAtDefinition() const { // If the input dies at the start of this instruction, we know its register can // be reused. Location location = input_interval.ToLocation(); - if (location.IsRegister()) { + if (SameRegisterKind(location)) { return location.reg(); } } @@ -393,13 +393,21 @@ int LiveInterval::FindHintAtDefinition() const { return kNoRegister; } +bool LiveInterval::SameRegisterKind(Location other) const { + return IsFloatingPoint() + ? other.IsFpuRegister() + : other.IsRegister(); +} + bool LiveInterval::NeedsTwoSpillSlots() const { return type_ == Primitive::kPrimLong || type_ == Primitive::kPrimDouble; } Location LiveInterval::ToLocation() const { if (HasRegister()) { - return Location::RegisterLocation(GetRegister()); + return IsFloatingPoint() + ? Location::FpuRegisterLocation(GetRegister()) + : Location::RegisterLocation(GetRegister()); } else { HInstruction* defined_by = GetParent()->GetDefinedBy(); if (defined_by->IsConstant()) { diff --git a/compiler/optimizing/ssa_liveness_analysis.h b/compiler/optimizing/ssa_liveness_analysis.h index e9bd30338d..7dda4f61d5 100644 --- a/compiler/optimizing/ssa_liveness_analysis.h +++ b/compiler/optimizing/ssa_liveness_analysis.h @@ -32,6 +32,7 @@ class BlockInfo : public ArenaObject { live_in_(allocator, number_of_ssa_values, false), live_out_(allocator, number_of_ssa_values, false), kill_(allocator, number_of_ssa_values, false) { + UNUSED(block_); live_in_.ClearAllBits(); live_out_.ClearAllBits(); kill_.ClearAllBits(); @@ -188,10 +189,14 @@ class LiveInterval : public ArenaObject { && (first_use_->GetPosition() < position)) { // The user uses the instruction multiple times, and one use dies before the other. // We update the use list so that the latter is first. + UsePosition* cursor = first_use_; + while ((cursor->GetNext() != nullptr) && (cursor->GetNext()->GetPosition() < position)) { + cursor = cursor->GetNext(); + } DCHECK(first_use_->GetPosition() + 1 == position); UsePosition* new_use = new (allocator_) UsePosition( - instruction, input_index, is_environment, position, first_use_->GetNext()); - first_use_->SetNext(new_use); + instruction, input_index, is_environment, position, cursor->GetNext()); + cursor->SetNext(new_use); if (first_range_->GetEnd() == first_use_->GetPosition()) { first_range_->end_ = position; } @@ -354,6 +359,10 @@ class LiveInterval : public ArenaObject { || (location.GetPolicy() == Location::kSameAsFirstInput && locations->InAt(0).GetPolicy() == Location::kRequiresRegister)) { return position; + } else if ((location.GetPolicy() == Location::kRequiresFpuRegister) + || (location.GetPolicy() == Location::kSameAsFirstInput + && locations->InAt(0).GetPolicy() == Location::kRequiresFpuRegister)) { + return position; } } } @@ -362,12 +371,12 @@ class LiveInterval : public ArenaObject { size_t end = GetEnd(); while (use != nullptr && use->GetPosition() <= end) { size_t use_position = use->GetPosition(); - if (use_position >= position && !use->GetIsEnvironment()) { + if (use_position > position && !use->GetIsEnvironment()) { Location location = use->GetUser()->GetLocations()->InAt(use->GetInputIndex()); - if (location.IsUnallocated() && location.GetPolicy() == Location::kRequiresRegister) { - // Return the lifetime just before the user, so that the interval has a register - // when entering the user. - return use->GetUser()->GetLifetimePosition() - 1; + if (location.IsUnallocated() + && (location.GetPolicy() == Location::kRequiresRegister + || location.GetPolicy() == Location::kRequiresFpuRegister)) { + return use_position; } } use = use->GetNext(); @@ -498,6 +507,10 @@ class LiveInterval : public ArenaObject { // slots for spilling. bool NeedsTwoSpillSlots() const; + bool IsFloatingPoint() const { + return type_ == Primitive::kPrimFloat || type_ == Primitive::kPrimDouble; + } + // Converts the location of the interval to a `Location` object. Location ToLocation() const; @@ -509,6 +522,9 @@ class LiveInterval : public ArenaObject { bool IsTemp() const { return is_temp_; } + // Returns whether `other` and `this` share the same kind of register. + bool SameRegisterKind(Location other) const; + private: ArenaAllocator* const allocator_; diff --git a/compiler/optimizing/ssa_phi_elimination.cc b/compiler/optimizing/ssa_phi_elimination.cc index e02a182ec8..4eda0f3757 100644 --- a/compiler/optimizing/ssa_phi_elimination.cc +++ b/compiler/optimizing/ssa_phi_elimination.cc @@ -24,18 +24,13 @@ void SsaDeadPhiElimination::Run() { HBasicBlock* block = it.Current(); for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) { HPhi* phi = it.Current()->AsPhi(); - if (phi->HasEnvironmentUses()) { - // TODO: Do we want to keep that phi alive? - worklist_.Add(phi); - phi->SetLive(); - continue; - } for (HUseIterator<HInstruction> it(phi->GetUses()); !it.Done(); it.Advance()) { HUseListNode<HInstruction>* current = it.Current(); HInstruction* user = current->GetUser(); if (!user->IsPhi()) { worklist_.Add(phi); phi->SetLive(); + break; } else { phi->SetDead(); } @@ -76,6 +71,14 @@ void SsaDeadPhiElimination::Run() { current->RemoveUser(user, user_node->GetIndex()); } } + if (current->HasEnvironmentUses()) { + for (HUseIterator<HEnvironment> it(current->GetEnvUses()); !it.Done(); it.Advance()) { + HUseListNode<HEnvironment>* user_node = it.Current(); + HEnvironment* user = user_node->GetUser(); + user->SetRawEnvAt(user_node->GetIndex(), nullptr); + current->RemoveEnvironmentUser(user, user_node->GetIndex()); + } + } block->RemovePhi(current->AsPhi()); } current = next; diff --git a/compiler/optimizing/ssa_type_propagation.cc b/compiler/optimizing/ssa_type_propagation.cc index a860cb7cfe..3828142ed2 100644 --- a/compiler/optimizing/ssa_type_propagation.cc +++ b/compiler/optimizing/ssa_type_propagation.cc @@ -14,6 +14,7 @@ * limitations under the License. */ +#include "ssa_builder.h" #include "ssa_type_propagation.h" #include "nodes.h" @@ -38,15 +39,31 @@ static Primitive::Type MergeTypes(Primitive::Type existing, Primitive::Type new_ // Re-compute and update the type of the instruction. Returns // whether or not the type was changed. -static bool UpdateType(HPhi* phi) { +bool SsaTypePropagation::UpdateType(HPhi* phi) { Primitive::Type existing = phi->GetType(); - Primitive::Type new_type = Primitive::kPrimVoid; + Primitive::Type new_type = existing; for (size_t i = 0, e = phi->InputCount(); i < e; ++i) { Primitive::Type input_type = phi->InputAt(i)->GetType(); new_type = MergeTypes(new_type, input_type); } phi->SetType(new_type); + + if (new_type == Primitive::kPrimDouble || new_type == Primitive::kPrimFloat) { + // If the phi is of floating point type, we need to update its inputs to that + // type. For inputs that are phis, we need to recompute their types. + for (size_t i = 0, e = phi->InputCount(); i < e; ++i) { + HInstruction* input = phi->InputAt(i); + if (input->GetType() != new_type) { + HInstruction* equivalent = SsaBuilder::GetFloatOrDoubleEquivalent(phi, input, new_type); + phi->ReplaceInput(equivalent, i); + if (equivalent->IsPhi()) { + AddToWorklist(equivalent->AsPhi()); + } + } + } + } + return existing != new_type; } @@ -63,7 +80,12 @@ void SsaTypePropagation::VisitBasicBlock(HBasicBlock* block) { HPhi* phi = it.Current()->AsPhi(); // Set the initial type for the phi. Use the non back edge input for reaching // a fixed point faster. - phi->SetType(phi->InputAt(0)->GetType()); + Primitive::Type phi_type = phi->GetType(); + // We merge with the existing type, that has been set by the SSA builder. + DCHECK(phi_type == Primitive::kPrimVoid + || phi_type == Primitive::kPrimFloat + || phi_type == Primitive::kPrimDouble); + phi->SetType(MergeTypes(phi->InputAt(0)->GetType(), phi->GetType())); AddToWorklist(phi); } } else { diff --git a/compiler/optimizing/ssa_type_propagation.h b/compiler/optimizing/ssa_type_propagation.h index 5f471a9811..f4d3d6344a 100644 --- a/compiler/optimizing/ssa_type_propagation.h +++ b/compiler/optimizing/ssa_type_propagation.h @@ -34,6 +34,7 @@ class SsaTypePropagation : public ValueObject { void ProcessWorklist(); void AddToWorklist(HPhi* phi); void AddDependentInstructionsToWorklist(HPhi* phi); + bool UpdateType(HPhi* phi); HGraph* const graph_; GrowableArray<HPhi*> worklist_; |