summaryrefslogtreecommitdiffstats
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/dex/bb_optimizations.cc28
-rw-r--r--compiler/dex/bb_optimizations.h63
-rw-r--r--compiler/dex/mir_optimization.cc3
-rw-r--r--compiler/dex/pass_driver_me_opts.cc3
-rw-r--r--compiler/dex/pass_driver_me_post_opt.cc3
-rw-r--r--compiler/dex/post_opt_passes.cc29
-rw-r--r--compiler/dex/post_opt_passes.h38
-rw-r--r--compiler/dex/quick/arm/int_arm.cc1
-rw-r--r--compiler/dex/vreg_analysis.cc2
-rw-r--r--compiler/optimizing/code_generator.cc18
-rw-r--r--compiler/optimizing/code_generator.h12
-rw-r--r--compiler/optimizing/code_generator_arm.cc83
-rw-r--r--compiler/optimizing/code_generator_arm.h12
-rw-r--r--compiler/optimizing/code_generator_arm64.cc23
-rw-r--r--compiler/optimizing/code_generator_arm64.h5
-rw-r--r--compiler/optimizing/code_generator_x86.cc29
-rw-r--r--compiler/optimizing/code_generator_x86.h6
-rw-r--r--compiler/optimizing/code_generator_x86_64.cc27
-rw-r--r--compiler/optimizing/code_generator_x86_64.h5
-rw-r--r--compiler/optimizing/codegen_test.cc21
-rw-r--r--compiler/optimizing/constant_folding_test.cc3
-rw-r--r--compiler/optimizing/dead_code_elimination_test.cc3
-rw-r--r--compiler/optimizing/graph_checker.cc62
-rw-r--r--compiler/optimizing/graph_checker.h1
-rw-r--r--compiler/optimizing/intrinsics_x86_64.cc2
-rw-r--r--compiler/optimizing/linearize_test.cc3
-rw-r--r--compiler/optimizing/live_ranges_test.cc13
-rw-r--r--compiler/optimizing/liveness_test.cc3
-rw-r--r--compiler/optimizing/locations.h12
-rw-r--r--compiler/optimizing/nodes.cc7
-rw-r--r--compiler/optimizing/nodes.h48
-rw-r--r--compiler/optimizing/optimizing_compiler.cc19
-rw-r--r--compiler/optimizing/optimizing_unit_test.h8
-rw-r--r--compiler/optimizing/parallel_move_resolver.cc3
-rw-r--r--compiler/optimizing/parallel_move_test.cc105
-rw-r--r--compiler/optimizing/register_allocator.cc21
-rw-r--r--compiler/optimizing/register_allocator.h1
-rw-r--r--compiler/optimizing/register_allocator_test.cc133
-rw-r--r--compiler/optimizing/ssa_liveness_analysis.h6
-rw-r--r--compiler/optimizing/test/ConstantFolding.java221
-rw-r--r--compiler/optimizing/test/Inliner.java202
-rw-r--r--compiler/utils/x86_64/assembler_x86_64.cc8
-rw-r--r--compiler/utils/x86_64/assembler_x86_64.h1
43 files changed, 604 insertions, 692 deletions
diff --git a/compiler/dex/bb_optimizations.cc b/compiler/dex/bb_optimizations.cc
index e5358139d8..11a7e44f98 100644
--- a/compiler/dex/bb_optimizations.cc
+++ b/compiler/dex/bb_optimizations.cc
@@ -51,4 +51,32 @@ bool BBCombine::Worker(PassDataHolder* data) const {
return false;
}
+/*
+ * MethodUseCount pass implementation start.
+ */
+bool MethodUseCount::Gate(const PassDataHolder* data) const {
+ DCHECK(data != nullptr);
+ CompilationUnit* c_unit = down_cast<const PassMEDataHolder*>(data)->c_unit;
+ DCHECK(c_unit != nullptr);
+ // First initialize the data.
+ c_unit->mir_graph->InitializeMethodUses();
+
+ // Now check if the pass is to be ignored.
+ bool res = ((c_unit->disable_opt & (1 << kPromoteRegs)) == 0);
+
+ return res;
+}
+
+bool MethodUseCount::Worker(PassDataHolder* data) const {
+ DCHECK(data != nullptr);
+ PassMEDataHolder* pass_me_data_holder = down_cast<PassMEDataHolder*>(data);
+ CompilationUnit* c_unit = pass_me_data_holder->c_unit;
+ DCHECK(c_unit != nullptr);
+ BasicBlock* bb = pass_me_data_holder->bb;
+ DCHECK(bb != nullptr);
+ c_unit->mir_graph->CountUses(bb);
+ // No need of repeating, so just return false.
+ return false;
+}
+
} // namespace art
diff --git a/compiler/dex/bb_optimizations.h b/compiler/dex/bb_optimizations.h
index b07a415d4a..aac2644e9b 100644
--- a/compiler/dex/bb_optimizations.h
+++ b/compiler/dex/bb_optimizations.h
@@ -171,27 +171,6 @@ class NullCheckElimination : public PassME {
}
};
-/**
- * @class TypeInference
- * @brief Type inference pass.
- */
-class TypeInference : public PassME {
- public:
- TypeInference()
- : PassME("TypeInference", kRepeatingPreOrderDFSTraversal, "4_post_type_cfg") {
- }
-
- bool Worker(PassDataHolder* data) const {
- DCHECK(data != nullptr);
- PassMEDataHolder* pass_me_data_holder = down_cast<PassMEDataHolder*>(data);
- CompilationUnit* c_unit = pass_me_data_holder->c_unit;
- DCHECK(c_unit != nullptr);
- BasicBlock* bb = pass_me_data_holder->bb;
- DCHECK(bb != nullptr);
- return c_unit->mir_graph->InferTypes(bb);
- }
-};
-
class ClassInitCheckElimination : public PassME {
public:
ClassInitCheckElimination()
@@ -279,6 +258,48 @@ class BBCombine : public PassME {
};
/**
+ * @class ConstantPropagation
+ * @brief Perform a constant propagation pass.
+ */
+class ConstantPropagation : public PassME {
+ public:
+ ConstantPropagation() : PassME("ConstantPropagation") {
+ }
+
+ void Start(PassDataHolder* data) const {
+ DCHECK(data != nullptr);
+ CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
+ DCHECK(c_unit != nullptr);
+ c_unit->mir_graph->InitializeConstantPropagation();
+ }
+
+ bool Worker(PassDataHolder* data) const {
+ DCHECK(data != nullptr);
+ CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
+ DCHECK(c_unit != nullptr);
+ BasicBlock* bb = down_cast<PassMEDataHolder*>(data)->bb;
+ DCHECK(bb != nullptr);
+ c_unit->mir_graph->DoConstantPropagation(bb);
+ // No need of repeating, so just return false.
+ return false;
+ }
+};
+
+/**
+ * @class MethodUseCount
+ * @brief Count the register uses of the method
+ */
+class MethodUseCount : public PassME {
+ public:
+ MethodUseCount() : PassME("UseCount") {
+ }
+
+ bool Worker(PassDataHolder* data) const;
+
+ bool Gate(const PassDataHolder* data) const;
+};
+
+/**
* @class BasicBlock Optimizations
* @brief Any simple BasicBlock optimization can be put here.
*/
diff --git a/compiler/dex/mir_optimization.cc b/compiler/dex/mir_optimization.cc
index 15b83413b7..ebbd28f6c4 100644
--- a/compiler/dex/mir_optimization.cc
+++ b/compiler/dex/mir_optimization.cc
@@ -35,6 +35,7 @@ static unsigned int Predecessors(BasicBlock* bb) {
void MIRGraph::SetConstant(int32_t ssa_reg, int32_t value) {
is_constant_v_->SetBit(ssa_reg);
constant_values_[ssa_reg] = value;
+ reg_location_[ssa_reg].is_const = true;
}
void MIRGraph::SetConstantWide(int32_t ssa_reg, int64_t value) {
@@ -42,6 +43,8 @@ void MIRGraph::SetConstantWide(int32_t ssa_reg, int64_t value) {
is_constant_v_->SetBit(ssa_reg + 1);
constant_values_[ssa_reg] = Low32Bits(value);
constant_values_[ssa_reg + 1] = High32Bits(value);
+ reg_location_[ssa_reg].is_const = true;
+ reg_location_[ssa_reg + 1].is_const = true;
}
void MIRGraph::DoConstantPropagation(BasicBlock* bb) {
diff --git a/compiler/dex/pass_driver_me_opts.cc b/compiler/dex/pass_driver_me_opts.cc
index c476b2aabc..6bb94c3c0e 100644
--- a/compiler/dex/pass_driver_me_opts.cc
+++ b/compiler/dex/pass_driver_me_opts.cc
@@ -43,8 +43,9 @@ const Pass* const PassDriver<PassDriverMEOpts>::g_passes[] = {
GetPassInstance<NullCheckElimination>(),
GetPassInstance<BBCombine>(),
GetPassInstance<CodeLayout>(),
- GetPassInstance<TypeInference>(),
GetPassInstance<GlobalValueNumberingPass>(),
+ GetPassInstance<ConstantPropagation>(),
+ GetPassInstance<MethodUseCount>(),
GetPassInstance<BBOptimizations>(),
GetPassInstance<SuspendCheckElimination>(),
};
diff --git a/compiler/dex/pass_driver_me_post_opt.cc b/compiler/dex/pass_driver_me_post_opt.cc
index 9b56c0da87..5e2140d393 100644
--- a/compiler/dex/pass_driver_me_post_opt.cc
+++ b/compiler/dex/pass_driver_me_post_opt.cc
@@ -40,9 +40,8 @@ const Pass* const PassDriver<PassDriverMEPostOpt>::g_passes[] = {
GetPassInstance<CreatePhiNodes>(),
GetPassInstance<SSAConversion>(),
GetPassInstance<PhiNodeOperands>(),
- GetPassInstance<ConstantPropagation>(),
GetPassInstance<PerformInitRegLocations>(),
- GetPassInstance<MethodUseCount>(),
+ GetPassInstance<TypeInference>(),
GetPassInstance<FinishSSATransformation>(),
};
diff --git a/compiler/dex/post_opt_passes.cc b/compiler/dex/post_opt_passes.cc
index 675dbcf91d..92078b471b 100644
--- a/compiler/dex/post_opt_passes.cc
+++ b/compiler/dex/post_opt_passes.cc
@@ -20,35 +20,6 @@
namespace art {
-/*
- * MethodUseCount pass implementation start.
- */
-bool MethodUseCount::Gate(const PassDataHolder* data) const {
- DCHECK(data != nullptr);
- CompilationUnit* c_unit = down_cast<const PassMEDataHolder*>(data)->c_unit;
- DCHECK(c_unit != nullptr);
- // First initialize the data.
- c_unit->mir_graph->InitializeMethodUses();
-
- // Now check if the pass is to be ignored.
- bool res = ((c_unit->disable_opt & (1 << kPromoteRegs)) == 0);
-
- return res;
-}
-
-bool MethodUseCount::Worker(PassDataHolder* data) const {
- DCHECK(data != nullptr);
- PassMEDataHolder* pass_me_data_holder = down_cast<PassMEDataHolder*>(data);
- CompilationUnit* c_unit = pass_me_data_holder->c_unit;
- DCHECK(c_unit != nullptr);
- BasicBlock* bb = pass_me_data_holder->bb;
- DCHECK(bb != nullptr);
- c_unit->mir_graph->CountUses(bb);
- // No need of repeating, so just return false.
- return false;
-}
-
-
bool ClearPhiInstructions::Worker(PassDataHolder* data) const {
DCHECK(data != nullptr);
PassMEDataHolder* pass_me_data_holder = down_cast<PassMEDataHolder*>(data);
diff --git a/compiler/dex/post_opt_passes.h b/compiler/dex/post_opt_passes.h
index 964355bb5d..55ae874451 100644
--- a/compiler/dex/post_opt_passes.h
+++ b/compiler/dex/post_opt_passes.h
@@ -63,20 +63,6 @@ class InitializeSSATransformation : public PassMEMirSsaRep {
};
/**
- * @class MethodUseCount
- * @brief Count the register uses of the method
- */
-class MethodUseCount : public PassME {
- public:
- MethodUseCount() : PassME("UseCount") {
- }
-
- bool Worker(PassDataHolder* data) const;
-
- bool Gate(const PassDataHolder* data) const;
-};
-
-/**
* @class ClearPhiInformation
* @brief Clear the PHI nodes from the CFG.
*/
@@ -274,30 +260,22 @@ class PerformInitRegLocations : public PassMEMirSsaRep {
};
/**
- * @class ConstantPropagation
- * @brief Perform a constant propagation pass.
+ * @class TypeInference
+ * @brief Type inference pass.
*/
-class ConstantPropagation : public PassMEMirSsaRep {
+class TypeInference : public PassMEMirSsaRep {
public:
- ConstantPropagation() : PassMEMirSsaRep("ConstantPropagation") {
+ TypeInference() : PassMEMirSsaRep("TypeInference", kRepeatingPreOrderDFSTraversal) {
}
bool Worker(PassDataHolder* data) const {
DCHECK(data != nullptr);
- CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
+ PassMEDataHolder* pass_me_data_holder = down_cast<PassMEDataHolder*>(data);
+ CompilationUnit* c_unit = pass_me_data_holder->c_unit;
DCHECK(c_unit != nullptr);
- BasicBlock* bb = down_cast<PassMEDataHolder*>(data)->bb;
+ BasicBlock* bb = pass_me_data_holder->bb;
DCHECK(bb != nullptr);
- c_unit->mir_graph->DoConstantPropagation(bb);
- // No need of repeating, so just return false.
- return false;
- }
-
- void Start(PassDataHolder* data) const {
- DCHECK(data != nullptr);
- CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
- DCHECK(c_unit != nullptr);
- c_unit->mir_graph->InitializeConstantPropagation();
+ return c_unit->mir_graph->InferTypes(bb);
}
};
diff --git a/compiler/dex/quick/arm/int_arm.cc b/compiler/dex/quick/arm/int_arm.cc
index 860a987c2c..7970bd823d 100644
--- a/compiler/dex/quick/arm/int_arm.cc
+++ b/compiler/dex/quick/arm/int_arm.cc
@@ -573,6 +573,7 @@ bool ArmMir2Lir::GetEasyMultiplyOp(int lit, ArmMir2Lir::EasyMultiplyOp* op) {
// GenArithOpIntLit will directly generate exception-throwing code, and multiply-by-zero will
// have been optimized away earlier.
op->op = kOpInvalid;
+ op->shift = 0;
return true;
}
diff --git a/compiler/dex/vreg_analysis.cc b/compiler/dex/vreg_analysis.cc
index a541c7d7a7..62c40892cf 100644
--- a/compiler/dex/vreg_analysis.cc
+++ b/compiler/dex/vreg_analysis.cc
@@ -442,7 +442,7 @@ void MIRGraph::InitRegLocations() {
for (int i = 0; i < GetNumSSARegs(); i++) {
loc[i] = fresh_loc;
loc[i].s_reg_low = i;
- loc[i].is_const = is_constant_v_->IsBitSet(i);
+ loc[i].is_const = false; // Constants will be marked by constant propagation pass later.
loc[i].wide = false;
}
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index 9e8907078b..cdfd989bb8 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -330,23 +330,25 @@ bool CodeGenerator::GoesToNextBlock(HBasicBlock* current, HBasicBlock* next) con
CodeGenerator* CodeGenerator::Create(HGraph* graph,
InstructionSet instruction_set,
- const InstructionSetFeatures& isa_features) {
+ const InstructionSetFeatures& isa_features,
+ const CompilerOptions& compiler_options) {
switch (instruction_set) {
case kArm:
case kThumb2: {
return new arm::CodeGeneratorARM(graph,
- isa_features.AsArmInstructionSetFeatures());
+ *isa_features.AsArmInstructionSetFeatures(),
+ compiler_options);
}
case kArm64: {
- return new arm64::CodeGeneratorARM64(graph);
+ return new arm64::CodeGeneratorARM64(graph, compiler_options);
}
case kMips:
return nullptr;
case kX86: {
- return new x86::CodeGeneratorX86(graph);
+ return new x86::CodeGeneratorX86(graph, compiler_options);
}
case kX86_64: {
- return new x86_64::CodeGeneratorX86_64(graph);
+ return new x86_64::CodeGeneratorX86_64(graph, compiler_options);
}
default:
return nullptr;
@@ -696,11 +698,9 @@ void CodeGenerator::ClearSpillSlotsFromLoopPhisInStackMap(HSuspendCheck* suspend
}
void CodeGenerator::EmitParallelMoves(Location from1, Location to1, Location from2, Location to2) {
- MoveOperands move1(from1, to1, nullptr);
- MoveOperands move2(from2, to2, nullptr);
HParallelMove parallel_move(GetGraph()->GetArena());
- parallel_move.AddMove(&move1);
- parallel_move.AddMove(&move2);
+ parallel_move.AddMove(from1, to1, nullptr);
+ parallel_move.AddMove(from2, to2, nullptr);
GetMoveResolver()->EmitNativeCode(&parallel_move);
}
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h
index 88e50b6c88..682c260585 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -20,6 +20,7 @@
#include "arch/instruction_set.h"
#include "arch/instruction_set_features.h"
#include "base/bit_field.h"
+#include "driver/compiler_options.h"
#include "globals.h"
#include "locations.h"
#include "memory_region.h"
@@ -85,7 +86,8 @@ class CodeGenerator {
void CompileOptimized(CodeAllocator* allocator);
static CodeGenerator* Create(HGraph* graph,
InstructionSet instruction_set,
- const InstructionSetFeatures& isa_features);
+ const InstructionSetFeatures& isa_features,
+ const CompilerOptions& compiler_options);
virtual ~CodeGenerator() {}
HGraph* GetGraph() const { return graph_; }
@@ -130,6 +132,9 @@ class CodeGenerator {
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;
+
+ const CompilerOptions& GetCompilerOptions() const { return compiler_options_; }
+
// 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.
@@ -200,7 +205,8 @@ class CodeGenerator {
CodeGenerator(HGraph* graph,
size_t number_of_core_registers,
size_t number_of_fpu_registers,
- size_t number_of_register_pairs)
+ size_t number_of_register_pairs,
+ const CompilerOptions& compiler_options)
: frame_size_(kUninitializedFrameSize),
core_spill_mask_(0),
first_register_slot_in_slow_path_(0),
@@ -211,6 +217,7 @@ class CodeGenerator {
number_of_fpu_registers_(number_of_fpu_registers),
number_of_register_pairs_(number_of_register_pairs),
graph_(graph),
+ compiler_options_(compiler_options),
pc_infos_(graph->GetArena(), 32),
slow_paths_(graph->GetArena(), 8),
is_leaf_(true),
@@ -249,6 +256,7 @@ class CodeGenerator {
size_t GetStackOffsetOfSavedRegister(size_t index);
HGraph* const graph_;
+ const CompilerOptions& compiler_options_;
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 c4ba0fd3e5..1ca1cee275 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -49,9 +49,6 @@ static constexpr SRegister kRuntimeParameterFpuRegisters[] = { S0, S1, S2, S3 };
static constexpr size_t kRuntimeParameterFpuRegistersLength =
arraysize(kRuntimeParameterFpuRegisters);
-static constexpr DRegister DTMP = D7;
-static constexpr SRegister STMP = S14;
-
class InvokeRuntimeCallingConvention : public CallingConvention<Register, SRegister> {
public:
InvokeRuntimeCallingConvention()
@@ -387,8 +384,10 @@ size_t CodeGeneratorARM::RestoreFloatingPointRegister(size_t stack_index, uint32
}
CodeGeneratorARM::CodeGeneratorARM(HGraph* graph,
- const ArmInstructionSetFeatures* isa_features)
- : CodeGenerator(graph, kNumberOfCoreRegisters, kNumberOfSRegisters, kNumberOfRegisterPairs),
+ const ArmInstructionSetFeatures& isa_features,
+ const CompilerOptions& compiler_options)
+ : CodeGenerator(graph, kNumberOfCoreRegisters, kNumberOfSRegisters,
+ kNumberOfRegisterPairs, compiler_options),
block_labels_(graph->GetArena(), 0),
location_builder_(graph, this),
instruction_visitor_(graph, this),
@@ -475,11 +474,6 @@ void CodeGeneratorARM::SetupBlockedRegisters() const {
blocked_core_registers_[R10] = true;
blocked_core_registers_[R11] = true;
- // Don't allocate our temporary double register.
- blocked_fpu_registers_[STMP] = true;
- blocked_fpu_registers_[STMP + 1] = true;
- DCHECK_EQ(FromLowSToD(STMP), DTMP);
-
blocked_fpu_registers_[S16] = true;
blocked_fpu_registers_[S17] = true;
blocked_fpu_registers_[S18] = true;
@@ -793,7 +787,7 @@ void CodeGeneratorARM::Move(HInstruction* instruction, Location location, HInstr
__ StoreToOffset(kStoreWord, IP, SP, location.GetStackIndex());
}
} else {
- DCHECK(const_to_move->IsLongConstant()) << const_to_move;
+ DCHECK(const_to_move->IsLongConstant()) << const_to_move->DebugName();
int64_t value = const_to_move->AsLongConstant()->GetValue();
if (location.IsRegisterPair()) {
__ LoadImmediate(location.AsRegisterPairLow<Register>(), Low32Bits(value));
@@ -2616,7 +2610,7 @@ void LocationsBuilderARM::HandleFieldSet(HInstruction* instruction, const FieldI
bool is_wide = field_type == Primitive::kPrimLong || field_type == Primitive::kPrimDouble;
bool generate_volatile = field_info.IsVolatile()
&& is_wide
- && !codegen_->GetInstructionSetFeatures()->HasAtomicLdrdAndStrd();
+ && !codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
// Temporary registers for the write barrier.
// TODO: consider renaming StoreNeedsWriteBarrier to StoreNeedsGCMark.
if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1))) {
@@ -2649,7 +2643,7 @@ void InstructionCodeGeneratorARM::HandleFieldSet(HInstruction* instruction,
Location value = locations->InAt(1);
bool is_volatile = field_info.IsVolatile();
- bool atomic_ldrd_strd = codegen_->GetInstructionSetFeatures()->HasAtomicLdrdAndStrd();
+ bool atomic_ldrd_strd = codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
Primitive::Type field_type = field_info.GetFieldType();
uint32_t offset = field_info.GetFieldOffset().Uint32Value();
@@ -2738,7 +2732,7 @@ void LocationsBuilderARM::HandleFieldGet(HInstruction* instruction, const FieldI
bool generate_volatile = field_info.IsVolatile()
&& (field_info.GetFieldType() == Primitive::kPrimDouble)
- && !codegen_->GetInstructionSetFeatures()->HasAtomicLdrdAndStrd();
+ && !codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
if (generate_volatile) {
// Arm encoding have some additional constraints for ldrexd/strexd:
// - registers need to be consecutive
@@ -2759,7 +2753,7 @@ void InstructionCodeGeneratorARM::HandleFieldGet(HInstruction* instruction,
Register base = locations->InAt(0).AsRegister<Register>();
Location out = locations->Out();
bool is_volatile = field_info.IsVolatile();
- bool atomic_ldrd_strd = codegen_->GetInstructionSetFeatures()->HasAtomicLdrdAndStrd();
+ bool atomic_ldrd_strd = codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
Primitive::Type field_type = field_info.GetFieldType();
uint32_t offset = field_info.GetFieldOffset().Uint32Value();
@@ -2864,13 +2858,22 @@ void InstructionCodeGeneratorARM::VisitStaticFieldSet(HStaticFieldSet* instructi
void LocationsBuilderARM::VisitNullCheck(HNullCheck* instruction) {
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
- locations->SetInAt(0, Location::RequiresRegister());
+ Location loc = codegen_->GetCompilerOptions().GetImplicitNullChecks()
+ ? Location::RequiresRegister()
+ : Location::RegisterOrConstant(instruction->InputAt(0));
+ locations->SetInAt(0, loc);
if (instruction->HasUses()) {
locations->SetOut(Location::SameAsFirstInput());
}
}
-void InstructionCodeGeneratorARM::VisitNullCheck(HNullCheck* instruction) {
+void InstructionCodeGeneratorARM::GenerateImplicitNullCheck(HNullCheck* instruction) {
+ Location obj = instruction->GetLocations()->InAt(0);
+ __ LoadFromOffset(kLoadWord, IP, obj.AsRegister<Register>(), 0);
+ codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
+}
+
+void InstructionCodeGeneratorARM::GenerateExplicitNullCheck(HNullCheck* instruction) {
SlowPathCodeARM* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathARM(instruction);
codegen_->AddSlowPath(slow_path);
@@ -2887,6 +2890,14 @@ void InstructionCodeGeneratorARM::VisitNullCheck(HNullCheck* instruction) {
}
}
+void InstructionCodeGeneratorARM::VisitNullCheck(HNullCheck* instruction) {
+ if (codegen_->GetCompilerOptions().GetImplicitNullChecks()) {
+ GenerateImplicitNullCheck(instruction);
+ } else {
+ GenerateExplicitNullCheck(instruction);
+ }
+}
+
void LocationsBuilderARM::VisitArrayGet(HArrayGet* instruction) {
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
@@ -3302,15 +3313,6 @@ void ParallelMoveResolverARM::EmitMove(size_t index) {
DCHECK(destination.IsStackSlot());
__ StoreSToOffset(source.AsFpuRegister<SRegister>(), SP, destination.GetStackIndex());
}
- } else if (source.IsFpuRegisterPair()) {
- if (destination.IsFpuRegisterPair()) {
- __ vmovd(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()),
- FromLowSToD(source.AsFpuRegisterPairLow<SRegister>()));
- } else {
- DCHECK(destination.IsDoubleStackSlot()) << destination;
- __ StoreDToOffset(FromLowSToD(source.AsFpuRegisterPairLow<SRegister>()),
- SP, destination.GetStackIndex());
- }
} else if (source.IsDoubleStackSlot()) {
if (destination.IsFpuRegisterPair()) {
__ LoadDFromOffset(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()),
@@ -3383,9 +3385,9 @@ void ParallelMoveResolverARM::EmitSwap(size_t index) {
} else if (source.IsStackSlot() && destination.IsStackSlot()) {
Exchange(source.GetStackIndex(), destination.GetStackIndex());
} else if (source.IsFpuRegister() && destination.IsFpuRegister()) {
- __ vmovs(STMP, source.AsFpuRegister<SRegister>());
+ __ vmovrs(IP, source.AsFpuRegister<SRegister>());
__ vmovs(source.AsFpuRegister<SRegister>(), destination.AsFpuRegister<SRegister>());
- __ vmovs(destination.AsFpuRegister<SRegister>(), STMP);
+ __ vmovsr(destination.AsFpuRegister<SRegister>(), IP);
} else if (source.IsFpuRegister() || destination.IsFpuRegister()) {
SRegister reg = source.IsFpuRegister() ? source.AsFpuRegister<SRegister>()
: destination.AsFpuRegister<SRegister>();
@@ -3393,29 +3395,10 @@ void ParallelMoveResolverARM::EmitSwap(size_t index) {
? destination.GetStackIndex()
: source.GetStackIndex();
- __ vmovs(STMP, reg);
- __ LoadSFromOffset(reg, SP, mem);
- __ StoreSToOffset(STMP, SP, mem);
- } else if (source.IsFpuRegisterPair() && destination.IsFpuRegisterPair()) {
- __ vmovd(DTMP, FromLowSToD(source.AsFpuRegisterPairLow<SRegister>()));
- __ vmovd(FromLowSToD(source.AsFpuRegisterPairLow<SRegister>()),
- FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()));
- __ vmovd(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()), DTMP);
- } else if (source.IsFpuRegisterPair() || destination.IsFpuRegisterPair()) {
- DRegister reg = source.IsFpuRegisterPair()
- ? FromLowSToD(source.AsFpuRegisterPairLow<SRegister>())
- : FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>());
- int mem = source.IsFpuRegisterPair()
- ? destination.GetStackIndex()
- : source.GetStackIndex();
-
- __ vmovd(DTMP, reg);
- __ LoadDFromOffset(reg, SP, mem);
- __ StoreDToOffset(DTMP, SP, mem);
+ __ vmovrs(IP, reg);
+ __ LoadFromOffset(kLoadWord, IP, SP, mem);
+ __ StoreToOffset(kStoreWord, IP, SP, mem);
} else if (source.IsDoubleStackSlot() && destination.IsDoubleStackSlot()) {
- // TODO: We could use DTMP and ask for a pair scratch register (float or core).
- // This would save four instructions if two scratch registers are available, and
- // two instructions if not.
Exchange(source.GetStackIndex(), destination.GetStackIndex());
Exchange(source.GetHighStackIndex(kArmWordSize), destination.GetHighStackIndex(kArmWordSize));
} else {
diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h
index 267d9a2cef..fe373d54e3 100644
--- a/compiler/optimizing/code_generator_arm.h
+++ b/compiler/optimizing/code_generator_arm.h
@@ -19,6 +19,7 @@
#include "code_generator.h"
#include "dex/compiler_enums.h"
+#include "driver/compiler_options.h"
#include "nodes.h"
#include "parallel_move_resolver.h"
#include "utils/arm/assembler_thumb2.h"
@@ -143,7 +144,8 @@ class InstructionCodeGeneratorARM : public HGraphVisitor {
Register out_lo, Register out_hi);
void HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info);
void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info);
-
+ void GenerateImplicitNullCheck(HNullCheck* instruction);
+ void GenerateExplicitNullCheck(HNullCheck* instruction);
ArmAssembler* const assembler_;
CodeGeneratorARM* const codegen_;
@@ -153,7 +155,9 @@ class InstructionCodeGeneratorARM : public HGraphVisitor {
class CodeGeneratorARM : public CodeGenerator {
public:
- CodeGeneratorARM(HGraph* graph, const ArmInstructionSetFeatures* isa_features);
+ CodeGeneratorARM(HGraph* graph,
+ const ArmInstructionSetFeatures& isa_features,
+ const CompilerOptions& compiler_options);
virtual ~CodeGeneratorARM() {}
void GenerateFrameEntry() OVERRIDE;
@@ -234,7 +238,7 @@ class CodeGeneratorARM : public CodeGenerator {
block_labels_.SetSize(GetGraph()->GetBlocks().Size());
}
- const ArmInstructionSetFeatures* GetInstructionSetFeatures() const {
+ const ArmInstructionSetFeatures& GetInstructionSetFeatures() const {
return isa_features_;
}
@@ -249,7 +253,7 @@ class CodeGeneratorARM : public CodeGenerator {
InstructionCodeGeneratorARM instruction_visitor_;
ParallelMoveResolverARM move_resolver_;
Thumb2Assembler assembler_;
- const ArmInstructionSetFeatures* isa_features_;
+ const ArmInstructionSetFeatures& isa_features_;
DISALLOW_COPY_AND_ASSIGN(CodeGeneratorARM);
};
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 6d2c3de5d5..c1bce2a077 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -22,6 +22,7 @@
#include "mirror/array-inl.h"
#include "mirror/art_method.h"
#include "mirror/class.h"
+#include "offsets.h"
#include "thread.h"
#include "utils/arm64/assembler_arm64.h"
#include "utils/assembler.h"
@@ -562,11 +563,12 @@ Location InvokeDexCallingConventionVisitor::GetNextLocation(Primitive::Type type
return next_location;
}
-CodeGeneratorARM64::CodeGeneratorARM64(HGraph* graph)
+CodeGeneratorARM64::CodeGeneratorARM64(HGraph* graph, const CompilerOptions& compiler_options)
: CodeGenerator(graph,
kNumberOfAllocatableRegisters,
kNumberOfAllocatableFPRegisters,
- kNumberOfAllocatableRegisterPairs),
+ kNumberOfAllocatableRegisterPairs,
+ compiler_options),
block_labels_(nullptr),
location_builder_(graph, this),
instruction_visitor_(graph, this),
@@ -2291,7 +2293,14 @@ void LocationsBuilderARM64::VisitNullCheck(HNullCheck* instruction) {
}
}
-void InstructionCodeGeneratorARM64::VisitNullCheck(HNullCheck* instruction) {
+void InstructionCodeGeneratorARM64::GenerateImplicitNullCheck(HNullCheck* instruction) {
+ Location obj = instruction->GetLocations()->InAt(0);
+
+ __ Ldr(wzr, HeapOperandFrom(obj, Offset(0)));
+ codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
+}
+
+void InstructionCodeGeneratorARM64::GenerateExplicitNullCheck(HNullCheck* instruction) {
SlowPathCodeARM64* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathARM64(instruction);
codegen_->AddSlowPath(slow_path);
@@ -2306,6 +2315,14 @@ void InstructionCodeGeneratorARM64::VisitNullCheck(HNullCheck* instruction) {
}
}
+void InstructionCodeGeneratorARM64::VisitNullCheck(HNullCheck* instruction) {
+ if (codegen_->GetCompilerOptions().GetImplicitNullChecks()) {
+ GenerateImplicitNullCheck(instruction);
+ } else {
+ GenerateExplicitNullCheck(instruction);
+ }
+}
+
void LocationsBuilderARM64::VisitOr(HOr* instruction) {
HandleBinaryOp(instruction);
}
diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h
index 590bc1d778..e20d02ee2d 100644
--- a/compiler/optimizing/code_generator_arm64.h
+++ b/compiler/optimizing/code_generator_arm64.h
@@ -19,6 +19,7 @@
#include "code_generator.h"
#include "dex/compiler_enums.h"
+#include "driver/compiler_options.h"
#include "nodes.h"
#include "parallel_move_resolver.h"
#include "utils/arm64/assembler_arm64.h"
@@ -113,6 +114,8 @@ class InstructionCodeGeneratorARM64 : public HGraphVisitor {
void GenerateSuspendCheck(HSuspendCheck* instruction, HBasicBlock* successor);
void HandleBinaryOp(HBinaryOperation* instr);
void HandleShift(HBinaryOperation* instr);
+ void GenerateImplicitNullCheck(HNullCheck* instruction);
+ void GenerateExplicitNullCheck(HNullCheck* instruction);
Arm64Assembler* const assembler_;
CodeGeneratorARM64* const codegen_;
@@ -164,7 +167,7 @@ class ParallelMoveResolverARM64 : public ParallelMoveResolver {
class CodeGeneratorARM64 : public CodeGenerator {
public:
- explicit CodeGeneratorARM64(HGraph* graph);
+ CodeGeneratorARM64(HGraph* graph, const CompilerOptions& compiler_options);
virtual ~CodeGeneratorARM64() {}
void GenerateFrameEntry() OVERRIDE;
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 1a0df44ea6..9052b8fdec 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -373,8 +373,9 @@ size_t CodeGeneratorX86::RestoreCoreRegister(size_t stack_index, uint32_t reg_id
return kX86WordSize;
}
-CodeGeneratorX86::CodeGeneratorX86(HGraph* graph)
- : CodeGenerator(graph, kNumberOfCpuRegisters, kNumberOfXmmRegisters, kNumberOfRegisterPairs),
+CodeGeneratorX86::CodeGeneratorX86(HGraph* graph, const CompilerOptions& compiler_options)
+ : CodeGenerator(graph, kNumberOfCpuRegisters, kNumberOfXmmRegisters,
+ kNumberOfRegisterPairs, compiler_options),
block_labels_(graph->GetArena(), 0),
location_builder_(graph, this),
instruction_visitor_(graph, this),
@@ -2924,13 +2925,23 @@ void InstructionCodeGeneratorX86::VisitInstanceFieldGet(HInstanceFieldGet* instr
void LocationsBuilderX86::VisitNullCheck(HNullCheck* instruction) {
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
- locations->SetInAt(0, Location::Any());
+ Location loc = codegen_->GetCompilerOptions().GetImplicitNullChecks()
+ ? Location::RequiresRegister()
+ : Location::Any();
+ locations->SetInAt(0, loc);
if (instruction->HasUses()) {
locations->SetOut(Location::SameAsFirstInput());
}
}
-void InstructionCodeGeneratorX86::VisitNullCheck(HNullCheck* instruction) {
+void InstructionCodeGeneratorX86::GenerateImplicitNullCheck(HNullCheck* instruction) {
+ LocationSummary* locations = instruction->GetLocations();
+ Location obj = locations->InAt(0);
+ __ testl(EAX, Address(obj.AsRegister<Register>(), 0));
+ codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
+}
+
+void InstructionCodeGeneratorX86::GenerateExplicitNullCheck(HNullCheck* instruction) {
SlowPathCodeX86* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathX86(instruction);
codegen_->AddSlowPath(slow_path);
@@ -2950,6 +2961,14 @@ void InstructionCodeGeneratorX86::VisitNullCheck(HNullCheck* instruction) {
__ j(kEqual, slow_path->GetEntryLabel());
}
+void InstructionCodeGeneratorX86::VisitNullCheck(HNullCheck* instruction) {
+ if (codegen_->GetCompilerOptions().GetImplicitNullChecks()) {
+ GenerateImplicitNullCheck(instruction);
+ } else {
+ GenerateExplicitNullCheck(instruction);
+ }
+}
+
void LocationsBuilderX86::VisitArrayGet(HArrayGet* instruction) {
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
@@ -3363,7 +3382,7 @@ void ParallelMoveResolverX86::EmitMove(size_t index) {
__ movl(Address(ESP, destination.GetStackIndex()), imm);
}
} else {
- LOG(FATAL) << "Unimplemented";
+ LOG(FATAL) << "Unimplemented move: " << destination << " <- " << source;
}
}
diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h
index 2d8adb2cf1..b77a1aa856 100644
--- a/compiler/optimizing/code_generator_x86.h
+++ b/compiler/optimizing/code_generator_x86.h
@@ -19,6 +19,7 @@
#include "code_generator.h"
#include "dex/compiler_enums.h"
+#include "driver/compiler_options.h"
#include "nodes.h"
#include "parallel_move_resolver.h"
#include "utils/x86/assembler_x86.h"
@@ -144,6 +145,9 @@ class InstructionCodeGeneratorX86 : public HGraphVisitor {
void HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info);
void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info);
+ void GenerateImplicitNullCheck(HNullCheck* instruction);
+ void GenerateExplicitNullCheck(HNullCheck* instruction);
+
X86Assembler* const assembler_;
CodeGeneratorX86* const codegen_;
@@ -152,7 +156,7 @@ class InstructionCodeGeneratorX86 : public HGraphVisitor {
class CodeGeneratorX86 : public CodeGenerator {
public:
- explicit CodeGeneratorX86(HGraph* graph);
+ CodeGeneratorX86(HGraph* graph, const CompilerOptions& compiler_options);
virtual ~CodeGeneratorX86() {}
void GenerateFrameEntry() OVERRIDE;
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 3d7f122d36..723573afa0 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -416,8 +416,8 @@ size_t CodeGeneratorX86_64::RestoreFloatingPointRegister(size_t stack_index, uin
return kX86_64WordSize;
}
-CodeGeneratorX86_64::CodeGeneratorX86_64(HGraph* graph)
- : CodeGenerator(graph, kNumberOfCpuRegisters, kNumberOfFloatRegisters, 0),
+CodeGeneratorX86_64::CodeGeneratorX86_64(HGraph* graph, const CompilerOptions& compiler_options)
+ : CodeGenerator(graph, kNumberOfCpuRegisters, kNumberOfFloatRegisters, 0, compiler_options),
block_labels_(graph->GetArena(), 0),
location_builder_(graph, this),
instruction_visitor_(graph, this),
@@ -2623,13 +2623,24 @@ void InstructionCodeGeneratorX86_64::VisitStaticFieldSet(HStaticFieldSet* instru
void LocationsBuilderX86_64::VisitNullCheck(HNullCheck* instruction) {
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
- locations->SetInAt(0, Location::Any());
+ Location loc = codegen_->GetCompilerOptions().GetImplicitNullChecks()
+ ? Location::RequiresRegister()
+ : Location::Any();
+ locations->SetInAt(0, loc);
if (instruction->HasUses()) {
locations->SetOut(Location::SameAsFirstInput());
}
}
-void InstructionCodeGeneratorX86_64::VisitNullCheck(HNullCheck* instruction) {
+void InstructionCodeGeneratorX86_64::GenerateImplicitNullCheck(HNullCheck* instruction) {
+ LocationSummary* locations = instruction->GetLocations();
+ Location obj = locations->InAt(0);
+
+ __ testl(CpuRegister(RAX), Address(obj.AsRegister<CpuRegister>(), 0));
+ codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
+}
+
+void InstructionCodeGeneratorX86_64::GenerateExplicitNullCheck(HNullCheck* instruction) {
SlowPathCodeX86_64* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathX86_64(instruction);
codegen_->AddSlowPath(slow_path);
@@ -2649,6 +2660,14 @@ void InstructionCodeGeneratorX86_64::VisitNullCheck(HNullCheck* instruction) {
__ j(kEqual, slow_path->GetEntryLabel());
}
+void InstructionCodeGeneratorX86_64::VisitNullCheck(HNullCheck* instruction) {
+ if (codegen_->GetCompilerOptions().GetImplicitNullChecks()) {
+ GenerateImplicitNullCheck(instruction);
+ } else {
+ GenerateExplicitNullCheck(instruction);
+ }
+}
+
void LocationsBuilderX86_64::VisitArrayGet(HArrayGet* instruction) {
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h
index c501568a89..befe994efb 100644
--- a/compiler/optimizing/code_generator_x86_64.h
+++ b/compiler/optimizing/code_generator_x86_64.h
@@ -19,6 +19,7 @@
#include "code_generator.h"
#include "dex/compiler_enums.h"
+#include "driver/compiler_options.h"
#include "nodes.h"
#include "parallel_move_resolver.h"
#include "utils/x86_64/assembler_x86_64.h"
@@ -159,6 +160,8 @@ class InstructionCodeGeneratorX86_64 : public HGraphVisitor {
void GenerateMemoryBarrier(MemBarrierKind kind);
void HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info);
void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info);
+ void GenerateImplicitNullCheck(HNullCheck* instruction);
+ void GenerateExplicitNullCheck(HNullCheck* instruction);
X86_64Assembler* const assembler_;
CodeGeneratorX86_64* const codegen_;
@@ -168,7 +171,7 @@ class InstructionCodeGeneratorX86_64 : public HGraphVisitor {
class CodeGeneratorX86_64 : public CodeGenerator {
public:
- explicit CodeGeneratorX86_64(HGraph* graph);
+ CodeGeneratorX86_64(HGraph* graph, const CompilerOptions& compiler_options);
virtual ~CodeGeneratorX86_64() {}
void GenerateFrameEntry() OVERRIDE;
diff --git a/compiler/optimizing/codegen_test.cc b/compiler/optimizing/codegen_test.cc
index 18722f732b..aa4fc8f611 100644
--- a/compiler/optimizing/codegen_test.cc
+++ b/compiler/optimizing/codegen_test.cc
@@ -27,6 +27,7 @@
#include "common_compiler_test.h"
#include "dex_file.h"
#include "dex_instruction.h"
+#include "driver/compiler_options.h"
#include "nodes.h"
#include "optimizing_unit_test.h"
#include "prepare_for_register_allocation.h"
@@ -80,7 +81,8 @@ template <typename Expected>
static void RunCodeBaseline(HGraph* graph, bool has_result, Expected expected) {
InternalCodeAllocator allocator;
- x86::CodeGeneratorX86 codegenX86(graph);
+ CompilerOptions compiler_options;
+ x86::CodeGeneratorX86 codegenX86(graph, compiler_options);
// We avoid doing a stack overflow check that requires the runtime being setup,
// by making sure the compiler knows the methods we are running are leaf methods.
codegenX86.CompileBaseline(&allocator, true);
@@ -90,19 +92,19 @@ static void RunCodeBaseline(HGraph* graph, bool has_result, Expected expected) {
std::unique_ptr<const ArmInstructionSetFeatures> features(
ArmInstructionSetFeatures::FromCppDefines());
- arm::CodeGeneratorARM codegenARM(graph, features.get());
+ arm::CodeGeneratorARM codegenARM(graph, *features.get(), compiler_options);
codegenARM.CompileBaseline(&allocator, true);
if (kRuntimeISA == kArm || kRuntimeISA == kThumb2) {
Run(allocator, codegenARM, has_result, expected);
}
- x86_64::CodeGeneratorX86_64 codegenX86_64(graph);
+ x86_64::CodeGeneratorX86_64 codegenX86_64(graph, compiler_options);
codegenX86_64.CompileBaseline(&allocator, true);
if (kRuntimeISA == kX86_64) {
Run(allocator, codegenX86_64, has_result, expected);
}
- arm64::CodeGeneratorARM64 codegenARM64(graph);
+ arm64::CodeGeneratorARM64 codegenARM64(graph, compiler_options);
codegenARM64.CompileBaseline(&allocator, true);
if (kRuntimeISA == kArm64) {
Run(allocator, codegenARM64, has_result, expected);
@@ -132,17 +134,20 @@ static void RunCodeOptimized(HGraph* graph,
std::function<void(HGraph*)> hook_before_codegen,
bool has_result,
Expected expected) {
+ CompilerOptions compiler_options;
if (kRuntimeISA == kArm || kRuntimeISA == kThumb2) {
- arm::CodeGeneratorARM codegenARM(graph, ArmInstructionSetFeatures::FromCppDefines());
+ arm::CodeGeneratorARM codegenARM(graph,
+ *ArmInstructionSetFeatures::FromCppDefines(),
+ compiler_options);
RunCodeOptimized(&codegenARM, graph, hook_before_codegen, has_result, expected);
} else if (kRuntimeISA == kArm64) {
- arm64::CodeGeneratorARM64 codegenARM64(graph);
+ arm64::CodeGeneratorARM64 codegenARM64(graph, compiler_options);
RunCodeOptimized(&codegenARM64, graph, hook_before_codegen, has_result, expected);
} else if (kRuntimeISA == kX86) {
- x86::CodeGeneratorX86 codegenX86(graph);
+ x86::CodeGeneratorX86 codegenX86(graph, compiler_options);
RunCodeOptimized(&codegenX86, graph, hook_before_codegen, has_result, expected);
} else if (kRuntimeISA == kX86_64) {
- x86_64::CodeGeneratorX86_64 codegenX86_64(graph);
+ x86_64::CodeGeneratorX86_64 codegenX86_64(graph, compiler_options);
RunCodeOptimized(&codegenX86_64, graph, hook_before_codegen, has_result, expected);
}
}
diff --git a/compiler/optimizing/constant_folding_test.cc b/compiler/optimizing/constant_folding_test.cc
index ed7e57be7c..6ceccfbf0e 100644
--- a/compiler/optimizing/constant_folding_test.cc
+++ b/compiler/optimizing/constant_folding_test.cc
@@ -19,6 +19,7 @@
#include "code_generator_x86.h"
#include "constant_folding.h"
#include "dead_code_elimination.h"
+#include "driver/compiler_options.h"
#include "graph_checker.h"
#include "optimizing_unit_test.h"
#include "pretty_printer.h"
@@ -45,7 +46,7 @@ static void TestCode(const uint16_t* data,
std::string actual_before = printer_before.str();
ASSERT_EQ(expected_before, actual_before);
- x86::CodeGeneratorX86 codegen(graph);
+ x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
HConstantFolding(graph).Run();
SSAChecker ssa_checker_cf(&allocator, graph);
ssa_checker_cf.Run();
diff --git a/compiler/optimizing/dead_code_elimination_test.cc b/compiler/optimizing/dead_code_elimination_test.cc
index 3dbd04e250..a644719622 100644
--- a/compiler/optimizing/dead_code_elimination_test.cc
+++ b/compiler/optimizing/dead_code_elimination_test.cc
@@ -16,6 +16,7 @@
#include "code_generator_x86.h"
#include "dead_code_elimination.h"
+#include "driver/compiler_options.h"
#include "graph_checker.h"
#include "optimizing_unit_test.h"
#include "pretty_printer.h"
@@ -39,7 +40,7 @@ static void TestCode(const uint16_t* data,
std::string actual_before = printer_before.str();
ASSERT_EQ(actual_before, expected_before);
- x86::CodeGeneratorX86 codegen(graph);
+ x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
HDeadCodeElimination(graph).Run();
SSAChecker ssa_checker(&allocator, graph);
ssa_checker.Run();
diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc
index e55175faec..291b14cb52 100644
--- a/compiler/optimizing/graph_checker.cc
+++ b/compiler/optimizing/graph_checker.cc
@@ -363,9 +363,29 @@ static Primitive::Type PrimitiveKind(Primitive::Type type) {
}
}
+void SSAChecker::VisitIf(HIf* instruction) {
+ VisitInstruction(instruction);
+ HInstruction* input = instruction->InputAt(0);
+ if (input->IsIntConstant()) {
+ int value = input->AsIntConstant()->GetValue();
+ if (value != 0 && value != 1) {
+ std::stringstream error;
+ error << "If instruction " << instruction->GetId()
+ << " has a non-boolean constant input whose value is: "
+ << value << ".";
+ errors_.push_back(error.str());
+ }
+ } else if (instruction->InputAt(0)->GetType() != Primitive::kPrimBoolean) {
+ std::stringstream error;
+ error << "If instruction " << instruction->GetId()
+ << " has a non-boolean input type: "
+ << instruction->InputAt(0)->GetType() << ".";
+ errors_.push_back(error.str());
+ }
+}
+
void SSAChecker::VisitCondition(HCondition* op) {
VisitInstruction(op);
- // TODO: check inputs types, and special case the `null` check.
if (op->GetType() != Primitive::kPrimBoolean) {
std::stringstream error;
error << "Condition " << op->DebugName() << " " << op->GetId()
@@ -373,6 +393,46 @@ void SSAChecker::VisitCondition(HCondition* op) {
<< op->GetType() << ".";
errors_.push_back(error.str());
}
+ HInstruction* lhs = op->InputAt(0);
+ HInstruction* rhs = op->InputAt(1);
+ if (lhs->GetType() == Primitive::kPrimNot) {
+ if (!op->IsEqual() && !op->IsNotEqual()) {
+ std::stringstream error;
+ error << "Condition " << op->DebugName() << " " << op->GetId()
+ << " uses an object as left-hand side input.";
+ errors_.push_back(error.str());
+ }
+ if (rhs->IsIntConstant() && rhs->AsIntConstant()->GetValue() != 0) {
+ std::stringstream error;
+ error << "Condition " << op->DebugName() << " " << op->GetId()
+ << " compares an object with a non-0 integer: "
+ << rhs->AsIntConstant()->GetValue()
+ << ".";
+ errors_.push_back(error.str());
+ }
+ } else if (rhs->GetType() == Primitive::kPrimNot) {
+ if (!op->IsEqual() && !op->IsNotEqual()) {
+ std::stringstream error;
+ error << "Condition " << op->DebugName() << " " << op->GetId()
+ << " uses an object as right-hand side input.";
+ errors_.push_back(error.str());
+ }
+ if (lhs->IsIntConstant() && lhs->AsIntConstant()->GetValue() != 0) {
+ std::stringstream error;
+ error << "Condition " << op->DebugName() << " " << op->GetId()
+ << " compares a non-0 integer with an object: "
+ << lhs->AsIntConstant()->GetValue()
+ << ".";
+ errors_.push_back(error.str());
+ }
+ } else if (PrimitiveKind(lhs->GetType()) != PrimitiveKind(rhs->GetType())) {
+ std::stringstream error;
+ error << "Condition " << op->DebugName() << " " << op->GetId()
+ << " has inputs of different type: "
+ << lhs->GetType() << ", and " << rhs->GetType()
+ << ".";
+ errors_.push_back(error.str());
+ }
}
void SSAChecker::VisitBinaryOperation(HBinaryOperation* op) {
diff --git a/compiler/optimizing/graph_checker.h b/compiler/optimizing/graph_checker.h
index ba60cb99c6..ae1557b57c 100644
--- a/compiler/optimizing/graph_checker.h
+++ b/compiler/optimizing/graph_checker.h
@@ -101,6 +101,7 @@ class SSAChecker : public GraphChecker {
void VisitPhi(HPhi* phi) OVERRIDE;
void VisitBinaryOperation(HBinaryOperation* op) OVERRIDE;
void VisitCondition(HCondition* op) OVERRIDE;
+ void VisitIf(HIf* instruction) OVERRIDE;
private:
DISALLOW_COPY_AND_ASSIGN(SSAChecker);
diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc
index c1f4c94b7f..2c239458f1 100644
--- a/compiler/optimizing/intrinsics_x86_64.cc
+++ b/compiler/optimizing/intrinsics_x86_64.cc
@@ -116,7 +116,7 @@ static void MoveArguments(HInvoke* invoke, ArenaAllocator* arena, CodeGeneratorX
Location cc_loc = calling_convention_visitor.GetNextLocation(input->GetType());
Location actual_loc = locations->InAt(i);
- parallel_move.AddMove(new (arena) MoveOperands(actual_loc, cc_loc, nullptr));
+ parallel_move.AddMove(actual_loc, cc_loc, nullptr);
}
codegen->GetMoveResolver()->EmitNativeCode(&parallel_move);
diff --git a/compiler/optimizing/linearize_test.cc b/compiler/optimizing/linearize_test.cc
index 59404dcb14..2ab9b571ff 100644
--- a/compiler/optimizing/linearize_test.cc
+++ b/compiler/optimizing/linearize_test.cc
@@ -22,6 +22,7 @@
#include "code_generator_x86.h"
#include "dex_file.h"
#include "dex_instruction.h"
+#include "driver/compiler_options.h"
#include "graph_visualizer.h"
#include "nodes.h"
#include "optimizing_unit_test.h"
@@ -44,7 +45,7 @@ static void TestCode(const uint16_t* data, const int* expected_order, size_t num
graph->TryBuildingSsa();
- x86::CodeGeneratorX86 codegen(graph);
+ x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
SsaLivenessAnalysis liveness(*graph, &codegen);
liveness.Analyze();
diff --git a/compiler/optimizing/live_ranges_test.cc b/compiler/optimizing/live_ranges_test.cc
index 007c43e218..ff23eda21e 100644
--- a/compiler/optimizing/live_ranges_test.cc
+++ b/compiler/optimizing/live_ranges_test.cc
@@ -19,6 +19,7 @@
#include "code_generator_x86.h"
#include "dex_file.h"
#include "dex_instruction.h"
+#include "driver/compiler_options.h"
#include "nodes.h"
#include "optimizing_unit_test.h"
#include "prepare_for_register_allocation.h"
@@ -63,7 +64,7 @@ TEST(LiveRangesTest, CFG1) {
ArenaAllocator allocator(&pool);
HGraph* graph = BuildGraph(data, &allocator);
- x86::CodeGeneratorX86 codegen(graph);
+ x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
SsaLivenessAnalysis liveness(*graph, &codegen);
liveness.Analyze();
@@ -109,7 +110,7 @@ TEST(LiveRangesTest, CFG2) {
ArenaPool pool;
ArenaAllocator allocator(&pool);
HGraph* graph = BuildGraph(data, &allocator);
- x86::CodeGeneratorX86 codegen(graph);
+ x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
SsaLivenessAnalysis liveness(*graph, &codegen);
liveness.Analyze();
@@ -158,7 +159,7 @@ TEST(LiveRangesTest, CFG3) {
ArenaPool pool;
ArenaAllocator allocator(&pool);
HGraph* graph = BuildGraph(data, &allocator);
- x86::CodeGeneratorX86 codegen(graph);
+ x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
SsaLivenessAnalysis liveness(*graph, &codegen);
liveness.Analyze();
@@ -235,7 +236,7 @@ TEST(LiveRangesTest, Loop1) {
ArenaAllocator allocator(&pool);
HGraph* graph = BuildGraph(data, &allocator);
RemoveSuspendChecks(graph);
- x86::CodeGeneratorX86 codegen(graph);
+ x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
SsaLivenessAnalysis liveness(*graph, &codegen);
liveness.Analyze();
@@ -313,7 +314,7 @@ TEST(LiveRangesTest, Loop2) {
ArenaPool pool;
ArenaAllocator allocator(&pool);
HGraph* graph = BuildGraph(data, &allocator);
- x86::CodeGeneratorX86 codegen(graph);
+ x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
SsaLivenessAnalysis liveness(*graph, &codegen);
liveness.Analyze();
@@ -389,7 +390,7 @@ TEST(LiveRangesTest, CFG4) {
ArenaPool pool;
ArenaAllocator allocator(&pool);
HGraph* graph = BuildGraph(data, &allocator);
- x86::CodeGeneratorX86 codegen(graph);
+ x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
SsaLivenessAnalysis liveness(*graph, &codegen);
liveness.Analyze();
diff --git a/compiler/optimizing/liveness_test.cc b/compiler/optimizing/liveness_test.cc
index 6f706c391d..f2d49ac397 100644
--- a/compiler/optimizing/liveness_test.cc
+++ b/compiler/optimizing/liveness_test.cc
@@ -19,6 +19,7 @@
#include "code_generator_x86.h"
#include "dex_file.h"
#include "dex_instruction.h"
+#include "driver/compiler_options.h"
#include "nodes.h"
#include "optimizing_unit_test.h"
#include "prepare_for_register_allocation.h"
@@ -51,7 +52,7 @@ static void TestCode(const uint16_t* data, const char* expected) {
graph->TryBuildingSsa();
// `Inline` conditions into ifs.
PrepareForRegisterAllocation(graph).Run();
- x86::CodeGeneratorX86 codegen(graph);
+ x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
SsaLivenessAnalysis liveness(*graph, &codegen);
liveness.Analyze();
diff --git a/compiler/optimizing/locations.h b/compiler/optimizing/locations.h
index d41b3aecfd..68d605957f 100644
--- a/compiler/optimizing/locations.h
+++ b/compiler/optimizing/locations.h
@@ -290,18 +290,6 @@ class Location : public ValueObject {
return value_ == other.value_;
}
- // Returns whether this location contains `other`.
- bool Contains(Location other) const {
- if (Equals(other)) return true;
- if (IsRegisterPair() && other.IsRegister()) {
- return low() == other.reg() || high() == other.reg();
- }
- if (IsFpuRegisterPair() && other.IsFpuRegister()) {
- return low() == other.reg() || high() == other.reg();
- }
- return false;
- }
-
const char* DebugString() const {
switch (GetKind()) {
case kInvalid: return "I";
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index 4133cf676f..ade31380ec 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -643,7 +643,12 @@ HConstant* HBinaryOperation::TryStaticEvaluation() const {
} else if (GetLeft()->IsLongConstant() && GetRight()->IsLongConstant()) {
int64_t value = Evaluate(GetLeft()->AsLongConstant()->GetValue(),
GetRight()->AsLongConstant()->GetValue());
- return new(GetBlock()->GetGraph()->GetArena()) HLongConstant(value);
+ if (GetResultType() == Primitive::kPrimLong) {
+ return new(GetBlock()->GetGraph()->GetArena()) HLongConstant(value);
+ } else {
+ DCHECK(GetResultType() == Primitive::kPrimInt);
+ return new(GetBlock()->GetGraph()->GetArena()) HIntConstant(value);
+ }
}
return nullptr;
}
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 4838e1a97a..fa51f27f0a 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -2755,7 +2755,7 @@ class MoveOperands : public ArenaObject<kArenaAllocMisc> {
// True if this blocks a move from the given location.
bool Blocks(Location loc) const {
- return !IsEliminated() && source_.Contains(loc);
+ return !IsEliminated() && source_.Equals(loc);
}
// A move is redundant if it's been eliminated, if its source and
@@ -2784,8 +2784,6 @@ class MoveOperands : public ArenaObject<kArenaAllocMisc> {
// This is only used in debug mode, to ensure we do not connect interval siblings
// in the same parallel move.
HInstruction* instruction_;
-
- DISALLOW_COPY_AND_ASSIGN(MoveOperands);
};
static constexpr size_t kDefaultNumberOfMoves = 4;
@@ -2795,18 +2793,46 @@ class HParallelMove : public HTemplateInstruction<0> {
explicit HParallelMove(ArenaAllocator* arena)
: HTemplateInstruction(SideEffects::None()), moves_(arena, kDefaultNumberOfMoves) {}
- void AddMove(MoveOperands* move) {
- if (kIsDebugBuild && move->GetInstruction() != nullptr) {
- for (size_t i = 0, e = moves_.Size(); i < e; ++i) {
- DCHECK_NE(moves_.Get(i)->GetInstruction(), move->GetInstruction())
- << "Doing parallel moves for the same instruction.";
+ void AddMove(Location source, Location destination, HInstruction* instruction) {
+ DCHECK(source.IsValid());
+ DCHECK(destination.IsValid());
+ // The parallel move resolver does not handle pairs. So we decompose the
+ // pair locations into two moves.
+ if (source.IsPair() && destination.IsPair()) {
+ AddMove(source.ToLow(), destination.ToLow(), instruction);
+ AddMove(source.ToHigh(), destination.ToHigh(), nullptr);
+ } else if (source.IsPair()) {
+ DCHECK(destination.IsDoubleStackSlot());
+ AddMove(source.ToLow(), Location::StackSlot(destination.GetStackIndex()), instruction);
+ AddMove(source.ToHigh(), Location::StackSlot(destination.GetHighStackIndex(4)), nullptr);
+ } else if (destination.IsPair()) {
+ DCHECK(source.IsDoubleStackSlot());
+ AddMove(Location::StackSlot(source.GetStackIndex()), destination.ToLow(), instruction);
+ // TODO: rewrite GetHighStackIndex to not require a word size. It's supposed to
+ // always be 4.
+ static constexpr int kHighOffset = 4;
+ AddMove(Location::StackSlot(source.GetHighStackIndex(kHighOffset)),
+ destination.ToHigh(),
+ nullptr);
+ } else {
+ if (kIsDebugBuild) {
+ if (instruction != nullptr) {
+ for (size_t i = 0, e = moves_.Size(); i < e; ++i) {
+ DCHECK_NE(moves_.Get(i).GetInstruction(), instruction)
+ << "Doing parallel moves for the same instruction.";
+ }
+ }
+ for (size_t i = 0, e = moves_.Size(); i < e; ++i) {
+ DCHECK(!destination.Equals(moves_.Get(i).GetDestination()))
+ << "Same destination for two moves in a parallel move.";
+ }
}
+ moves_.Add(MoveOperands(source, destination, instruction));
}
- moves_.Add(move);
}
MoveOperands* MoveOperandsAt(size_t index) const {
- return moves_.Get(index);
+ return moves_.GetRawStorage() + index;
}
size_t NumMoves() const { return moves_.Size(); }
@@ -2814,7 +2840,7 @@ class HParallelMove : public HTemplateInstruction<0> {
DECLARE_INSTRUCTION(ParallelMove);
private:
- GrowableArray<MoveOperands*> moves_;
+ GrowableArray<MoveOperands> moves_;
DISALLOW_COPY_AND_ASSIGN(HParallelMove);
};
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 605637300f..1e0d65a945 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -208,11 +208,12 @@ static void RunOptimizations(HGraph* graph,
SsaRedundantPhiElimination redundant_phi(graph);
SsaDeadPhiElimination dead_phi(graph);
HDeadCodeElimination dce(graph);
- HConstantFolding fold(graph);
+ HConstantFolding fold1(graph);
InstructionSimplifier simplify1(graph);
HInliner inliner(graph, dex_compilation_unit, driver, stats);
+ HConstantFolding fold2(graph);
GVNOptimization gvn(graph);
BoundsCheckElimination bce(graph);
InstructionSimplifier simplify2(graph);
@@ -224,9 +225,10 @@ static void RunOptimizations(HGraph* graph,
&dead_phi,
&intrinsics,
&dce,
- &fold,
+ &fold1,
&simplify1,
&inliner,
+ &fold2,
&gvn,
&bce,
&simplify2
@@ -328,7 +330,8 @@ CompiledMethod* OptimizingCompiler::Compile(const DexFile::CodeItem* code_item,
const DexFile& dex_file) const {
UNUSED(invoke_type);
compilation_stats_.RecordStat(MethodCompilationStat::kAttemptCompilation);
- InstructionSet instruction_set = GetCompilerDriver()->GetInstructionSet();
+ CompilerDriver* compiler_driver = GetCompilerDriver();
+ InstructionSet instruction_set = compiler_driver->GetInstructionSet();
// Always use the thumb2 assembler: some runtime functionality (like implicit stack
// overflow checks) assume thumb2.
if (instruction_set == kArm) {
@@ -349,7 +352,7 @@ CompiledMethod* OptimizingCompiler::Compile(const DexFile::CodeItem* code_item,
DexCompilationUnit dex_compilation_unit(
nullptr, class_loader, art::Runtime::Current()->GetClassLinker(), dex_file, code_item,
class_def_idx, method_idx, access_flags,
- GetCompilerDriver()->GetVerifiedMethod(&dex_file, method_idx));
+ compiler_driver->GetVerifiedMethod(&dex_file, method_idx));
std::string method_name = PrettyMethod(method_idx, dex_file);
@@ -364,7 +367,7 @@ CompiledMethod* OptimizingCompiler::Compile(const DexFile::CodeItem* code_item,
&dex_compilation_unit,
&dex_compilation_unit,
&dex_file,
- GetCompilerDriver(),
+ compiler_driver,
&compilation_stats_);
VLOG(compiler) << "Building " << PrettyMethod(method_idx, dex_file);
@@ -374,9 +377,11 @@ CompiledMethod* OptimizingCompiler::Compile(const DexFile::CodeItem* code_item,
return nullptr;
}
- CompilerDriver* compiler_driver = GetCompilerDriver();
std::unique_ptr<CodeGenerator> codegen(
- CodeGenerator::Create(graph, instruction_set, *compiler_driver->GetInstructionSetFeatures()));
+ CodeGenerator::Create(graph,
+ instruction_set,
+ *compiler_driver->GetInstructionSetFeatures(),
+ compiler_driver->GetCompilerOptions()));
if (codegen.get() == nullptr) {
CHECK(!shouldCompile) << "Could not find code generator for optimizing compiler";
compilation_stats_.RecordStat(MethodCompilationStat::kNotCompiledNoCodegen);
diff --git a/compiler/optimizing/optimizing_unit_test.h b/compiler/optimizing/optimizing_unit_test.h
index 04b56345c4..b3eb1e2d51 100644
--- a/compiler/optimizing/optimizing_unit_test.h
+++ b/compiler/optimizing/optimizing_unit_test.h
@@ -45,8 +45,12 @@ namespace art {
LiveInterval* BuildInterval(const size_t ranges[][2],
size_t number_of_ranges,
ArenaAllocator* allocator,
- int reg = -1) {
- LiveInterval* interval = LiveInterval::MakeInterval(allocator, Primitive::kPrimInt);
+ int reg = -1,
+ HInstruction* defined_by = nullptr) {
+ LiveInterval* interval = LiveInterval::MakeInterval(allocator, Primitive::kPrimInt, defined_by);
+ if (defined_by != nullptr) {
+ defined_by->SetLiveInterval(interval);
+ }
for (size_t i = number_of_ranges; i > 0; --i) {
interval->AddRange(ranges[i - 1][0], ranges[i - 1][1]);
}
diff --git a/compiler/optimizing/parallel_move_resolver.cc b/compiler/optimizing/parallel_move_resolver.cc
index b8f5070999..debe466560 100644
--- a/compiler/optimizing/parallel_move_resolver.cc
+++ b/compiler/optimizing/parallel_move_resolver.cc
@@ -57,6 +57,9 @@ void ParallelMoveResolver::BuildInitialMoveList(HParallelMove* parallel_move) {
// unallocated, or the move was already eliminated).
for (size_t i = 0; i < parallel_move->NumMoves(); ++i) {
MoveOperands* move = parallel_move->MoveOperandsAt(i);
+ // The parallel move resolver algorithm does not work with register pairs.
+ DCHECK(!move->GetSource().IsPair());
+ DCHECK(!move->GetDestination().IsPair());
if (!move->IsRedundant()) {
moves_.Add(move);
}
diff --git a/compiler/optimizing/parallel_move_test.cc b/compiler/optimizing/parallel_move_test.cc
index 210f7d7f09..28b5697bbd 100644
--- a/compiler/optimizing/parallel_move_test.cc
+++ b/compiler/optimizing/parallel_move_test.cc
@@ -26,20 +26,26 @@ class TestParallelMoveResolver : public ParallelMoveResolver {
public:
explicit TestParallelMoveResolver(ArenaAllocator* allocator) : ParallelMoveResolver(allocator) {}
+ void Dump(Location location) {
+ if (location.IsConstant()) {
+ message_ << "C";
+ } else if (location.IsPair()) {
+ message_ << location.low() << "," << location.high();
+ } else {
+ message_ << location.reg();
+ }
+ }
+
virtual void EmitMove(size_t index) {
MoveOperands* move = moves_.Get(index);
if (!message_.str().empty()) {
message_ << " ";
}
message_ << "(";
- if (move->GetSource().IsConstant()) {
- message_ << "C";
- } else {
- message_ << move->GetSource().reg();
- }
- message_ << " -> "
- << move->GetDestination().reg()
- << ")";
+ Dump(move->GetSource());
+ message_ << " -> ";
+ Dump(move->GetDestination());
+ message_ << ")";
}
virtual void EmitSwap(size_t index) {
@@ -47,11 +53,11 @@ class TestParallelMoveResolver : public ParallelMoveResolver {
if (!message_.str().empty()) {
message_ << " ";
}
- message_ << "("
- << move->GetSource().reg()
- << " <-> "
- << move->GetDestination().reg()
- << ")";
+ message_ << "(";
+ Dump(move->GetSource());
+ message_ << " <-> ";
+ Dump(move->GetDestination());
+ message_ << ")";
}
virtual void SpillScratch(int reg ATTRIBUTE_UNUSED) {}
@@ -73,10 +79,10 @@ static HParallelMove* BuildParallelMove(ArenaAllocator* allocator,
size_t number_of_moves) {
HParallelMove* moves = new (allocator) HParallelMove(allocator);
for (size_t i = 0; i < number_of_moves; ++i) {
- moves->AddMove(new (allocator) MoveOperands(
+ moves->AddMove(
Location::RegisterLocation(operands[i][0]),
Location::RegisterLocation(operands[i][1]),
- nullptr));
+ nullptr);
}
return moves;
}
@@ -120,16 +126,9 @@ TEST(ParallelMoveTest, Swap) {
{
TestParallelMoveResolver resolver(&allocator);
- static constexpr size_t moves[][2] = {{0, 1}, {1, 2}, {2, 3}, {3, 4}, {4, 1}};
+ static constexpr size_t moves[][2] = {{0, 1}, {1, 2}, {2, 3}, {3, 4}, {4, 0}};
resolver.EmitNativeCode(BuildParallelMove(&allocator, moves, arraysize(moves)));
- ASSERT_STREQ("(4 <-> 1) (3 <-> 4) (2 <-> 3) (0 -> 1)", resolver.GetMessage().c_str());
- }
-
- {
- TestParallelMoveResolver resolver(&allocator);
- static constexpr size_t moves[][2] = {{0, 1}, {1, 2}, {2, 3}, {3, 4}, {4, 1}, {5, 4}};
- resolver.EmitNativeCode(BuildParallelMove(&allocator, moves, arraysize(moves)));
- ASSERT_STREQ("(4 <-> 1) (3 <-> 4) (2 <-> 3) (0 -> 1) (5 -> 4)", resolver.GetMessage().c_str());
+ ASSERT_STREQ("(4 <-> 0) (3 <-> 4) (2 <-> 3) (1 <-> 2)", resolver.GetMessage().c_str());
}
}
@@ -138,16 +137,66 @@ TEST(ParallelMoveTest, ConstantLast) {
ArenaAllocator allocator(&pool);
TestParallelMoveResolver resolver(&allocator);
HParallelMove* moves = new (&allocator) HParallelMove(&allocator);
- moves->AddMove(new (&allocator) MoveOperands(
+ moves->AddMove(
Location::ConstantLocation(new (&allocator) HIntConstant(0)),
Location::RegisterLocation(0),
- nullptr));
- moves->AddMove(new (&allocator) MoveOperands(
+ nullptr);
+ moves->AddMove(
Location::RegisterLocation(1),
Location::RegisterLocation(2),
- nullptr));
+ nullptr);
resolver.EmitNativeCode(moves);
ASSERT_STREQ("(1 -> 2) (C -> 0)", resolver.GetMessage().c_str());
}
+TEST(ParallelMoveTest, Pairs) {
+ ArenaPool pool;
+ ArenaAllocator allocator(&pool);
+
+ {
+ TestParallelMoveResolver resolver(&allocator);
+ HParallelMove* moves = new (&allocator) HParallelMove(&allocator);
+ moves->AddMove(
+ Location::RegisterLocation(2),
+ Location::RegisterLocation(4),
+ nullptr);
+ moves->AddMove(
+ Location::RegisterPairLocation(0, 1),
+ Location::RegisterPairLocation(2, 3),
+ nullptr);
+ resolver.EmitNativeCode(moves);
+ ASSERT_STREQ("(2 -> 4) (0 -> 2) (1 -> 3)", resolver.GetMessage().c_str());
+ }
+
+ {
+ TestParallelMoveResolver resolver(&allocator);
+ HParallelMove* moves = new (&allocator) HParallelMove(&allocator);
+ moves->AddMove(
+ Location::RegisterPairLocation(0, 1),
+ Location::RegisterPairLocation(2, 3),
+ nullptr);
+ moves->AddMove(
+ Location::RegisterLocation(2),
+ Location::RegisterLocation(4),
+ nullptr);
+ resolver.EmitNativeCode(moves);
+ ASSERT_STREQ("(2 -> 4) (0 -> 2) (1 -> 3)", resolver.GetMessage().c_str());
+ }
+
+ {
+ TestParallelMoveResolver resolver(&allocator);
+ HParallelMove* moves = new (&allocator) HParallelMove(&allocator);
+ moves->AddMove(
+ Location::RegisterPairLocation(0, 1),
+ Location::RegisterPairLocation(2, 3),
+ nullptr);
+ moves->AddMove(
+ Location::RegisterLocation(2),
+ Location::RegisterLocation(0),
+ nullptr);
+ resolver.EmitNativeCode(moves);
+ ASSERT_STREQ("(2 <-> 0) (1 -> 3)", resolver.GetMessage().c_str());
+ }
+}
+
} // namespace art
diff --git a/compiler/optimizing/register_allocator.cc b/compiler/optimizing/register_allocator.cc
index 1d155f9b5d..1b42e94960 100644
--- a/compiler/optimizing/register_allocator.cc
+++ b/compiler/optimizing/register_allocator.cc
@@ -857,7 +857,9 @@ bool RegisterAllocator::AllocateBlockedReg(LiveInterval* current) {
DCHECK(!active->IsFixed());
LiveInterval* split = Split(active, current->GetStart());
active_.DeleteAt(i);
- handled_.Add(active);
+ if (split != active) {
+ handled_.Add(active);
+ }
AddSorted(unhandled_, split);
break;
}
@@ -878,9 +880,14 @@ bool RegisterAllocator::AllocateBlockedReg(LiveInterval* current) {
if (next_intersection != kNoLifetime) {
if (inactive->IsFixed()) {
LiveInterval* split = Split(current, next_intersection);
+ DCHECK_NE(split, current);
AddSorted(unhandled_, split);
} else {
- LiveInterval* split = Split(inactive, next_intersection);
+ // Split at the start of `current`, which will lead to splitting
+ // at the end of the lifetime hole of `inactive`.
+ LiveInterval* split = Split(inactive, current->GetStart());
+ // If it's inactive, it must start before the current interval.
+ DCHECK_NE(split, inactive);
inactive_.DeleteAt(i);
--i;
--e;
@@ -1044,7 +1051,7 @@ void RegisterAllocator::AddInputMoveFor(HInstruction* user,
move = previous->AsParallelMove();
}
DCHECK_EQ(move->GetLifetimePosition(), user->GetLifetimePosition());
- move->AddMove(new (allocator_) MoveOperands(source, destination, nullptr));
+ move->AddMove(source, destination, nullptr);
}
static bool IsInstructionStart(size_t position) {
@@ -1116,7 +1123,7 @@ void RegisterAllocator::InsertParallelMoveAt(size_t position,
}
}
DCHECK_EQ(move->GetLifetimePosition(), position);
- move->AddMove(new (allocator_) MoveOperands(source, destination, instruction));
+ move->AddMove(source, destination, instruction);
}
void RegisterAllocator::InsertParallelMoveAtExitOf(HBasicBlock* block,
@@ -1146,7 +1153,7 @@ void RegisterAllocator::InsertParallelMoveAtExitOf(HBasicBlock* block,
} else {
move = previous->AsParallelMove();
}
- move->AddMove(new (allocator_) MoveOperands(source, destination, instruction));
+ move->AddMove(source, destination, instruction);
}
void RegisterAllocator::InsertParallelMoveAtEntryOf(HBasicBlock* block,
@@ -1165,7 +1172,7 @@ void RegisterAllocator::InsertParallelMoveAtEntryOf(HBasicBlock* block,
move->SetLifetimePosition(block->GetLifetimeStart());
block->InsertInstructionBefore(move, first);
}
- move->AddMove(new (allocator_) MoveOperands(source, destination, instruction));
+ move->AddMove(source, destination, instruction);
}
void RegisterAllocator::InsertMoveAfter(HInstruction* instruction,
@@ -1189,7 +1196,7 @@ void RegisterAllocator::InsertMoveAfter(HInstruction* instruction,
move->SetLifetimePosition(position);
instruction->GetBlock()->InsertInstructionBefore(move, instruction->GetNext());
}
- move->AddMove(new (allocator_) MoveOperands(source, destination, instruction));
+ move->AddMove(source, destination, instruction);
}
void RegisterAllocator::ConnectSiblings(LiveInterval* interval) {
diff --git a/compiler/optimizing/register_allocator.h b/compiler/optimizing/register_allocator.h
index 70841b8e1f..ec46a776b5 100644
--- a/compiler/optimizing/register_allocator.h
+++ b/compiler/optimizing/register_allocator.h
@@ -197,6 +197,7 @@ class RegisterAllocator {
size_t maximum_number_of_live_fp_registers_;
ART_FRIEND_TEST(RegisterAllocatorTest, FreeUntil);
+ ART_FRIEND_TEST(RegisterAllocatorTest, SpillInactive);
DISALLOW_COPY_AND_ASSIGN(RegisterAllocator);
};
diff --git a/compiler/optimizing/register_allocator_test.cc b/compiler/optimizing/register_allocator_test.cc
index c2ea80ec33..cb5010afcd 100644
--- a/compiler/optimizing/register_allocator_test.cc
+++ b/compiler/optimizing/register_allocator_test.cc
@@ -19,6 +19,7 @@
#include "code_generator_x86.h"
#include "dex_file.h"
#include "dex_instruction.h"
+#include "driver/compiler_options.h"
#include "nodes.h"
#include "optimizing_unit_test.h"
#include "register_allocator.h"
@@ -40,7 +41,7 @@ static bool Check(const uint16_t* data) {
const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);
HGraph* graph = builder.BuildGraph(*item);
graph->TryBuildingSsa();
- x86::CodeGeneratorX86 codegen(graph);
+ x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
SsaLivenessAnalysis liveness(*graph, &codegen);
liveness.Analyze();
RegisterAllocator register_allocator(&allocator, &codegen, liveness);
@@ -56,7 +57,7 @@ TEST(RegisterAllocatorTest, ValidateIntervals) {
ArenaPool pool;
ArenaAllocator allocator(&pool);
HGraph* graph = new (&allocator) HGraph(&allocator);
- x86::CodeGeneratorX86 codegen(graph);
+ x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
GrowableArray<LiveInterval*> intervals(&allocator, 0);
// Test with two intervals of the same range.
@@ -295,7 +296,7 @@ TEST(RegisterAllocatorTest, Loop3) {
ArenaPool pool;
ArenaAllocator allocator(&pool);
HGraph* graph = BuildSSAGraph(data, &allocator);
- x86::CodeGeneratorX86 codegen(graph);
+ x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
SsaLivenessAnalysis liveness(*graph, &codegen);
liveness.Analyze();
RegisterAllocator register_allocator(&allocator, &codegen, liveness);
@@ -327,7 +328,7 @@ TEST(RegisterAllocatorTest, FirstRegisterUse) {
ArenaPool pool;
ArenaAllocator allocator(&pool);
HGraph* graph = BuildSSAGraph(data, &allocator);
- x86::CodeGeneratorX86 codegen(graph);
+ x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
SsaLivenessAnalysis liveness(*graph, &codegen);
liveness.Analyze();
@@ -380,7 +381,7 @@ TEST(RegisterAllocatorTest, DeadPhi) {
ArenaAllocator allocator(&pool);
HGraph* graph = BuildSSAGraph(data, &allocator);
SsaDeadPhiElimination(graph).Run();
- x86::CodeGeneratorX86 codegen(graph);
+ x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
SsaLivenessAnalysis liveness(*graph, &codegen);
liveness.Analyze();
RegisterAllocator register_allocator(&allocator, &codegen, liveness);
@@ -402,7 +403,7 @@ TEST(RegisterAllocatorTest, FreeUntil) {
ArenaAllocator allocator(&pool);
HGraph* graph = BuildSSAGraph(data, &allocator);
SsaDeadPhiElimination(graph).Run();
- x86::CodeGeneratorX86 codegen(graph);
+ x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
SsaLivenessAnalysis liveness(*graph, &codegen);
liveness.Analyze();
RegisterAllocator register_allocator(&allocator, &codegen, liveness);
@@ -504,7 +505,7 @@ TEST(RegisterAllocatorTest, PhiHint) {
{
HGraph* graph = BuildIfElseWithPhi(&allocator, &phi, &input1, &input2);
- x86::CodeGeneratorX86 codegen(graph);
+ x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
SsaLivenessAnalysis liveness(*graph, &codegen);
liveness.Analyze();
@@ -519,7 +520,7 @@ TEST(RegisterAllocatorTest, PhiHint) {
{
HGraph* graph = BuildIfElseWithPhi(&allocator, &phi, &input1, &input2);
- x86::CodeGeneratorX86 codegen(graph);
+ x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
SsaLivenessAnalysis liveness(*graph, &codegen);
liveness.Analyze();
@@ -536,7 +537,7 @@ TEST(RegisterAllocatorTest, PhiHint) {
{
HGraph* graph = BuildIfElseWithPhi(&allocator, &phi, &input1, &input2);
- x86::CodeGeneratorX86 codegen(graph);
+ x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
SsaLivenessAnalysis liveness(*graph, &codegen);
liveness.Analyze();
@@ -553,7 +554,7 @@ TEST(RegisterAllocatorTest, PhiHint) {
{
HGraph* graph = BuildIfElseWithPhi(&allocator, &phi, &input1, &input2);
- x86::CodeGeneratorX86 codegen(graph);
+ x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
SsaLivenessAnalysis liveness(*graph, &codegen);
liveness.Analyze();
@@ -603,7 +604,7 @@ TEST(RegisterAllocatorTest, ExpectedInRegisterHint) {
{
HGraph* graph = BuildFieldReturn(&allocator, &field, &ret);
- x86::CodeGeneratorX86 codegen(graph);
+ x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
SsaLivenessAnalysis liveness(*graph, &codegen);
liveness.Analyze();
@@ -616,7 +617,7 @@ TEST(RegisterAllocatorTest, ExpectedInRegisterHint) {
{
HGraph* graph = BuildFieldReturn(&allocator, &field, &ret);
- x86::CodeGeneratorX86 codegen(graph);
+ x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
SsaLivenessAnalysis liveness(*graph, &codegen);
liveness.Analyze();
@@ -665,7 +666,7 @@ TEST(RegisterAllocatorTest, SameAsFirstInputHint) {
{
HGraph* graph = BuildTwoAdds(&allocator, &first_add, &second_add);
- x86::CodeGeneratorX86 codegen(graph);
+ x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
SsaLivenessAnalysis liveness(*graph, &codegen);
liveness.Analyze();
@@ -679,7 +680,7 @@ TEST(RegisterAllocatorTest, SameAsFirstInputHint) {
{
HGraph* graph = BuildTwoAdds(&allocator, &first_add, &second_add);
- x86::CodeGeneratorX86 codegen(graph);
+ x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
SsaLivenessAnalysis liveness(*graph, &codegen);
liveness.Analyze();
@@ -726,7 +727,7 @@ TEST(RegisterAllocatorTest, ExpectedExactInRegisterAndSameOutputHint) {
{
HGraph* graph = BuildDiv(&allocator, &div);
- x86::CodeGeneratorX86 codegen(graph);
+ x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
SsaLivenessAnalysis liveness(*graph, &codegen);
liveness.Analyze();
@@ -738,4 +739,106 @@ TEST(RegisterAllocatorTest, ExpectedExactInRegisterAndSameOutputHint) {
}
}
+// Test a bug in the register allocator, where allocating a blocked
+// register would lead to spilling an inactive interval at the wrong
+// position.
+TEST(RegisterAllocatorTest, SpillInactive) {
+ ArenaPool pool;
+
+ // Create a synthesized graph to please the register_allocator and
+ // ssa_liveness_analysis code.
+ ArenaAllocator allocator(&pool);
+ HGraph* graph = new (&allocator) HGraph(&allocator);
+ HBasicBlock* entry = new (&allocator) HBasicBlock(graph);
+ graph->AddBlock(entry);
+ graph->SetEntryBlock(entry);
+ HInstruction* one = new (&allocator) HParameterValue(0, Primitive::kPrimInt);
+ HInstruction* two = new (&allocator) HParameterValue(0, Primitive::kPrimInt);
+ HInstruction* three = new (&allocator) HParameterValue(0, Primitive::kPrimInt);
+ HInstruction* four = new (&allocator) HParameterValue(0, Primitive::kPrimInt);
+ entry->AddInstruction(one);
+ entry->AddInstruction(two);
+ entry->AddInstruction(three);
+ entry->AddInstruction(four);
+
+ HBasicBlock* block = new (&allocator) HBasicBlock(graph);
+ graph->AddBlock(block);
+ entry->AddSuccessor(block);
+ block->AddInstruction(new (&allocator) HExit());
+
+ // We create a synthesized user requesting a register, to avoid just spilling the
+ // intervals.
+ HPhi* user = new (&allocator) HPhi(&allocator, 0, 1, Primitive::kPrimInt);
+ user->AddInput(one);
+ user->SetBlock(block);
+ LocationSummary* locations = new (&allocator) LocationSummary(user, LocationSummary::kNoCall);
+ locations->SetInAt(0, Location::RequiresRegister());
+ static constexpr size_t phi_ranges[][2] = {{20, 30}};
+ BuildInterval(phi_ranges, arraysize(phi_ranges), &allocator, -1, user);
+
+ // Create an interval with lifetime holes.
+ static constexpr size_t ranges1[][2] = {{0, 2}, {4, 6}, {8, 10}};
+ LiveInterval* first = BuildInterval(ranges1, arraysize(ranges1), &allocator, -1, one);
+ first->first_use_ = new(&allocator) UsePosition(user, 0, false, 8, first->first_use_);
+ first->first_use_ = new(&allocator) UsePosition(user, 0, false, 7, first->first_use_);
+ first->first_use_ = new(&allocator) UsePosition(user, 0, false, 6, first->first_use_);
+
+ locations = new (&allocator) LocationSummary(first->GetDefinedBy(), LocationSummary::kNoCall);
+ locations->SetOut(Location::RequiresRegister());
+ first = first->SplitAt(1);
+
+ // Create an interval that conflicts with the next interval, to force the next
+ // interval to call `AllocateBlockedReg`.
+ static constexpr size_t ranges2[][2] = {{2, 4}};
+ LiveInterval* second = BuildInterval(ranges2, arraysize(ranges2), &allocator, -1, two);
+ locations = new (&allocator) LocationSummary(second->GetDefinedBy(), LocationSummary::kNoCall);
+ locations->SetOut(Location::RequiresRegister());
+
+ // Create an interval that will lead to splitting the first interval. The bug occured
+ // by splitting at a wrong position, in this case at the next intersection between
+ // this interval and the first interval. We would have then put the interval with ranges
+ // "[0, 2(, [4, 6(" in the list of handled intervals, even though we haven't processed intervals
+ // before lifetime position 6 yet.
+ static constexpr size_t ranges3[][2] = {{2, 4}, {8, 10}};
+ LiveInterval* third = BuildInterval(ranges3, arraysize(ranges3), &allocator, -1, three);
+ third->first_use_ = new(&allocator) UsePosition(user, 0, false, 8, third->first_use_);
+ third->first_use_ = new(&allocator) UsePosition(user, 0, false, 4, third->first_use_);
+ third->first_use_ = new(&allocator) UsePosition(user, 0, false, 3, third->first_use_);
+ locations = new (&allocator) LocationSummary(third->GetDefinedBy(), LocationSummary::kNoCall);
+ locations->SetOut(Location::RequiresRegister());
+ third = third->SplitAt(3);
+
+ // Because the first part of the split interval was considered handled, this interval
+ // was free to allocate the same register, even though it conflicts with it.
+ static constexpr size_t ranges4[][2] = {{4, 6}};
+ LiveInterval* fourth = BuildInterval(ranges4, arraysize(ranges4), &allocator, -1, four);
+ locations = new (&allocator) LocationSummary(fourth->GetDefinedBy(), LocationSummary::kNoCall);
+ locations->SetOut(Location::RequiresRegister());
+
+ x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
+ SsaLivenessAnalysis liveness(*graph, &codegen);
+
+ RegisterAllocator register_allocator(&allocator, &codegen, liveness);
+ register_allocator.unhandled_core_intervals_.Add(fourth);
+ register_allocator.unhandled_core_intervals_.Add(third);
+ register_allocator.unhandled_core_intervals_.Add(second);
+ register_allocator.unhandled_core_intervals_.Add(first);
+
+ // Set just one register available to make all intervals compete for the same.
+ register_allocator.number_of_registers_ = 1;
+ register_allocator.registers_array_ = allocator.AllocArray<size_t>(1);
+ register_allocator.processing_core_registers_ = true;
+ register_allocator.unhandled_ = &register_allocator.unhandled_core_intervals_;
+ register_allocator.LinearScan();
+
+ // Test that there is no conflicts between intervals.
+ GrowableArray<LiveInterval*> intervals(&allocator, 0);
+ intervals.Add(first);
+ intervals.Add(second);
+ intervals.Add(third);
+ intervals.Add(fourth);
+ ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
+ intervals, 0, 0, codegen, &allocator, true, false));
+}
+
} // namespace art
diff --git a/compiler/optimizing/ssa_liveness_analysis.h b/compiler/optimizing/ssa_liveness_analysis.h
index 74611e1cbb..a123313426 100644
--- a/compiler/optimizing/ssa_liveness_analysis.h
+++ b/compiler/optimizing/ssa_liveness_analysis.h
@@ -429,7 +429,7 @@ class LiveInterval : public ArenaObject<kArenaAllocMisc> {
LiveRange* current = first_range_;
LiveRange* previous = nullptr;
// Iterate over the ranges, and either find a range that covers this position, or
- // a two ranges in between this position (that is, the position is in a lifetime hole).
+ // two ranges in between this position (that is, the position is in a lifetime hole).
do {
if (position >= current->GetEnd()) {
// Move to next range.
@@ -530,7 +530,7 @@ class LiveInterval : public ArenaObject<kArenaAllocMisc> {
bool SameRegisterKind(Location other) const;
bool HasHighInterval() const {
- return !IsHighInterval() && (GetParent()->high_or_low_interval_ != nullptr);
+ return IsLowInterval();
}
bool HasLowInterval() const {
@@ -653,6 +653,8 @@ class LiveInterval : public ArenaObject<kArenaAllocMisc> {
static constexpr int kNoRegister = -1;
static constexpr int kNoSpillSlot = -1;
+ ART_FRIEND_TEST(RegisterAllocatorTest, SpillInactive);
+
DISALLOW_COPY_AND_ASSIGN(LiveInterval);
};
diff --git a/compiler/optimizing/test/ConstantFolding.java b/compiler/optimizing/test/ConstantFolding.java
deleted file mode 100644
index d08006b4d5..0000000000
--- a/compiler/optimizing/test/ConstantFolding.java
+++ /dev/null
@@ -1,221 +0,0 @@
-/*
-* 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.
-*/
-
-public class ConstantFolding {
-
- /**
- * Tiny three-register program exercising int constant folding
- * on negation.
- */
-
- // CHECK-START: int ConstantFolding.IntNegation() constant_folding (before)
- // CHECK-DAG: [[Const42:i\d+]] IntConstant 42
- // CHECK-DAG: [[Neg:i\d+]] Neg [ [[Const42]] ]
- // CHECK-DAG: Return [ [[Neg]] ]
-
- // CHECK-START: int ConstantFolding.IntNegation() constant_folding (after)
- // CHECK-DAG: [[ConstN42:i\d+]] IntConstant -42
- // CHECK-DAG: Return [ [[ConstN42]] ]
-
- public static int IntNegation() {
- int x, y;
- x = 42;
- y = -x;
- return y;
- }
-
- /**
- * Tiny three-register program exercising int constant folding
- * on addition.
- */
-
- // CHECK-START: int ConstantFolding.IntAddition1() constant_folding (before)
- // CHECK-DAG: [[Const1:i\d+]] IntConstant 1
- // CHECK-DAG: [[Const2:i\d+]] IntConstant 2
- // CHECK-DAG: [[Add:i\d+]] Add [ [[Const1]] [[Const2]] ]
- // CHECK-DAG: Return [ [[Add]] ]
-
- // CHECK-START: int ConstantFolding.IntAddition1() constant_folding (after)
- // CHECK-DAG: [[Const3:i\d+]] IntConstant 3
- // CHECK-DAG: Return [ [[Const3]] ]
-
- public static int IntAddition1() {
- int a, b, c;
- a = 1;
- b = 2;
- c = a + b;
- return c;
- }
-
- /**
- * Small three-register program exercising int constant folding
- * on addition.
- */
-
- // CHECK-START: int ConstantFolding.IntAddition2() constant_folding (before)
- // CHECK-DAG: [[Const1:i\d+]] IntConstant 1
- // CHECK-DAG: [[Const2:i\d+]] IntConstant 2
- // CHECK-DAG: [[Const5:i\d+]] IntConstant 5
- // CHECK-DAG: [[Const6:i\d+]] IntConstant 6
- // CHECK-DAG: [[Add1:i\d+]] Add [ [[Const1]] [[Const2]] ]
- // CHECK-DAG: [[Add2:i\d+]] Add [ [[Const5]] [[Const6]] ]
- // CHECK-DAG: [[Add3:i\d+]] Add [ [[Add1]] [[Add2]] ]
- // CHECK-DAG: Return [ [[Add3]] ]
-
- // CHECK-START: int ConstantFolding.IntAddition2() constant_folding (after)
- // CHECK-DAG: [[Const14:i\d+]] IntConstant 14
- // CHECK-DAG: Return [ [[Const14]] ]
-
- public static int IntAddition2() {
- int a, b, c;
- a = 1;
- b = 2;
- a += b;
- b = 5;
- c = 6;
- b += c;
- c = a + b;
- return c;
- }
-
- /**
- * Tiny three-register program exercising int constant folding
- * on subtraction.
- */
-
- // CHECK-START: int ConstantFolding.IntSubtraction() constant_folding (before)
- // CHECK-DAG: [[Const5:i\d+]] IntConstant 5
- // CHECK-DAG: [[Const2:i\d+]] IntConstant 2
- // CHECK-DAG: [[Sub:i\d+]] Sub [ [[Const5]] [[Const2]] ]
- // CHECK-DAG: Return [ [[Sub]] ]
-
- // CHECK-START: int ConstantFolding.IntSubtraction() constant_folding (after)
- // CHECK-DAG: [[Const3:i\d+]] IntConstant 3
- // CHECK-DAG: Return [ [[Const3]] ]
-
- public static int IntSubtraction() {
- int a, b, c;
- a = 5;
- b = 2;
- c = a - b;
- return c;
- }
-
- /**
- * Tiny three-register program exercising long constant folding
- * on addition.
- */
-
- // CHECK-START: long ConstantFolding.LongAddition() constant_folding (before)
- // CHECK-DAG: [[Const1:j\d+]] LongConstant 1
- // CHECK-DAG: [[Const2:j\d+]] LongConstant 2
- // CHECK-DAG: [[Add:j\d+]] Add [ [[Const1]] [[Const2]] ]
- // CHECK-DAG: Return [ [[Add]] ]
-
- // CHECK-START: long ConstantFolding.LongAddition() constant_folding (after)
- // CHECK-DAG: [[Const3:j\d+]] LongConstant 3
- // CHECK-DAG: Return [ [[Const3]] ]
-
- public static long LongAddition() {
- long a, b, c;
- a = 1L;
- b = 2L;
- c = a + b;
- return c;
- }
-
- /**
- * Tiny three-register program exercising long constant folding
- * on subtraction.
- */
-
- // CHECK-START: long ConstantFolding.LongSubtraction() constant_folding (before)
- // CHECK-DAG: [[Const5:j\d+]] LongConstant 5
- // CHECK-DAG: [[Const2:j\d+]] LongConstant 2
- // CHECK-DAG: [[Sub:j\d+]] Sub [ [[Const5]] [[Const2]] ]
- // CHECK-DAG: Return [ [[Sub]] ]
-
- // CHECK-START: long ConstantFolding.LongSubtraction() constant_folding (after)
- // CHECK-DAG: [[Const3:j\d+]] LongConstant 3
- // CHECK-DAG: Return [ [[Const3]] ]
-
- public static long LongSubtraction() {
- long a, b, c;
- a = 5L;
- b = 2L;
- c = a - b;
- return c;
- }
-
- /**
- * Three-register program with a constant (static) condition.
- */
-
- // CHECK-START: int ConstantFolding.StaticCondition() constant_folding (before)
- // CHECK-DAG: [[Const5:i\d+]] IntConstant 5
- // CHECK-DAG: [[Const2:i\d+]] IntConstant 2
- // CHECK-DAG: [[Cond:z\d+]] GreaterThanOrEqual [ [[Const5]] [[Const2]] ]
- // CHECK-DAG: If [ [[Cond]] ]
-
- // CHECK-START: int ConstantFolding.StaticCondition() constant_folding (after)
- // CHECK-DAG: [[Const1:i\d+]] IntConstant 1
- // CHECK-DAG: If [ [[Const1]] ]
-
- public static int StaticCondition() {
- int a, b, c;
- a = 5;
- b = 2;
- if (a < b)
- c = a + b;
- else
- c = a - b;
- return c;
- }
-
- /**
- * Four-variable program with jumps leading to the creation of many
- * blocks.
- *
- * The intent of this test is to ensure that all constant expressions
- * are actually evaluated at compile-time, thanks to the reverse
- * (forward) post-order traversal of the the dominator tree.
- */
-
- // CHECK-START: int ConstantFolding.JumpsAndConditionals(boolean) constant_folding (before)
- // CHECK-DAG: [[Const2:i\d+]] IntConstant 2
- // CHECK-DAG: [[Const5:i\d+]] IntConstant 5
- // CHECK-DAG: [[Add:i\d+]] Add [ [[Const5]] [[Const2]] ]
- // CHECK-DAG: [[Sub:i\d+]] Sub [ [[Const5]] [[Const2]] ]
- // CHECK-DAG: [[Phi:i\d+]] Phi [ [[Add]] [[Sub]] ]
- // CHECK-DAG: Return [ [[Phi]] ]
-
- // CHECK-START: int ConstantFolding.JumpsAndConditionals(boolean) constant_folding (after)
- // CHECK-DAG: [[Const3:i\d+]] IntConstant 3
- // CHECK-DAG: [[Const7:i\d+]] IntConstant 7
- // CHECK-DAG: [[Phi:i\d+]] Phi [ [[Const7]] [[Const3]] ]
- // CHECK-DAG: Return [ [[Phi]] ]
-
- public static int JumpsAndConditionals(boolean cond) {
- int a, b, c;
- a = 5;
- b = 2;
- if (cond)
- c = a + b;
- else
- c = a - b;
- return c;
- }
-}
diff --git a/compiler/optimizing/test/Inliner.java b/compiler/optimizing/test/Inliner.java
deleted file mode 100644
index 54cce62a57..0000000000
--- a/compiler/optimizing/test/Inliner.java
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
-* 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.
-*/
-
-public class Inliner {
-
- // CHECK-START: void Inliner.InlineVoid() inliner (before)
- // CHECK-DAG: [[Const42:i\d+]] IntConstant 42
- // CHECK-DAG: InvokeStaticOrDirect
- // CHECK-DAG: InvokeStaticOrDirect [ [[Const42]] ]
-
- // CHECK-START: void Inliner.InlineVoid() inliner (after)
- // CHECK-NOT: InvokeStaticOrDirect
-
- public static void InlineVoid() {
- returnVoid();
- returnVoidWithOneParameter(42);
- }
-
- // CHECK-START: int Inliner.InlineParameter(int) inliner (before)
- // CHECK-DAG: [[Param:i\d+]] ParameterValue
- // CHECK-DAG: [[Result:i\d+]] InvokeStaticOrDirect [ [[Param]] ]
- // CHECK-DAG: Return [ [[Result]] ]
-
- // CHECK-START: int Inliner.InlineParameter(int) inliner (after)
- // CHECK-DAG: [[Param:i\d+]] ParameterValue
- // CHECK-DAG: Return [ [[Param]] ]
-
- public static int InlineParameter(int a) {
- return returnParameter(a);
- }
-
- // CHECK-START: long Inliner.InlineWideParameter(long) inliner (before)
- // CHECK-DAG: [[Param:j\d+]] ParameterValue
- // CHECK-DAG: [[Result:j\d+]] InvokeStaticOrDirect [ [[Param]] ]
- // CHECK-DAG: Return [ [[Result]] ]
-
- // CHECK-START: long Inliner.InlineWideParameter(long) inliner (after)
- // CHECK-DAG: [[Param:j\d+]] ParameterValue
- // CHECK-DAG: Return [ [[Param]] ]
-
- public static long InlineWideParameter(long a) {
- return returnWideParameter(a);
- }
-
- // CHECK-START: java.lang.Object Inliner.InlineReferenceParameter(java.lang.Object) inliner (before)
- // CHECK-DAG: [[Param:l\d+]] ParameterValue
- // CHECK-DAG: [[Result:l\d+]] InvokeStaticOrDirect [ [[Param]] ]
- // CHECK-DAG: Return [ [[Result]] ]
-
- // CHECK-START: java.lang.Object Inliner.InlineReferenceParameter(java.lang.Object) inliner (after)
- // CHECK-DAG: [[Param:l\d+]] ParameterValue
- // CHECK-DAG: Return [ [[Param]] ]
-
- public static Object InlineReferenceParameter(Object o) {
- return returnReferenceParameter(o);
- }
-
- // CHECK-START: int Inliner.InlineInt() inliner (before)
- // CHECK-DAG: [[Result:i\d+]] InvokeStaticOrDirect
- // CHECK-DAG: Return [ [[Result]] ]
-
- // CHECK-START: int Inliner.InlineInt() inliner (after)
- // CHECK-DAG: [[Const4:i\d+]] IntConstant 4
- // CHECK-DAG: Return [ [[Const4]] ]
-
- public static int InlineInt() {
- return returnInt();
- }
-
- // CHECK-START: long Inliner.InlineWide() inliner (before)
- // CHECK-DAG: [[Result:j\d+]] InvokeStaticOrDirect
- // CHECK-DAG: Return [ [[Result]] ]
-
- // CHECK-START: long Inliner.InlineWide() inliner (after)
- // CHECK-DAG: [[Const8:j\d+]] LongConstant 8
- // CHECK-DAG: Return [ [[Const8]] ]
-
- public static long InlineWide() {
- return returnWide();
- }
-
- // CHECK-START: int Inliner.InlineAdd() inliner (before)
- // CHECK-DAG: [[Const3:i\d+]] IntConstant 3
- // CHECK-DAG: [[Const5:i\d+]] IntConstant 5
- // CHECK-DAG: [[Result:i\d+]] InvokeStaticOrDirect
- // CHECK-DAG: Return [ [[Result]] ]
-
- // CHECK-START: int Inliner.InlineAdd() inliner (after)
- // CHECK-DAG: [[Const3:i\d+]] IntConstant 3
- // CHECK-DAG: [[Const5:i\d+]] IntConstant 5
- // CHECK-DAG: [[Add:i\d+]] Add [ [[Const3]] [[Const5]] ]
- // CHECK-DAG: Return [ [[Add]] ]
-
- public static int InlineAdd() {
- return returnAdd(3, 5);
- }
-
- // CHECK-START: int Inliner.InlineFieldAccess() inliner (before)
- // CHECK-DAG: [[After:i\d+]] InvokeStaticOrDirect
- // CHECK-DAG: Return [ [[After]] ]
-
- // CHECK-START: int Inliner.InlineFieldAccess() inliner (after)
- // CHECK-DAG: [[Const1:i\d+]] IntConstant 1
- // CHECK-DAG: [[Before:i\d+]] StaticFieldGet
- // CHECK-DAG: [[After:i\d+]] Add [ [[Before]] [[Const1]] ]
- // CHECK-DAG: StaticFieldSet [ {{l\d+}} [[After]] ]
- // CHECK-DAG: Return [ [[After]] ]
-
- // CHECK-START: int Inliner.InlineFieldAccess() inliner (after)
- // CHECK-NOT: InvokeStaticOrDirect
-
- public static int InlineFieldAccess() {
- return incCounter();
- }
-
- // CHECK-START: int Inliner.InlineWithControlFlow(boolean) inliner (before)
- // CHECK-DAG: [[Const1:i\d+]] IntConstant 1
- // CHECK-DAG: [[Const3:i\d+]] IntConstant 3
- // CHECK-DAG: [[Const5:i\d+]] IntConstant 5
- // CHECK-DAG: [[Add:i\d+]] InvokeStaticOrDirect [ [[Const1]] [[Const3]] ]
- // CHECK-DAG: [[Sub:i\d+]] InvokeStaticOrDirect [ [[Const5]] [[Const3]] ]
- // CHECK-DAG: [[Phi:i\d+]] Phi [ [[Add]] [[Sub]] ]
- // CHECK-DAG: Return [ [[Phi]] ]
-
- // CHECK-START: int Inliner.InlineWithControlFlow(boolean) inliner (after)
- // CHECK-DAG: [[Const1:i\d+]] IntConstant 1
- // CHECK-DAG: [[Const3:i\d+]] IntConstant 3
- // CHECK-DAG: [[Const5:i\d+]] IntConstant 5
- // CHECK-DAG: [[Add:i\d+]] Add [ [[Const1]] [[Const3]] ]
- // CHECK-DAG: [[Sub:i\d+]] Sub [ [[Const5]] [[Const3]] ]
- // CHECK-DAG: [[Phi:i\d+]] Phi [ [[Add]] [[Sub]] ]
- // CHECK-DAG: Return [ [[Phi]] ]
-
- public static int InlineWithControlFlow(boolean cond) {
- int x, const1, const3, const5;
- const1 = 1;
- const3 = 3;
- const5 = 5;
- if (cond) {
- x = returnAdd(const1, const3);
- } else {
- x = returnSub(const5, const3);
- }
- return x;
- }
-
-
- private static void returnVoid() {
- return;
- }
-
- private static void returnVoidWithOneParameter(int a) {
- return;
- }
-
- private static int returnParameter(int a) {
- return a;
- }
-
- private static long returnWideParameter(long a) {
- return a;
- }
-
- private static Object returnReferenceParameter(Object o) {
- return o;
- }
-
- private static int returnInt() {
- return 4;
- }
-
- private static long returnWide() {
- return 8L;
- }
-
- private static int returnAdd(int a, int b) {
- return a + b;
- }
-
- private static int returnSub(int a, int b) {
- return a - b;
- }
-
- private static int counter = 42;
-
- private static int incCounter() {
- return ++counter;
- }
-}
diff --git a/compiler/utils/x86_64/assembler_x86_64.cc b/compiler/utils/x86_64/assembler_x86_64.cc
index c7414a12fc..906eabf4bb 100644
--- a/compiler/utils/x86_64/assembler_x86_64.cc
+++ b/compiler/utils/x86_64/assembler_x86_64.cc
@@ -1121,6 +1121,14 @@ void X86_64Assembler::testl(CpuRegister reg1, CpuRegister reg2) {
}
+void X86_64Assembler::testl(CpuRegister reg, const Address& address) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitOptionalRex32(reg, address);
+ EmitUint8(0x85);
+ EmitOperand(reg.LowBits(), address);
+}
+
+
void X86_64Assembler::testl(CpuRegister reg, const Immediate& immediate) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
// For registers that have a byte variant (RAX, RBX, RCX, and RDX)
diff --git a/compiler/utils/x86_64/assembler_x86_64.h b/compiler/utils/x86_64/assembler_x86_64.h
index 5c8d608cf2..4a509faa0c 100644
--- a/compiler/utils/x86_64/assembler_x86_64.h
+++ b/compiler/utils/x86_64/assembler_x86_64.h
@@ -409,6 +409,7 @@ class X86_64Assembler FINAL : public Assembler {
void cmpq(const Address& address, const Immediate& imm);
void testl(CpuRegister reg1, CpuRegister reg2);
+ void testl(CpuRegister reg, const Address& address);
void testl(CpuRegister reg, const Immediate& imm);
void testq(CpuRegister reg1, CpuRegister reg2);