diff options
93 files changed, 2083 insertions, 2616 deletions
diff --git a/Android.mk b/Android.mk index 3438beba3f..97a82e2077 100644 --- a/Android.mk +++ b/Android.mk @@ -77,6 +77,7 @@ include $(art_path)/build/Android.cpplint.mk # product rules include $(art_path)/runtime/Android.mk +include $(art_path)/runtime/simulator/Android.mk include $(art_path)/compiler/Android.mk include $(art_path)/dexdump/Android.mk include $(art_path)/dexlist/Android.mk diff --git a/cmdline/cmdline_parser_test.cc b/cmdline/cmdline_parser_test.cc index 529143d93d..fe83ba9e14 100644 --- a/cmdline/cmdline_parser_test.cc +++ b/cmdline/cmdline_parser_test.cc @@ -243,8 +243,8 @@ TEST_F(CmdlineParserTest, TestSimpleFailures) { TEST_F(CmdlineParserTest, TestLogVerbosity) { { const char* log_args = "-verbose:" - "class,compiler,gc,heap,jdwp,jni,monitor,profiler,signals,startup,third-party-jni," - "threads,verifier"; + "class,compiler,gc,heap,jdwp,jni,monitor,profiler,signals,simulator,startup," + "third-party-jni,threads,verifier"; LogVerbosity log_verbosity = LogVerbosity(); log_verbosity.class_linker = true; @@ -256,6 +256,7 @@ TEST_F(CmdlineParserTest, TestLogVerbosity) { log_verbosity.monitor = true; log_verbosity.profiler = true; log_verbosity.signals = true; + log_verbosity.simulator = true; log_verbosity.startup = true; log_verbosity.third_party_jni = true; log_verbosity.threads = true; diff --git a/cmdline/cmdline_types.h b/cmdline/cmdline_types.h index 47519894eb..6c0a0e1f4f 100644 --- a/cmdline/cmdline_types.h +++ b/cmdline/cmdline_types.h @@ -606,6 +606,8 @@ struct CmdlineType<LogVerbosity> : CmdlineTypeParser<LogVerbosity> { log_verbosity.profiler = true; } else if (verbose_options[j] == "signals") { log_verbosity.signals = true; + } else if (verbose_options[j] == "simulator") { + log_verbosity.simulator = true; } else if (verbose_options[j] == "startup") { log_verbosity.startup = true; } else if (verbose_options[j] == "third-party-jni") { diff --git a/compiler/Android.mk b/compiler/Android.mk index f0bf4997c6..bdd9a84433 100644 --- a/compiler/Android.mk +++ b/compiler/Android.mk @@ -92,6 +92,7 @@ LIBART_COMPILER_SRC_FILES := \ optimizing/parallel_move_resolver.cc \ optimizing/pc_relative_fixups_x86.cc \ optimizing/prepare_for_register_allocation.cc \ + optimizing/primitive_type_propagation.cc \ optimizing/reference_type_propagation.cc \ optimizing/register_allocator.cc \ optimizing/sharpening.cc \ diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc index c7c190793c..638b897eea 100644 --- a/compiler/common_compiler_test.cc +++ b/compiler/common_compiler_test.cc @@ -142,7 +142,10 @@ void CommonCompilerTest::MakeExecutable(mirror::ClassLoader* class_loader, const mirror::Class* klass = class_linker_->FindClass(self, class_descriptor.c_str(), loader); CHECK(klass != nullptr) << "Class not found " << class_name; size_t pointer_size = class_linker_->GetImagePointerSize(); - for (auto& m : klass->GetMethods(pointer_size)) { + for (auto& m : klass->GetDirectMethods(pointer_size)) { + MakeExecutable(&m); + } + for (auto& m : klass->GetVirtualMethods(pointer_size)) { MakeExecutable(&m); } } @@ -256,7 +259,10 @@ void CommonCompilerTest::CompileClass(mirror::ClassLoader* class_loader, const c mirror::Class* klass = class_linker_->FindClass(self, class_descriptor.c_str(), loader); CHECK(klass != nullptr) << "Class not found " << class_name; auto pointer_size = class_linker_->GetImagePointerSize(); - for (auto& m : klass->GetMethods(pointer_size)) { + for (auto& m : klass->GetDirectMethods(pointer_size)) { + CompileMethod(&m); + } + for (auto& m : klass->GetVirtualMethods(pointer_size)) { CompileMethod(&m); } } diff --git a/compiler/compiled_method.h b/compiler/compiled_method.h index 7a93613481..15a4ba0f6f 100644 --- a/compiler/compiled_method.h +++ b/compiler/compiled_method.h @@ -23,7 +23,7 @@ #include "arch/instruction_set.h" #include "base/bit_utils.h" -#include "base/length_prefixed_array.h" +#include "length_prefixed_array.h" #include "method_reference.h" #include "utils/array_ref.h" diff --git a/compiler/driver/compiled_method_storage.h b/compiler/driver/compiled_method_storage.h index d6961a0876..ef10b6768b 100644 --- a/compiler/driver/compiled_method_storage.h +++ b/compiler/driver/compiled_method_storage.h @@ -20,8 +20,8 @@ #include <iosfwd> #include <memory> -#include "base/length_prefixed_array.h" #include "base/macros.h" +#include "length_prefixed_array.h" #include "utils/array_ref.h" #include "utils/dedupe_set.h" #include "utils/swap_space.h" diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 56839f85f9..ba8f1d0df1 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -790,7 +790,10 @@ class ResolveCatchBlockExceptionsClassVisitor : public ClassVisitor { virtual bool Visit(mirror::Class* c) OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) { const auto pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); - for (auto& m : c->GetMethods(pointer_size)) { + for (auto& m : c->GetVirtualMethods(pointer_size)) { + ResolveExceptionsForMethod(&m, pointer_size); + } + for (auto& m : c->GetDirectMethods(pointer_size)) { ResolveExceptionsForMethod(&m, pointer_size); } return true; diff --git a/compiler/driver/compiler_driver_test.cc b/compiler/driver/compiler_driver_test.cc index 86f8b823cc..f8de9fa4a1 100644 --- a/compiler/driver/compiler_driver_test.cc +++ b/compiler/driver/compiler_driver_test.cc @@ -86,7 +86,10 @@ class CompilerDriverTest : public CommonCompilerTest { mirror::Class* c = class_linker->FindClass(soa.Self(), descriptor, loader); CHECK(c != nullptr); const auto pointer_size = class_linker->GetImagePointerSize(); - for (auto& m : c->GetMethods(pointer_size)) { + for (auto& m : c->GetDirectMethods(pointer_size)) { + MakeExecutable(&m); + } + for (auto& m : c->GetVirtualMethods(pointer_size)) { MakeExecutable(&m); } } diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index b9ec47d3fe..fce08ea5f0 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -1030,42 +1030,44 @@ void ImageWriter::WalkFieldsInOrder(mirror::Object* obj) { } } // Visit and assign offsets for methods. - size_t num_methods = as_klass->NumMethods(); - if (num_methods != 0) { + LengthPrefixedArray<ArtMethod>* method_arrays[] = { + as_klass->GetDirectMethodsPtr(), as_klass->GetVirtualMethodsPtr(), + }; + for (LengthPrefixedArray<ArtMethod>* array : method_arrays) { + if (array == nullptr) { + continue; + } bool any_dirty = false; - for (auto& m : as_klass->GetMethods(target_ptr_size_)) { - if (WillMethodBeDirty(&m)) { - any_dirty = true; - break; - } + size_t count = 0; + const size_t method_alignment = ArtMethod::Alignment(target_ptr_size_); + const size_t method_size = ArtMethod::Size(target_ptr_size_); + auto iteration_range = + MakeIterationRangeFromLengthPrefixedArray(array, method_size, method_alignment); + for (auto& m : iteration_range) { + any_dirty = any_dirty || WillMethodBeDirty(&m); + ++count; } NativeObjectRelocationType type = any_dirty ? kNativeObjectRelocationTypeArtMethodDirty : kNativeObjectRelocationTypeArtMethodClean; Bin bin_type = BinTypeForNativeRelocationType(type); // Forward the entire array at once, but header first. - const size_t method_alignment = ArtMethod::Alignment(target_ptr_size_); - const size_t method_size = ArtMethod::Size(target_ptr_size_); const size_t header_size = LengthPrefixedArray<ArtMethod>::ComputeSize(0, method_size, method_alignment); - LengthPrefixedArray<ArtMethod>* array = as_klass->GetMethodsPtr(); auto it = native_object_relocations_.find(array); - CHECK(it == native_object_relocations_.end()) - << "Method array " << array << " already forwarded"; + CHECK(it == native_object_relocations_.end()) << "Method array " << array + << " already forwarded"; size_t& offset = bin_slot_sizes_[bin_type]; DCHECK(!IsInBootImage(array)); - native_object_relocations_.emplace( - array, NativeObjectRelocation { - offset, - any_dirty ? kNativeObjectRelocationTypeArtMethodArrayDirty - : kNativeObjectRelocationTypeArtMethodArrayClean - }); + native_object_relocations_.emplace(array, NativeObjectRelocation { offset, + any_dirty ? kNativeObjectRelocationTypeArtMethodArrayDirty : + kNativeObjectRelocationTypeArtMethodArrayClean }); offset += header_size; - for (auto& m : as_klass->GetMethods(target_ptr_size_)) { + for (auto& m : iteration_range) { AssignMethodOffset(&m, type); } - (any_dirty ? dirty_methods_ : clean_methods_) += num_methods; + (any_dirty ? dirty_methods_ : clean_methods_) += count; } } else if (h_obj->IsObjectArray()) { // Walk elements of an object array. diff --git a/compiler/image_writer.h b/compiler/image_writer.h index f1b2965a12..8e930f0373 100644 --- a/compiler/image_writer.h +++ b/compiler/image_writer.h @@ -27,11 +27,11 @@ #include <ostream> #include "base/bit_utils.h" -#include "base/length_prefixed_array.h" #include "base/macros.h" #include "driver/compiler_driver.h" #include "gc/space/space.h" #include "image.h" +#include "length_prefixed_array.h" #include "lock_word.h" #include "mem_map.h" #include "oat_file.h" diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc index 451aa682d6..eea52045cc 100644 --- a/compiler/oat_test.cc +++ b/compiler/oat_test.cc @@ -238,12 +238,12 @@ TEST_F(OatTest, WriteRead) { ++method_index; } size_t visited_virtuals = 0; - // TODO We should also check copied methods in this test. - for (auto& m : klass->GetDeclaredVirtualMethods(pointer_size)) { - EXPECT_FALSE(m.IsMiranda()); - CheckMethod(&m, oat_class.GetOatMethod(method_index), dex_file); - ++method_index; - ++visited_virtuals; + for (auto& m : klass->GetVirtualMethods(pointer_size)) { + if (!m.IsMiranda()) { + CheckMethod(&m, oat_class.GetOatMethod(method_index), dex_file); + ++method_index; + ++visited_virtuals; + } } EXPECT_EQ(visited_virtuals, num_virtual_methods); } diff --git a/compiler/optimizing/bounds_check_elimination.cc b/compiler/optimizing/bounds_check_elimination.cc index dc75ff1abc..4c3f66aa4f 100644 --- a/compiler/optimizing/bounds_check_elimination.cc +++ b/compiler/optimizing/bounds_check_elimination.cc @@ -1590,18 +1590,15 @@ class BCEVisitor : public HGraphVisitor { HGraph* graph = GetGraph(); HInstruction* zero; switch (type) { - case Primitive::kPrimNot: zero = graph->GetNullConstant(); break; - case Primitive::kPrimFloat: zero = graph->GetFloatConstant(0); break; - case Primitive::kPrimDouble: zero = graph->GetDoubleConstant(0); break; + case Primitive::Type::kPrimNot: zero = graph->GetNullConstant(); break; + case Primitive::Type::kPrimFloat: zero = graph->GetFloatConstant(0); break; + case Primitive::Type::kPrimDouble: zero = graph->GetDoubleConstant(0); break; default: zero = graph->GetConstant(type, 0); break; } HPhi* phi = new (graph->GetArena()) HPhi(graph->GetArena(), kNoRegNumber, /*number_of_inputs*/ 2, HPhi::ToPhiType(type)); phi->SetRawInputAt(0, instruction); phi->SetRawInputAt(1, zero); - if (type == Primitive::kPrimNot) { - phi->SetReferenceTypeInfo(instruction->GetReferenceTypeInfo()); - } new_preheader->AddPhi(phi); return phi; } diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h index ca71c32802..c3979f3dd1 100644 --- a/compiler/optimizing/builder.h +++ b/compiler/optimizing/builder.h @@ -90,9 +90,8 @@ class HGraphBuilder : public ValueObject { static constexpr const char* kBuilderPassName = "builder"; - // The number of entries in a packed switch before we use a jump table or specified - // compare/jump series. - static constexpr uint16_t kSmallSwitchThreshold = 3; + // The number of entries in a packed switch before we use a jump table. + static constexpr uint16_t kSmallSwitchThreshold = 5; private: // Analyzes the dex instruction and adds HInstruction to the graph diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index 9fda83840c..3630dbec24 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -59,7 +59,7 @@ static constexpr SRegister kFpuCalleeSaves[] = // S registers. Therefore there is no need to block it. static constexpr DRegister DTMP = D31; -static constexpr uint32_t kPackedSwitchCompareJumpThreshold = 7; +static constexpr uint32_t kPackedSwitchJumpTableThreshold = 6; #define __ down_cast<ArmAssembler*>(codegen->GetAssembler())-> #define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kArmWordSize, x).Int32Value() @@ -6250,7 +6250,7 @@ void LocationsBuilderARM::VisitPackedSwitch(HPackedSwitch* switch_instr) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(switch_instr, LocationSummary::kNoCall); locations->SetInAt(0, Location::RequiresRegister()); - if (switch_instr->GetNumEntries() > kPackedSwitchCompareJumpThreshold && + if (switch_instr->GetNumEntries() >= kPackedSwitchJumpTableThreshold && codegen_->GetAssembler()->IsThumb()) { locations->AddTemp(Location::RequiresRegister()); // We need a temp for the table base. if (switch_instr->GetStartValue() != 0) { @@ -6266,30 +6266,12 @@ void InstructionCodeGeneratorARM::VisitPackedSwitch(HPackedSwitch* switch_instr) Register value_reg = locations->InAt(0).AsRegister<Register>(); HBasicBlock* default_block = switch_instr->GetDefaultBlock(); - if (num_entries <= kPackedSwitchCompareJumpThreshold || !codegen_->GetAssembler()->IsThumb()) { + if (num_entries < kPackedSwitchJumpTableThreshold || !codegen_->GetAssembler()->IsThumb()) { // Create a series of compare/jumps. - Register temp_reg = IP; - // Note: It is fine for the below AddConstantSetFlags() using IP register to temporarily store - // the immediate, because IP is used as the destination register. For the other - // AddConstantSetFlags() and GenerateCompareWithImmediate(), the immediate values are constant, - // and they can be encoded in the instruction without making use of IP register. - __ AddConstantSetFlags(temp_reg, value_reg, -lower_bound); - const ArenaVector<HBasicBlock*>& successors = switch_instr->GetBlock()->GetSuccessors(); - // Jump to successors[0] if value == lower_bound. - __ b(codegen_->GetLabelOf(successors[0]), EQ); - int32_t last_index = 0; - for (; num_entries - last_index > 2; last_index += 2) { - __ AddConstantSetFlags(temp_reg, temp_reg, -2); - // Jump to successors[last_index + 1] if value < case_value[last_index + 2]. - __ b(codegen_->GetLabelOf(successors[last_index + 1]), LO); - // Jump to successors[last_index + 2] if value == case_value[last_index + 2]. - __ b(codegen_->GetLabelOf(successors[last_index + 2]), EQ); - } - if (num_entries - last_index == 2) { - // The last missing case_value. - GenerateCompareWithImmediate(temp_reg, 1); - __ b(codegen_->GetLabelOf(successors[last_index + 1]), EQ); + for (uint32_t i = 0; i < num_entries; i++) { + GenerateCompareWithImmediate(value_reg, lower_bound + i); + __ b(codegen_->GetLabelOf(successors[i]), EQ); } // And the default for any other value. diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index 52058302be..451470f271 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -71,10 +71,10 @@ using helpers::ARM64EncodableConstantOrRegister; using helpers::ArtVixlRegCodeCoherentForRegSet; static constexpr int kCurrentMethodStackOffset = 0; -// The compare/jump sequence will generate about (1.5 * num_entries + 3) instructions. While jump +// The compare/jump sequence will generate about (2 * num_entries + 1) instructions. While jump // table version generates 7 instructions and num_entries literals. Compare/jump sequence will // generates less code/data with a small num_entries. -static constexpr uint32_t kPackedSwitchCompareJumpThreshold = 7; +static constexpr uint32_t kPackedSwitchJumpTableThreshold = 6; inline Condition ARM64Condition(IfCondition cond) { switch (cond) { @@ -546,7 +546,7 @@ class ArraySetSlowPathARM64 : public SlowPathCodeARM64 { void JumpTableARM64::EmitTable(CodeGeneratorARM64* codegen) { uint32_t num_entries = switch_instr_->GetNumEntries(); - DCHECK_GE(num_entries, kPackedSwitchCompareJumpThreshold); + DCHECK_GE(num_entries, kPackedSwitchJumpTableThreshold); // We are about to use the assembler to place literals directly. Make sure we have enough // underlying code buffer and we have generated the jump table with right size. @@ -4582,29 +4582,20 @@ void InstructionCodeGeneratorARM64::VisitPackedSwitch(HPackedSwitch* switch_inst // ranges and emit the tables only as required. static constexpr int32_t kJumpTableInstructionThreshold = 1* MB / kMaxExpectedSizePerHInstruction; - if (num_entries <= kPackedSwitchCompareJumpThreshold || + if (num_entries < kPackedSwitchJumpTableThreshold || // Current instruction id is an upper bound of the number of HIRs in the graph. GetGraph()->GetCurrentInstructionId() > kJumpTableInstructionThreshold) { // Create a series of compare/jumps. - UseScratchRegisterScope temps(codegen_->GetVIXLAssembler()); - Register temp = temps.AcquireW(); - __ Subs(temp, value_reg, Operand(lower_bound)); - const ArenaVector<HBasicBlock*>& successors = switch_instr->GetBlock()->GetSuccessors(); - // Jump to successors[0] if value == lower_bound. - __ B(eq, codegen_->GetLabelOf(successors[0])); - int32_t last_index = 0; - for (; num_entries - last_index > 2; last_index += 2) { - __ Subs(temp, temp, Operand(2)); - // Jump to successors[last_index + 1] if value < case_value[last_index + 2]. - __ B(lo, codegen_->GetLabelOf(successors[last_index + 1])); - // Jump to successors[last_index + 2] if value == case_value[last_index + 2]. - __ B(eq, codegen_->GetLabelOf(successors[last_index + 2])); - } - if (num_entries - last_index == 2) { - // The last missing case_value. - __ Cmp(temp, Operand(1)); - __ B(eq, codegen_->GetLabelOf(successors[last_index + 1])); + for (uint32_t i = 0; i < num_entries; i++) { + int32_t case_value = lower_bound + i; + vixl::Label* succ = codegen_->GetLabelOf(successors[i]); + if (case_value == 0) { + __ Cbz(value_reg, succ); + } else { + __ Cmp(value_reg, Operand(case_value)); + __ B(eq, succ); + } } // And the default for any other value. diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc index 21bc8fd493..99a0982fe2 100644 --- a/compiler/optimizing/code_generator_mips.cc +++ b/compiler/optimizing/code_generator_mips.cc @@ -4897,31 +4897,19 @@ void InstructionCodeGeneratorMIPS::VisitPackedSwitch(HPackedSwitch* switch_instr HBasicBlock* default_block = switch_instr->GetDefaultBlock(); // Create a set of compare/jumps. - Register temp_reg = TMP; - __ Addiu32(temp_reg, value_reg, -lower_bound); - // Jump to default if index is negative - // Note: We don't check the case that index is positive while value < lower_bound, because in - // this case, index >= num_entries must be true. So that we can save one branch instruction. - __ Bltz(temp_reg, codegen_->GetLabelOf(default_block)); - const ArenaVector<HBasicBlock*>& successors = switch_instr->GetBlock()->GetSuccessors(); - // Jump to successors[0] if value == lower_bound. - __ Beqz(temp_reg, codegen_->GetLabelOf(successors[0])); - int32_t last_index = 0; - for (; num_entries - last_index > 2; last_index += 2) { - __ Addiu(temp_reg, temp_reg, -2); - // Jump to successors[last_index + 1] if value < case_value[last_index + 2]. - __ Bltz(temp_reg, codegen_->GetLabelOf(successors[last_index + 1])); - // Jump to successors[last_index + 2] if value == case_value[last_index + 2]. - __ Beqz(temp_reg, codegen_->GetLabelOf(successors[last_index + 2])); - } - if (num_entries - last_index == 2) { - // The last missing case_value. - __ Addiu(temp_reg, temp_reg, -1); - __ Beqz(temp_reg, codegen_->GetLabelOf(successors[last_index + 1])); - } - - // And the default for any other value. + for (int32_t i = 0; i < num_entries; ++i) { + int32_t case_value = lower_bound + i; + MipsLabel* successor_label = codegen_->GetLabelOf(successors[i]); + if (case_value == 0) { + __ Beqz(value_reg, successor_label); + } else { + __ LoadConst32(TMP, case_value); + __ Beq(value_reg, TMP, successor_label); + } + } + + // Insert the default branch for every other value. if (!codegen_->GoesToNextBlock(switch_instr->GetBlock(), default_block)) { __ B(codegen_->GetLabelOf(default_block)); } diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc index 1a9de15c6f..fd18527c8c 100644 --- a/compiler/optimizing/code_generator_mips64.cc +++ b/compiler/optimizing/code_generator_mips64.cc @@ -3989,34 +3989,17 @@ void InstructionCodeGeneratorMIPS64::VisitPackedSwitch(HPackedSwitch* switch_ins GpuRegister value_reg = locations->InAt(0).AsRegister<GpuRegister>(); HBasicBlock* default_block = switch_instr->GetDefaultBlock(); - // Create a set of compare/jumps. - GpuRegister temp_reg = TMP; - if (IsInt<16>(-lower_bound)) { - __ Addiu(temp_reg, value_reg, -lower_bound); - } else { - __ LoadConst32(AT, -lower_bound); - __ Addu(temp_reg, value_reg, AT); - } - // Jump to default if index is negative - // Note: We don't check the case that index is positive while value < lower_bound, because in - // this case, index >= num_entries must be true. So that we can save one branch instruction. - __ Bltzc(temp_reg, codegen_->GetLabelOf(default_block)); - + // Create a series of compare/jumps. const ArenaVector<HBasicBlock*>& successors = switch_instr->GetBlock()->GetSuccessors(); - // Jump to successors[0] if value == lower_bound. - __ Beqzc(temp_reg, codegen_->GetLabelOf(successors[0])); - int32_t last_index = 0; - for (; num_entries - last_index > 2; last_index += 2) { - __ Addiu(temp_reg, temp_reg, -2); - // Jump to successors[last_index + 1] if value < case_value[last_index + 2]. - __ Bltzc(temp_reg, codegen_->GetLabelOf(successors[last_index + 1])); - // Jump to successors[last_index + 2] if value == case_value[last_index + 2]. - __ Beqzc(temp_reg, codegen_->GetLabelOf(successors[last_index + 2])); - } - if (num_entries - last_index == 2) { - // The last missing case_value. - __ Addiu(temp_reg, temp_reg, -1); - __ Beqzc(temp_reg, codegen_->GetLabelOf(successors[last_index + 1])); + for (int32_t i = 0; i < num_entries; i++) { + int32_t case_value = lower_bound + i; + Mips64Label* succ = codegen_->GetLabelOf(successors[i]); + if (case_value == 0) { + __ Beqzc(value_reg, succ); + } else { + __ LoadConst32(TMP, case_value); + __ Beqc(value_reg, TMP, succ); + } } // And the default for any other value. diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 469dd49a8e..bc3256ec8c 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -42,6 +42,7 @@ namespace x86 { static constexpr int kCurrentMethodStackOffset = 0; static constexpr Register kMethodRegisterArgument = EAX; + static constexpr Register kCoreCalleeSaves[] = { EBP, ESI, EDI }; static constexpr int kC2ConditionMask = 0x400; @@ -6751,65 +6752,29 @@ void LocationsBuilderX86::VisitPackedSwitch(HPackedSwitch* switch_instr) { locations->SetInAt(0, Location::RequiresRegister()); } -void InstructionCodeGeneratorX86::GenPackedSwitchWithCompares(Register value_reg, - int32_t lower_bound, - uint32_t num_entries, - HBasicBlock* switch_block, - HBasicBlock* default_block) { - // Figure out the correct compare values and jump conditions. - // Handle the first compare/branch as a special case because it might - // jump to the default case. - DCHECK_GT(num_entries, 2u); - Condition first_condition; - uint32_t index; - const ArenaVector<HBasicBlock*>& successors = switch_block->GetSuccessors(); - if (lower_bound != 0) { - first_condition = kLess; - __ cmpl(value_reg, Immediate(lower_bound)); - __ j(first_condition, codegen_->GetLabelOf(default_block)); - __ j(kEqual, codegen_->GetLabelOf(successors[0])); - - index = 1; - } else { - // Handle all the compare/jumps below. - first_condition = kBelow; - index = 0; - } - - // Handle the rest of the compare/jumps. - for (; index + 1 < num_entries; index += 2) { - int32_t compare_to_value = lower_bound + index + 1; - __ cmpl(value_reg, Immediate(compare_to_value)); - // Jump to successors[index] if value < case_value[index]. - __ j(first_condition, codegen_->GetLabelOf(successors[index])); - // Jump to successors[index + 1] if value == case_value[index + 1]. - __ j(kEqual, codegen_->GetLabelOf(successors[index + 1])); - } - - if (index != num_entries) { - // There are an odd number of entries. Handle the last one. - DCHECK_EQ(index + 1, num_entries); - __ cmpl(value_reg, Immediate(lower_bound + index)); - __ j(kEqual, codegen_->GetLabelOf(successors[index])); - } - - // And the default for any other value. - if (!codegen_->GoesToNextBlock(switch_block, default_block)) { - __ jmp(codegen_->GetLabelOf(default_block)); - } -} - void InstructionCodeGeneratorX86::VisitPackedSwitch(HPackedSwitch* switch_instr) { int32_t lower_bound = switch_instr->GetStartValue(); - uint32_t num_entries = switch_instr->GetNumEntries(); + int32_t num_entries = switch_instr->GetNumEntries(); LocationSummary* locations = switch_instr->GetLocations(); Register value_reg = locations->InAt(0).AsRegister<Register>(); + HBasicBlock* default_block = switch_instr->GetDefaultBlock(); + + // Create a series of compare/jumps. + const ArenaVector<HBasicBlock*>& successors = switch_instr->GetBlock()->GetSuccessors(); + for (int i = 0; i < num_entries; i++) { + int32_t case_value = lower_bound + i; + if (case_value == 0) { + __ testl(value_reg, value_reg); + } else { + __ cmpl(value_reg, Immediate(case_value)); + } + __ j(kEqual, codegen_->GetLabelOf(successors[i])); + } - GenPackedSwitchWithCompares(value_reg, - lower_bound, - num_entries, - switch_instr->GetBlock(), - switch_instr->GetDefaultBlock()); + // And the default for any other value. + if (!codegen_->GoesToNextBlock(switch_instr->GetBlock(), default_block)) { + __ jmp(codegen_->GetLabelOf(default_block)); + } } void LocationsBuilderX86::VisitX86PackedSwitch(HX86PackedSwitch* switch_instr) { @@ -6826,20 +6791,11 @@ void LocationsBuilderX86::VisitX86PackedSwitch(HX86PackedSwitch* switch_instr) { void InstructionCodeGeneratorX86::VisitX86PackedSwitch(HX86PackedSwitch* switch_instr) { int32_t lower_bound = switch_instr->GetStartValue(); - uint32_t num_entries = switch_instr->GetNumEntries(); + int32_t num_entries = switch_instr->GetNumEntries(); LocationSummary* locations = switch_instr->GetLocations(); Register value_reg = locations->InAt(0).AsRegister<Register>(); HBasicBlock* default_block = switch_instr->GetDefaultBlock(); - if (num_entries <= kPackedSwitchJumpTableThreshold) { - GenPackedSwitchWithCompares(value_reg, - lower_bound, - num_entries, - switch_instr->GetBlock(), - default_block); - return; - } - // Optimizing has a jump area. Register temp_reg = locations->GetTemp(0).AsRegister<Register>(); Register constant_area = locations->InAt(1).AsRegister<Register>(); @@ -6851,7 +6807,7 @@ void InstructionCodeGeneratorX86::VisitX86PackedSwitch(HX86PackedSwitch* switch_ } // Is the value in range? - DCHECK_GE(num_entries, 1u); + DCHECK_GE(num_entries, 1); __ cmpl(value_reg, Immediate(num_entries - 1)); __ j(kAbove, codegen_->GetLabelOf(default_block)); diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h index 712179920b..7c292fa103 100644 --- a/compiler/optimizing/code_generator_x86.h +++ b/compiler/optimizing/code_generator_x86.h @@ -195,11 +195,6 @@ class InstructionCodeGeneratorX86 : public HGraphVisitor { X86Assembler* GetAssembler() const { return assembler_; } - // The compare/jump sequence will generate about (1.5 * num_entries) instructions. A jump - // table version generates 7 instructions and num_entries literals. Compare/jump sequence will - // generates less code/data with a small num_entries. - static constexpr uint32_t kPackedSwitchJumpTableThreshold = 5; - private: // Generate code for the given suspend check. If not null, `successor` // is the block to branch to if the suspend check is not needed, and after @@ -274,11 +269,6 @@ class InstructionCodeGeneratorX86 : public HGraphVisitor { void GenerateFPJumps(HCondition* cond, Label* true_label, Label* false_label); void GenerateLongComparesAndJumps(HCondition* cond, Label* true_label, Label* false_label); void HandleGoto(HInstruction* got, HBasicBlock* successor); - void GenPackedSwitchWithCompares(Register value_reg, - int32_t lower_bound, - uint32_t num_entries, - HBasicBlock* switch_block, - HBasicBlock* default_block); X86Assembler* const assembler_; CodeGeneratorX86* const codegen_; diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index 2c5fbc78bf..92cef5f226 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -41,10 +41,6 @@ namespace x86_64 { static constexpr int kCurrentMethodStackOffset = 0; static constexpr Register kMethodRegisterArgument = RDI; -// The compare/jump sequence will generate about (1.5 * num_entries) instructions. A jump -// table version generates 7 instructions and num_entries literals. Compare/jump sequence will -// generates less code/data with a small num_entries. -static constexpr uint32_t kPackedSwitchJumpTableThreshold = 5; static constexpr Register kCoreCalleeSaves[] = { RBX, RBP, R12, R13, R14, R15 }; static constexpr FloatRegister kFpuCalleeSaves[] = { XMM12, XMM13, XMM14, XMM15 }; @@ -6335,58 +6331,11 @@ void LocationsBuilderX86_64::VisitPackedSwitch(HPackedSwitch* switch_instr) { void InstructionCodeGeneratorX86_64::VisitPackedSwitch(HPackedSwitch* switch_instr) { int32_t lower_bound = switch_instr->GetStartValue(); - uint32_t num_entries = switch_instr->GetNumEntries(); + int32_t num_entries = switch_instr->GetNumEntries(); LocationSummary* locations = switch_instr->GetLocations(); CpuRegister value_reg_in = locations->InAt(0).AsRegister<CpuRegister>(); CpuRegister temp_reg = locations->GetTemp(0).AsRegister<CpuRegister>(); CpuRegister base_reg = locations->GetTemp(1).AsRegister<CpuRegister>(); - HBasicBlock* default_block = switch_instr->GetDefaultBlock(); - - // Should we generate smaller inline compare/jumps? - if (num_entries <= kPackedSwitchJumpTableThreshold) { - // Figure out the correct compare values and jump conditions. - // Handle the first compare/branch as a special case because it might - // jump to the default case. - DCHECK_GT(num_entries, 2u); - Condition first_condition; - uint32_t index; - const ArenaVector<HBasicBlock*>& successors = switch_instr->GetBlock()->GetSuccessors(); - if (lower_bound != 0) { - first_condition = kLess; - __ cmpl(value_reg_in, Immediate(lower_bound)); - __ j(first_condition, codegen_->GetLabelOf(default_block)); - __ j(kEqual, codegen_->GetLabelOf(successors[0])); - - index = 1; - } else { - // Handle all the compare/jumps below. - first_condition = kBelow; - index = 0; - } - - // Handle the rest of the compare/jumps. - for (; index + 1 < num_entries; index += 2) { - int32_t compare_to_value = lower_bound + index + 1; - __ cmpl(value_reg_in, Immediate(compare_to_value)); - // Jump to successors[index] if value < case_value[index]. - __ j(first_condition, codegen_->GetLabelOf(successors[index])); - // Jump to successors[index + 1] if value == case_value[index + 1]. - __ j(kEqual, codegen_->GetLabelOf(successors[index + 1])); - } - - if (index != num_entries) { - // There are an odd number of entries. Handle the last one. - DCHECK_EQ(index + 1, num_entries); - __ cmpl(value_reg_in, Immediate(lower_bound + index)); - __ j(kEqual, codegen_->GetLabelOf(successors[index])); - } - - // And the default for any other value. - if (!codegen_->GoesToNextBlock(switch_instr->GetBlock(), default_block)) { - __ jmp(codegen_->GetLabelOf(default_block)); - } - return; - } // Remove the bias, if needed. Register value_reg_out = value_reg_in.AsRegister(); @@ -6397,6 +6346,7 @@ void InstructionCodeGeneratorX86_64::VisitPackedSwitch(HPackedSwitch* switch_ins CpuRegister value_reg(value_reg_out); // Is the value in range? + HBasicBlock* default_block = switch_instr->GetDefaultBlock(); __ cmpl(value_reg, Immediate(num_entries - 1)); __ j(kAbove, codegen_->GetLabelOf(default_block)); diff --git a/compiler/optimizing/codegen_test.cc b/compiler/optimizing/codegen_test.cc index 57de41f557..d970704368 100644 --- a/compiler/optimizing/codegen_test.cc +++ b/compiler/optimizing/codegen_test.cc @@ -35,6 +35,7 @@ #include "code_generator_mips64.h" #include "code_generator_x86.h" #include "code_generator_x86_64.h" +#include "code_simulator_container.h" #include "common_compiler_test.h" #include "dex_file.h" #include "dex_instruction.h" @@ -124,26 +125,85 @@ class InternalCodeAllocator : public CodeAllocator { DISALLOW_COPY_AND_ASSIGN(InternalCodeAllocator); }; +static bool CanExecuteOnHardware(InstructionSet target_isa) { + return (target_isa == kRuntimeISA) + // Handle the special case of ARM, with two instructions sets (ARM32 and Thumb-2). + || (kRuntimeISA == kArm && target_isa == kThumb2); +} + +static bool CanExecute(InstructionSet target_isa) { + CodeSimulatorContainer simulator(target_isa); + return CanExecuteOnHardware(target_isa) || simulator.CanSimulate(); +} + +template <typename Expected> +static Expected SimulatorExecute(CodeSimulator* simulator, Expected (*f)()); + +template <> +bool SimulatorExecute<bool>(CodeSimulator* simulator, bool (*f)()) { + simulator->RunFrom(reinterpret_cast<intptr_t>(f)); + return simulator->GetCReturnBool(); +} + +template <> +int32_t SimulatorExecute<int32_t>(CodeSimulator* simulator, int32_t (*f)()) { + simulator->RunFrom(reinterpret_cast<intptr_t>(f)); + return simulator->GetCReturnInt32(); +} + +template <> +int64_t SimulatorExecute<int64_t>(CodeSimulator* simulator, int64_t (*f)()) { + simulator->RunFrom(reinterpret_cast<intptr_t>(f)); + return simulator->GetCReturnInt64(); +} + +template <typename Expected> +static void VerifyGeneratedCode(InstructionSet target_isa, + Expected (*f)(), + bool has_result, + Expected expected) { + ASSERT_TRUE(CanExecute(target_isa)) << "Target isa is not executable."; + + // Verify on simulator. + CodeSimulatorContainer simulator(target_isa); + if (simulator.CanSimulate()) { + Expected result = SimulatorExecute<Expected>(simulator.Get(), f); + if (has_result) { + ASSERT_EQ(expected, result); + } + } + + // Verify on hardware. + if (CanExecuteOnHardware(target_isa)) { + Expected result = f(); + if (has_result) { + ASSERT_EQ(expected, result); + } + } +} + template <typename Expected> static void Run(const InternalCodeAllocator& allocator, const CodeGenerator& codegen, bool has_result, Expected expected) { + InstructionSet target_isa = codegen.GetInstructionSet(); + typedef Expected (*fptr)(); CommonCompilerTest::MakeExecutable(allocator.GetMemory(), allocator.GetSize()); fptr f = reinterpret_cast<fptr>(allocator.GetMemory()); - if (codegen.GetInstructionSet() == kThumb2) { + if (target_isa == kThumb2) { // For thumb we need the bottom bit set. f = reinterpret_cast<fptr>(reinterpret_cast<uintptr_t>(f) + 1); } - Expected result = f(); - if (has_result) { - ASSERT_EQ(expected, result); - } + VerifyGeneratedCode(target_isa, f, has_result, expected); } template <typename Expected> -static void RunCodeBaseline(HGraph* graph, bool has_result, Expected expected) { +static void RunCodeBaseline(InstructionSet target_isa, + HGraph* graph, + bool has_result, + Expected expected) { InternalCodeAllocator allocator; CompilerOptions compiler_options; @@ -153,7 +213,7 @@ static void RunCodeBaseline(HGraph* graph, bool has_result, Expected expected) { // 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); - if (kRuntimeISA == kX86) { + if (target_isa == kX86) { Run(allocator, codegenX86, has_result, expected); } @@ -161,7 +221,7 @@ static void RunCodeBaseline(HGraph* graph, bool has_result, Expected expected) { ArmInstructionSetFeatures::FromCppDefines()); TestCodeGeneratorARM codegenARM(graph, *features_arm.get(), compiler_options); codegenARM.CompileBaseline(&allocator, true); - if (kRuntimeISA == kArm || kRuntimeISA == kThumb2) { + if (target_isa == kArm || target_isa == kThumb2) { Run(allocator, codegenARM, has_result, expected); } @@ -169,7 +229,7 @@ static void RunCodeBaseline(HGraph* graph, bool has_result, Expected expected) { X86_64InstructionSetFeatures::FromCppDefines()); x86_64::CodeGeneratorX86_64 codegenX86_64(graph, *features_x86_64.get(), compiler_options); codegenX86_64.CompileBaseline(&allocator, true); - if (kRuntimeISA == kX86_64) { + if (target_isa == kX86_64) { Run(allocator, codegenX86_64, has_result, expected); } @@ -177,7 +237,7 @@ static void RunCodeBaseline(HGraph* graph, bool has_result, Expected expected) { Arm64InstructionSetFeatures::FromCppDefines()); arm64::CodeGeneratorARM64 codegenARM64(graph, *features_arm64.get(), compiler_options); codegenARM64.CompileBaseline(&allocator, true); - if (kRuntimeISA == kArm64) { + if (target_isa == kArm64) { Run(allocator, codegenARM64, has_result, expected); } @@ -193,7 +253,7 @@ static void RunCodeBaseline(HGraph* graph, bool has_result, Expected expected) { Mips64InstructionSetFeatures::FromCppDefines()); mips64::CodeGeneratorMIPS64 codegenMIPS64(graph, *features_mips64.get(), compiler_options); codegenMIPS64.CompileBaseline(&allocator, true); - if (kRuntimeISA == kMips64) { + if (target_isa == kMips64) { Run(allocator, codegenMIPS64, has_result, expected); } } @@ -221,37 +281,38 @@ static void RunCodeOptimized(CodeGenerator* codegen, } template <typename Expected> -static void RunCodeOptimized(HGraph* graph, +static void RunCodeOptimized(InstructionSet target_isa, + HGraph* graph, std::function<void(HGraph*)> hook_before_codegen, bool has_result, Expected expected) { CompilerOptions compiler_options; - if (kRuntimeISA == kArm || kRuntimeISA == kThumb2) { - TestCodeGeneratorARM codegenARM(graph, - *ArmInstructionSetFeatures::FromCppDefines(), - compiler_options); + if (target_isa == kArm || target_isa == kThumb2) { + std::unique_ptr<const ArmInstructionSetFeatures> features_arm( + ArmInstructionSetFeatures::FromCppDefines()); + TestCodeGeneratorARM codegenARM(graph, *features_arm.get(), compiler_options); RunCodeOptimized(&codegenARM, graph, hook_before_codegen, has_result, expected); - } else if (kRuntimeISA == kArm64) { - arm64::CodeGeneratorARM64 codegenARM64(graph, - *Arm64InstructionSetFeatures::FromCppDefines(), - compiler_options); + } else if (target_isa == kArm64) { + std::unique_ptr<const Arm64InstructionSetFeatures> features_arm64( + Arm64InstructionSetFeatures::FromCppDefines()); + arm64::CodeGeneratorARM64 codegenARM64(graph, *features_arm64.get(), compiler_options); RunCodeOptimized(&codegenARM64, graph, hook_before_codegen, has_result, expected); - } else if (kRuntimeISA == kX86) { + } else if (target_isa == kX86) { std::unique_ptr<const X86InstructionSetFeatures> features_x86( X86InstructionSetFeatures::FromCppDefines()); x86::CodeGeneratorX86 codegenX86(graph, *features_x86.get(), compiler_options); RunCodeOptimized(&codegenX86, graph, hook_before_codegen, has_result, expected); - } else if (kRuntimeISA == kX86_64) { + } else if (target_isa == kX86_64) { std::unique_ptr<const X86_64InstructionSetFeatures> features_x86_64( X86_64InstructionSetFeatures::FromCppDefines()); x86_64::CodeGeneratorX86_64 codegenX86_64(graph, *features_x86_64.get(), compiler_options); RunCodeOptimized(&codegenX86_64, graph, hook_before_codegen, has_result, expected); - } else if (kRuntimeISA == kMips) { + } else if (target_isa == kMips) { std::unique_ptr<const MipsInstructionSetFeatures> features_mips( MipsInstructionSetFeatures::FromCppDefines()); mips::CodeGeneratorMIPS codegenMIPS(graph, *features_mips.get(), compiler_options); RunCodeOptimized(&codegenMIPS, graph, hook_before_codegen, has_result, expected); - } else if (kRuntimeISA == kMips64) { + } else if (target_isa == kMips64) { std::unique_ptr<const Mips64InstructionSetFeatures> features_mips64( Mips64InstructionSetFeatures::FromCppDefines()); mips64::CodeGeneratorMIPS64 codegenMIPS64(graph, *features_mips64.get(), compiler_options); @@ -259,7 +320,10 @@ static void RunCodeOptimized(HGraph* graph, } } -static void TestCode(const uint16_t* data, bool has_result = false, int32_t expected = 0) { +static void TestCode(InstructionSet target_isa, + const uint16_t* data, + bool has_result = false, + int32_t expected = 0) { ArenaPool pool; ArenaAllocator arena(&pool); HGraph* graph = CreateGraph(&arena); @@ -269,10 +333,13 @@ static void TestCode(const uint16_t* data, bool has_result = false, int32_t expe ASSERT_TRUE(graph_built); // Remove suspend checks, they cannot be executed in this context. RemoveSuspendChecks(graph); - RunCodeBaseline(graph, has_result, expected); + RunCodeBaseline(target_isa, graph, has_result, expected); } -static void TestCodeLong(const uint16_t* data, bool has_result, int64_t expected) { +static void TestCodeLong(InstructionSet target_isa, + const uint16_t* data, + bool has_result, + int64_t expected) { ArenaPool pool; ArenaAllocator arena(&pool); HGraph* graph = CreateGraph(&arena); @@ -282,108 +349,110 @@ static void TestCodeLong(const uint16_t* data, bool has_result, int64_t expected ASSERT_TRUE(graph_built); // Remove suspend checks, they cannot be executed in this context. RemoveSuspendChecks(graph); - RunCodeBaseline(graph, has_result, expected); + RunCodeBaseline(target_isa, graph, has_result, expected); } -TEST(CodegenTest, ReturnVoid) { +class CodegenTest: public ::testing::TestWithParam<InstructionSet> {}; + +TEST_P(CodegenTest, ReturnVoid) { const uint16_t data[] = ZERO_REGISTER_CODE_ITEM(Instruction::RETURN_VOID); - TestCode(data); + TestCode(GetParam(), data); } -TEST(CodegenTest, CFG1) { +TEST_P(CodegenTest, CFG1) { const uint16_t data[] = ZERO_REGISTER_CODE_ITEM( Instruction::GOTO | 0x100, Instruction::RETURN_VOID); - TestCode(data); + TestCode(GetParam(), data); } -TEST(CodegenTest, CFG2) { +TEST_P(CodegenTest, CFG2) { const uint16_t data[] = ZERO_REGISTER_CODE_ITEM( Instruction::GOTO | 0x100, Instruction::GOTO | 0x100, Instruction::RETURN_VOID); - TestCode(data); + TestCode(GetParam(), data); } -TEST(CodegenTest, CFG3) { +TEST_P(CodegenTest, CFG3) { const uint16_t data1[] = ZERO_REGISTER_CODE_ITEM( Instruction::GOTO | 0x200, Instruction::RETURN_VOID, Instruction::GOTO | 0xFF00); - TestCode(data1); + TestCode(GetParam(), data1); const uint16_t data2[] = ZERO_REGISTER_CODE_ITEM( Instruction::GOTO_16, 3, Instruction::RETURN_VOID, Instruction::GOTO_16, 0xFFFF); - TestCode(data2); + TestCode(GetParam(), data2); const uint16_t data3[] = ZERO_REGISTER_CODE_ITEM( Instruction::GOTO_32, 4, 0, Instruction::RETURN_VOID, Instruction::GOTO_32, 0xFFFF, 0xFFFF); - TestCode(data3); + TestCode(GetParam(), data3); } -TEST(CodegenTest, CFG4) { +TEST_P(CodegenTest, CFG4) { const uint16_t data[] = ZERO_REGISTER_CODE_ITEM( Instruction::RETURN_VOID, Instruction::GOTO | 0x100, Instruction::GOTO | 0xFE00); - TestCode(data); + TestCode(GetParam(), data); } -TEST(CodegenTest, CFG5) { +TEST_P(CodegenTest, CFG5) { const uint16_t data[] = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 3, Instruction::GOTO | 0x100, Instruction::RETURN_VOID); - TestCode(data); + TestCode(GetParam(), data); } -TEST(CodegenTest, IntConstant) { +TEST_P(CodegenTest, IntConstant) { const uint16_t data[] = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::RETURN_VOID); - TestCode(data); + TestCode(GetParam(), data); } -TEST(CodegenTest, Return1) { +TEST_P(CodegenTest, Return1) { const uint16_t data[] = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::RETURN | 0); - TestCode(data, true, 0); + TestCode(GetParam(), data, true, 0); } -TEST(CodegenTest, Return2) { +TEST_P(CodegenTest, Return2) { const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::CONST_4 | 0 | 1 << 8, Instruction::RETURN | 1 << 8); - TestCode(data, true, 0); + TestCode(GetParam(), data, true, 0); } -TEST(CodegenTest, Return3) { +TEST_P(CodegenTest, Return3) { const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::CONST_4 | 1 << 8 | 1 << 12, Instruction::RETURN | 1 << 8); - TestCode(data, true, 1); + TestCode(GetParam(), data, true, 1); } -TEST(CodegenTest, ReturnIf1) { +TEST_P(CodegenTest, ReturnIf1) { const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::CONST_4 | 1 << 8 | 1 << 12, @@ -391,10 +460,10 @@ TEST(CodegenTest, ReturnIf1) { Instruction::RETURN | 0 << 8, Instruction::RETURN | 1 << 8); - TestCode(data, true, 1); + TestCode(GetParam(), data, true, 1); } -TEST(CodegenTest, ReturnIf2) { +TEST_P(CodegenTest, ReturnIf2) { const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::CONST_4 | 1 << 8 | 1 << 12, @@ -402,12 +471,12 @@ TEST(CodegenTest, ReturnIf2) { Instruction::RETURN | 0 << 8, Instruction::RETURN | 1 << 8); - TestCode(data, true, 0); + TestCode(GetParam(), data, true, 0); } // Exercise bit-wise (one's complement) not-int instruction. #define NOT_INT_TEST(TEST_NAME, INPUT, EXPECTED_OUTPUT) \ -TEST(CodegenTest, TEST_NAME) { \ +TEST_P(CodegenTest, TEST_NAME) { \ const int32_t input = INPUT; \ const uint16_t input_lo = Low16Bits(input); \ const uint16_t input_hi = High16Bits(input); \ @@ -416,7 +485,7 @@ TEST(CodegenTest, TEST_NAME) { \ Instruction::NOT_INT | 1 << 8 | 0 << 12 , \ Instruction::RETURN | 1 << 8); \ \ - TestCode(data, true, EXPECTED_OUTPUT); \ + TestCode(GetParam(), data, true, EXPECTED_OUTPUT); \ } NOT_INT_TEST(ReturnNotIntMinus2, -2, 1) @@ -432,7 +501,7 @@ NOT_INT_TEST(ReturnNotIntINT32_MAX, 2147483647, -2147483648) // -(2^31) // Exercise bit-wise (one's complement) not-long instruction. #define NOT_LONG_TEST(TEST_NAME, INPUT, EXPECTED_OUTPUT) \ -TEST(CodegenTest, TEST_NAME) { \ +TEST_P(CodegenTest, TEST_NAME) { \ const int64_t input = INPUT; \ const uint16_t word0 = Low16Bits(Low32Bits(input)); /* LSW. */ \ const uint16_t word1 = High16Bits(Low32Bits(input)); \ @@ -443,7 +512,7 @@ TEST(CodegenTest, TEST_NAME) { \ Instruction::NOT_LONG | 2 << 8 | 0 << 12, \ Instruction::RETURN_WIDE | 2 << 8); \ \ - TestCodeLong(data, true, EXPECTED_OUTPUT); \ + TestCodeLong(GetParam(), data, true, EXPECTED_OUTPUT); \ } NOT_LONG_TEST(ReturnNotLongMinus2, INT64_C(-2), INT64_C(1)) @@ -482,7 +551,7 @@ NOT_LONG_TEST(ReturnNotLongINT64_MAX, #undef NOT_LONG_TEST -TEST(CodegenTest, IntToLongOfLongToInt) { +TEST_P(CodegenTest, IntToLongOfLongToInt) { const int64_t input = INT64_C(4294967296); // 2^32 const uint16_t word0 = Low16Bits(Low32Bits(input)); // LSW. const uint16_t word1 = High16Bits(Low32Bits(input)); @@ -496,48 +565,48 @@ TEST(CodegenTest, IntToLongOfLongToInt) { Instruction::INT_TO_LONG | 2 << 8 | 4 << 12, Instruction::RETURN_WIDE | 2 << 8); - TestCodeLong(data, true, 1); + TestCodeLong(GetParam(), data, true, 1); } -TEST(CodegenTest, ReturnAdd1) { +TEST_P(CodegenTest, ReturnAdd1) { const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 3 << 12 | 0, Instruction::CONST_4 | 4 << 12 | 1 << 8, Instruction::ADD_INT, 1 << 8 | 0, Instruction::RETURN); - TestCode(data, true, 7); + TestCode(GetParam(), data, true, 7); } -TEST(CodegenTest, ReturnAdd2) { +TEST_P(CodegenTest, ReturnAdd2) { const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 3 << 12 | 0, Instruction::CONST_4 | 4 << 12 | 1 << 8, Instruction::ADD_INT_2ADDR | 1 << 12, Instruction::RETURN); - TestCode(data, true, 7); + TestCode(GetParam(), data, true, 7); } -TEST(CodegenTest, ReturnAdd3) { +TEST_P(CodegenTest, ReturnAdd3) { const uint16_t data[] = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 4 << 12 | 0 << 8, Instruction::ADD_INT_LIT8, 3 << 8 | 0, Instruction::RETURN); - TestCode(data, true, 7); + TestCode(GetParam(), data, true, 7); } -TEST(CodegenTest, ReturnAdd4) { +TEST_P(CodegenTest, ReturnAdd4) { const uint16_t data[] = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 4 << 12 | 0 << 8, Instruction::ADD_INT_LIT16, 3, Instruction::RETURN); - TestCode(data, true, 7); + TestCode(GetParam(), data, true, 7); } -TEST(CodegenTest, NonMaterializedCondition) { +TEST_P(CodegenTest, NonMaterializedCondition) { ArenaPool pool; ArenaAllocator allocator(&pool); @@ -583,30 +652,30 @@ TEST(CodegenTest, NonMaterializedCondition) { block->InsertInstructionBefore(move, block->GetLastInstruction()); }; - RunCodeOptimized(graph, hook_before_codegen, true, 0); + RunCodeOptimized(GetParam(), graph, hook_before_codegen, true, 0); } -TEST(CodegenTest, ReturnMulInt) { +TEST_P(CodegenTest, ReturnMulInt) { const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 3 << 12 | 0, Instruction::CONST_4 | 4 << 12 | 1 << 8, Instruction::MUL_INT, 1 << 8 | 0, Instruction::RETURN); - TestCode(data, true, 12); + TestCode(GetParam(), data, true, 12); } -TEST(CodegenTest, ReturnMulInt2addr) { +TEST_P(CodegenTest, ReturnMulInt2addr) { const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 3 << 12 | 0, Instruction::CONST_4 | 4 << 12 | 1 << 8, Instruction::MUL_INT_2ADDR | 1 << 12, Instruction::RETURN); - TestCode(data, true, 12); + TestCode(GetParam(), data, true, 12); } -TEST(CodegenTest, ReturnMulLong) { +TEST_P(CodegenTest, ReturnMulLong) { const uint16_t data[] = FOUR_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 3 << 12 | 0, Instruction::CONST_4 | 0 << 12 | 1 << 8, @@ -615,10 +684,10 @@ TEST(CodegenTest, ReturnMulLong) { Instruction::MUL_LONG, 2 << 8 | 0, Instruction::RETURN_WIDE); - TestCodeLong(data, true, 12); + TestCodeLong(GetParam(), data, true, 12); } -TEST(CodegenTest, ReturnMulLong2addr) { +TEST_P(CodegenTest, ReturnMulLong2addr) { const uint16_t data[] = FOUR_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 3 << 12 | 0 << 8, Instruction::CONST_4 | 0 << 12 | 1 << 8, @@ -627,28 +696,28 @@ TEST(CodegenTest, ReturnMulLong2addr) { Instruction::MUL_LONG_2ADDR | 2 << 12, Instruction::RETURN_WIDE); - TestCodeLong(data, true, 12); + TestCodeLong(GetParam(), data, true, 12); } -TEST(CodegenTest, ReturnMulIntLit8) { +TEST_P(CodegenTest, ReturnMulIntLit8) { const uint16_t data[] = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 4 << 12 | 0 << 8, Instruction::MUL_INT_LIT8, 3 << 8 | 0, Instruction::RETURN); - TestCode(data, true, 12); + TestCode(GetParam(), data, true, 12); } -TEST(CodegenTest, ReturnMulIntLit16) { +TEST_P(CodegenTest, ReturnMulIntLit16) { const uint16_t data[] = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 4 << 12 | 0 << 8, Instruction::MUL_INT_LIT16, 3, Instruction::RETURN); - TestCode(data, true, 12); + TestCode(GetParam(), data, true, 12); } -TEST(CodegenTest, MaterializedCondition1) { +TEST_P(CodegenTest, MaterializedCondition1) { // Check that condition are materialized correctly. A materialized condition // should yield `1` if it evaluated to true, and `0` otherwise. // We force the materialization of comparisons for different combinations of @@ -689,11 +758,11 @@ TEST(CodegenTest, MaterializedCondition1) { block->InsertInstructionBefore(move, block->GetLastInstruction()); }; - RunCodeOptimized(graph, hook_before_codegen, true, lhs[i] < rhs[i]); + RunCodeOptimized(GetParam(), graph, hook_before_codegen, true, lhs[i] < rhs[i]); } } -TEST(CodegenTest, MaterializedCondition2) { +TEST_P(CodegenTest, MaterializedCondition2) { // Check that HIf correctly interprets a materialized condition. // We force the materialization of comparisons for different combinations of // inputs. An HIf takes the materialized combination as input and returns a @@ -755,31 +824,35 @@ TEST(CodegenTest, MaterializedCondition2) { block->InsertInstructionBefore(move, block->GetLastInstruction()); }; - RunCodeOptimized(graph, hook_before_codegen, true, lhs[i] < rhs[i]); + RunCodeOptimized(GetParam(), graph, hook_before_codegen, true, lhs[i] < rhs[i]); } } -TEST(CodegenTest, ReturnDivIntLit8) { +TEST_P(CodegenTest, ReturnDivIntLit8) { const uint16_t data[] = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 4 << 12 | 0 << 8, Instruction::DIV_INT_LIT8, 3 << 8 | 0, Instruction::RETURN); - TestCode(data, true, 1); + TestCode(GetParam(), data, true, 1); } -TEST(CodegenTest, ReturnDivInt2Addr) { +TEST_P(CodegenTest, ReturnDivInt2Addr) { const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 4 << 12 | 0, Instruction::CONST_4 | 2 << 12 | 1 << 8, Instruction::DIV_INT_2ADDR | 1 << 12, Instruction::RETURN); - TestCode(data, true, 2); + TestCode(GetParam(), data, true, 2); } // Helper method. -static void TestComparison(IfCondition condition, int64_t i, int64_t j, Primitive::Type type) { +static void TestComparison(IfCondition condition, + int64_t i, + int64_t j, + Primitive::Type type, + const InstructionSet target_isa) { ArenaPool pool; ArenaAllocator allocator(&pool); HGraph* graph = CreateGraph(&allocator); @@ -862,46 +935,78 @@ static void TestComparison(IfCondition condition, int64_t i, int64_t j, Primitiv auto hook_before_codegen = [](HGraph*) { }; - RunCodeOptimized(graph, hook_before_codegen, true, expected_result); + RunCodeOptimized(target_isa, graph, hook_before_codegen, true, expected_result); } -TEST(CodegenTest, ComparisonsInt) { +TEST_P(CodegenTest, ComparisonsInt) { + const InstructionSet target_isa = GetParam(); for (int64_t i = -1; i <= 1; i++) { for (int64_t j = -1; j <= 1; j++) { - TestComparison(kCondEQ, i, j, Primitive::kPrimInt); - TestComparison(kCondNE, i, j, Primitive::kPrimInt); - TestComparison(kCondLT, i, j, Primitive::kPrimInt); - TestComparison(kCondLE, i, j, Primitive::kPrimInt); - TestComparison(kCondGT, i, j, Primitive::kPrimInt); - TestComparison(kCondGE, i, j, Primitive::kPrimInt); - TestComparison(kCondB, i, j, Primitive::kPrimInt); - TestComparison(kCondBE, i, j, Primitive::kPrimInt); - TestComparison(kCondA, i, j, Primitive::kPrimInt); - TestComparison(kCondAE, i, j, Primitive::kPrimInt); + TestComparison(kCondEQ, i, j, Primitive::kPrimInt, target_isa); + TestComparison(kCondNE, i, j, Primitive::kPrimInt, target_isa); + TestComparison(kCondLT, i, j, Primitive::kPrimInt, target_isa); + TestComparison(kCondLE, i, j, Primitive::kPrimInt, target_isa); + TestComparison(kCondGT, i, j, Primitive::kPrimInt, target_isa); + TestComparison(kCondGE, i, j, Primitive::kPrimInt, target_isa); + TestComparison(kCondB, i, j, Primitive::kPrimInt, target_isa); + TestComparison(kCondBE, i, j, Primitive::kPrimInt, target_isa); + TestComparison(kCondA, i, j, Primitive::kPrimInt, target_isa); + TestComparison(kCondAE, i, j, Primitive::kPrimInt, target_isa); } } } -TEST(CodegenTest, ComparisonsLong) { +TEST_P(CodegenTest, ComparisonsLong) { // TODO: make MIPS work for long if (kRuntimeISA == kMips || kRuntimeISA == kMips64) { return; } + const InstructionSet target_isa = GetParam(); + if (target_isa == kMips || target_isa == kMips64) { + return; + } + for (int64_t i = -1; i <= 1; i++) { for (int64_t j = -1; j <= 1; j++) { - TestComparison(kCondEQ, i, j, Primitive::kPrimLong); - TestComparison(kCondNE, i, j, Primitive::kPrimLong); - TestComparison(kCondLT, i, j, Primitive::kPrimLong); - TestComparison(kCondLE, i, j, Primitive::kPrimLong); - TestComparison(kCondGT, i, j, Primitive::kPrimLong); - TestComparison(kCondGE, i, j, Primitive::kPrimLong); - TestComparison(kCondB, i, j, Primitive::kPrimLong); - TestComparison(kCondBE, i, j, Primitive::kPrimLong); - TestComparison(kCondA, i, j, Primitive::kPrimLong); - TestComparison(kCondAE, i, j, Primitive::kPrimLong); + TestComparison(kCondEQ, i, j, Primitive::kPrimLong, target_isa); + TestComparison(kCondNE, i, j, Primitive::kPrimLong, target_isa); + TestComparison(kCondLT, i, j, Primitive::kPrimLong, target_isa); + TestComparison(kCondLE, i, j, Primitive::kPrimLong, target_isa); + TestComparison(kCondGT, i, j, Primitive::kPrimLong, target_isa); + TestComparison(kCondGE, i, j, Primitive::kPrimLong, target_isa); + TestComparison(kCondB, i, j, Primitive::kPrimLong, target_isa); + TestComparison(kCondBE, i, j, Primitive::kPrimLong, target_isa); + TestComparison(kCondA, i, j, Primitive::kPrimLong, target_isa); + TestComparison(kCondAE, i, j, Primitive::kPrimLong, target_isa); } } } +static ::std::vector<InstructionSet> GetTargetISAs() { + ::std::vector<InstructionSet> v; + // Add all ISAs that are executable on hardware or on simulator. + const ::std::vector<InstructionSet> executable_isa_candidates = { + kArm, + kArm64, + kThumb2, + kX86, + kX86_64, + kMips, + kMips64 + }; + + for (auto target_isa : executable_isa_candidates) { + if (CanExecute(target_isa)) { + v.push_back(target_isa); + } + } + + return v; +} + +INSTANTIATE_TEST_CASE_P(MultipleTargets, + CodegenTest, + ::testing::ValuesIn(GetTargetISAs())); + } // namespace art diff --git a/compiler/optimizing/constant_folding_test.cc b/compiler/optimizing/constant_folding_test.cc index a8f65bf516..e469c8d6d0 100644 --- a/compiler/optimizing/constant_folding_test.cc +++ b/compiler/optimizing/constant_folding_test.cc @@ -32,7 +32,7 @@ namespace art { /** * Fixture class for the constant folding and dce tests. */ -class ConstantFoldingTest : public CommonCompilerTest { +class ConstantFoldingTest : public testing::Test { public: ConstantFoldingTest() : pool_(), allocator_(&pool_) { graph_ = CreateGraph(&allocator_); @@ -56,7 +56,7 @@ class ConstantFoldingTest : public CommonCompilerTest { const std::string& expected_after_dce, std::function<void(HGraph*)> check_after_cf) { ASSERT_NE(graph_, nullptr); - TransformToSsa(graph_); + graph_->TryBuildingSsa(); StringPrettyPrinter printer_before(graph_); printer_before.VisitInsertionOrder(); diff --git a/compiler/optimizing/dead_code_elimination_test.cc b/compiler/optimizing/dead_code_elimination_test.cc index f0f98efadb..2c6a1ef63d 100644 --- a/compiler/optimizing/dead_code_elimination_test.cc +++ b/compiler/optimizing/dead_code_elimination_test.cc @@ -26,8 +26,6 @@ namespace art { -class DeadCodeEliminationTest : public CommonCompilerTest {}; - static void TestCode(const uint16_t* data, const std::string& expected_before, const std::string& expected_after) { @@ -36,7 +34,7 @@ static void TestCode(const uint16_t* data, HGraph* graph = CreateCFG(&allocator, data); ASSERT_NE(graph, nullptr); - TransformToSsa(graph); + graph->TryBuildingSsa(); StringPrettyPrinter printer_before(graph); printer_before.VisitInsertionOrder(); @@ -57,6 +55,7 @@ static void TestCode(const uint16_t* data, ASSERT_EQ(actual_after, expected_after); } + /** * Small three-register program. * @@ -70,7 +69,7 @@ static void TestCode(const uint16_t* data, * L1: v2 <- v0 + v1 5. add-int v2, v0, v1 * return-void 7. return */ -TEST_F(DeadCodeEliminationTest, AdditionAndConditionalJump) { +TEST(DeadCodeElimination, AdditionAndConditionalJump) { const uint16_t data[] = THREE_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 1 << 8 | 1 << 12, Instruction::CONST_4 | 0 << 8 | 0 << 12, @@ -132,7 +131,7 @@ TEST_F(DeadCodeEliminationTest, AdditionAndConditionalJump) { * L3: v2 <- v1 + 4 11. add-int/lit16 v2, v1, #+4 * return 13. return-void */ -TEST_F(DeadCodeEliminationTest, AdditionsAndInconditionalJumps) { +TEST(DeadCodeElimination, AdditionsAndInconditionalJumps) { const uint16_t data[] = THREE_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 0 << 8 | 0 << 12, Instruction::CONST_4 | 1 << 8 | 1 << 12, diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc index f3c1dbe3f5..dfc363f9fd 100644 --- a/compiler/optimizing/graph_checker.cc +++ b/compiler/optimizing/graph_checker.cc @@ -24,7 +24,6 @@ #include "base/arena_containers.h" #include "base/bit_vector-inl.h" #include "base/stringprintf.h" -#include "handle_scope-inl.h" namespace art { @@ -595,17 +594,6 @@ void SSAChecker::VisitInstruction(HInstruction* instruction) { } } } - - // Ensure that reference type instructions have reference type info. - if (instruction->GetType() == Primitive::kPrimNot) { - ScopedObjectAccess soa(Thread::Current()); - if (!instruction->GetReferenceTypeInfo().IsValid()) { - AddError(StringPrintf("Reference type instruction %s:%d does not have " - "valid reference type information.", - instruction->DebugName(), - instruction->GetId())); - } - } } static Primitive::Type PrimitiveKind(Primitive::Type type) { diff --git a/compiler/optimizing/graph_checker_test.cc b/compiler/optimizing/graph_checker_test.cc index d10df4ce3f..fee56c7f9e 100644 --- a/compiler/optimizing/graph_checker_test.cc +++ b/compiler/optimizing/graph_checker_test.cc @@ -17,6 +17,8 @@ #include "graph_checker.h" #include "optimizing_unit_test.h" +#include "gtest/gtest.h" + namespace art { /** @@ -41,6 +43,7 @@ HGraph* CreateSimpleCFG(ArenaAllocator* allocator) { return graph; } + static void TestCode(const uint16_t* data) { ArenaPool pool; ArenaAllocator allocator(&pool); @@ -58,7 +61,8 @@ static void TestCodeSSA(const uint16_t* data) { HGraph* graph = CreateCFG(&allocator, data); ASSERT_NE(graph, nullptr); - TransformToSsa(graph); + graph->BuildDominatorTree(); + graph->TransformToSsa(); SSAChecker ssa_checker(graph); ssa_checker.Run(); @@ -141,9 +145,7 @@ TEST(GraphChecker, BlockEndingWithNonBranchInstruction) { ASSERT_FALSE(graph_checker.IsValid()); } -class SSACheckerTest : public CommonCompilerTest {}; - -TEST_F(SSACheckerTest, SSAPhi) { +TEST(SSAChecker, SSAPhi) { // This code creates one Phi function during the conversion to SSA form. const uint16_t data[] = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc index 5f1328f545..e9fdb84d1e 100644 --- a/compiler/optimizing/graph_visualizer.cc +++ b/compiler/optimizing/graph_visualizer.cc @@ -30,7 +30,6 @@ #include "optimization.h" #include "reference_type_propagation.h" #include "register_allocator.h" -#include "ssa_builder.h" #include "ssa_liveness_analysis.h" #include "utils/assembler.h" @@ -506,7 +505,7 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor { } else { StartAttributeStream("loop") << "B" << info->GetHeader()->GetBlockId(); } - } else if ((IsPass(SsaBuilder::kSsaBuilderPassName) + } else if ((IsPass(ReferenceTypePropagation::kReferenceTypePropagationPassName) || IsPass(HInliner::kInlinerPassName)) && (instruction->GetType() == Primitive::kPrimNot)) { ReferenceTypeInfo info = instruction->IsLoadClass() @@ -520,15 +519,21 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor { StartAttributeStream("exact") << std::boolalpha << info.IsExact() << std::noboolalpha; } else if (instruction->IsLoadClass()) { StartAttributeStream("klass") << "unresolved"; - } else { + } else if (instruction->IsNullConstant()) { // The NullConstant may be added to the graph during other passes that happen between // ReferenceTypePropagation and Inliner (e.g. InstructionSimplifier). If the inliner // doesn't run or doesn't inline anything, the NullConstant remains untyped. // So we should check NullConstants for validity only after reference type propagation. - DCHECK(graph_in_bad_state_ || - (!is_after_pass_ && IsPass(SsaBuilder::kSsaBuilderPassName))) - << instruction->DebugName() << instruction->GetId() << " has invalid rti " - << (is_after_pass_ ? "after" : "before") << " pass " << pass_name_; + // + // Note: The infrastructure to properly type NullConstants everywhere is to complex to add + // for the benefits. + StartAttributeStream("klass") << "not_set"; + DCHECK(!is_after_pass_ + || !IsPass(ReferenceTypePropagation::kReferenceTypePropagationPassName)) + << " Expected a valid rti after reference type propagation"; + } else { + DCHECK(!is_after_pass_) + << "Expected a valid rti after reference type propagation"; } } if (disasm_info_ != nullptr) { diff --git a/compiler/optimizing/gvn_test.cc b/compiler/optimizing/gvn_test.cc index 9929696ded..de60cf21aa 100644 --- a/compiler/optimizing/gvn_test.cc +++ b/compiler/optimizing/gvn_test.cc @@ -21,11 +21,11 @@ #include "optimizing_unit_test.h" #include "side_effects_analysis.h" -namespace art { +#include "gtest/gtest.h" -class GVNTest : public CommonCompilerTest {}; +namespace art { -TEST_F(GVNTest, LocalFieldElimination) { +TEST(GVNTest, LocalFieldElimination) { ArenaPool pool; ArenaAllocator allocator(&pool); NullHandle<mirror::DexCache> dex_cache; @@ -100,7 +100,7 @@ TEST_F(GVNTest, LocalFieldElimination) { ASSERT_EQ(different_offset->GetBlock(), block); ASSERT_EQ(use_after_kill->GetBlock(), block); - TransformToSsa(graph); + graph->TryBuildingSsa(); SideEffectsAnalysis side_effects(graph); side_effects.Run(); GVNOptimization(graph, side_effects).Run(); @@ -110,7 +110,7 @@ TEST_F(GVNTest, LocalFieldElimination) { ASSERT_EQ(use_after_kill->GetBlock(), block); } -TEST_F(GVNTest, GlobalFieldElimination) { +TEST(GVNTest, GlobalFieldElimination) { ArenaPool pool; ArenaAllocator allocator(&pool); NullHandle<mirror::DexCache> dex_cache; @@ -182,7 +182,7 @@ TEST_F(GVNTest, GlobalFieldElimination) { 0)); join->AddInstruction(new (&allocator) HExit()); - TransformToSsa(graph); + graph->TryBuildingSsa(); SideEffectsAnalysis side_effects(graph); side_effects.Run(); GVNOptimization(graph, side_effects).Run(); @@ -193,7 +193,7 @@ TEST_F(GVNTest, GlobalFieldElimination) { ASSERT_TRUE(join->GetFirstInstruction()->IsExit()); } -TEST_F(GVNTest, LoopFieldElimination) { +TEST(GVNTest, LoopFieldElimination) { ArenaPool pool; ArenaAllocator allocator(&pool); NullHandle<mirror::DexCache> dex_cache; @@ -288,7 +288,7 @@ TEST_F(GVNTest, LoopFieldElimination) { ASSERT_EQ(field_get_in_loop_body->GetBlock(), loop_body); ASSERT_EQ(field_get_in_exit->GetBlock(), exit); - TransformToSsa(graph); + graph->TryBuildingSsa(); { SideEffectsAnalysis side_effects(graph); side_effects.Run(); @@ -316,7 +316,7 @@ TEST_F(GVNTest, LoopFieldElimination) { } // Test that inner loops affect the side effects of the outer loop. -TEST_F(GVNTest, LoopSideEffects) { +TEST(GVNTest, LoopSideEffects) { ArenaPool pool; ArenaAllocator allocator(&pool); NullHandle<mirror::DexCache> dex_cache; @@ -364,7 +364,7 @@ TEST_F(GVNTest, LoopSideEffects) { inner_loop_exit->AddInstruction(new (&allocator) HGoto()); outer_loop_exit->AddInstruction(new (&allocator) HExit()); - TransformToSsa(graph); + graph->TryBuildingSsa(); ASSERT_TRUE(inner_loop_header->GetLoopInformation()->IsIn( *outer_loop_header->GetLoopInformation())); diff --git a/compiler/optimizing/induction_var_analysis_test.cc b/compiler/optimizing/induction_var_analysis_test.cc index 776c115e9d..5de94f43c9 100644 --- a/compiler/optimizing/induction_var_analysis_test.cc +++ b/compiler/optimizing/induction_var_analysis_test.cc @@ -18,6 +18,7 @@ #include "base/arena_allocator.h" #include "builder.h" +#include "gtest/gtest.h" #include "induction_var_analysis.h" #include "nodes.h" #include "optimizing_unit_test.h" @@ -27,7 +28,7 @@ namespace art { /** * Fixture class for the InductionVarAnalysis tests. */ -class InductionVarAnalysisTest : public CommonCompilerTest { +class InductionVarAnalysisTest : public testing::Test { public: InductionVarAnalysisTest() : pool_(), allocator_(&pool_) { graph_ = CreateGraph(&allocator_); @@ -101,7 +102,6 @@ class InductionVarAnalysisTest : public CommonCompilerTest { basic_[d] = new (&allocator_) HLocal(d); entry_->AddInstruction(basic_[d]); loop_preheader_[d]->AddInstruction(new (&allocator_) HStoreLocal(basic_[d], constant0_)); - loop_preheader_[d]->AddInstruction(new (&allocator_) HGoto()); HInstruction* load = new (&allocator_) HLoadLocal(basic_[d], Primitive::kPrimInt); loop_header_[d]->AddInstruction(load); HInstruction* compare = new (&allocator_) HLessThan(load, constant100_); @@ -168,7 +168,7 @@ class InductionVarAnalysisTest : public CommonCompilerTest { // Performs InductionVarAnalysis (after proper set up). void PerformInductionVarAnalysis() { - TransformToSsa(graph_); + ASSERT_TRUE(graph_->TryBuildingSsa()); iva_ = new (&allocator_) HInductionVarAnalysis(graph_); iva_->Run(); } @@ -212,7 +212,7 @@ TEST_F(InductionVarAnalysisTest, ProperLoopSetup) { // .. // } BuildLoopNest(10); - TransformToSsa(graph_); + ASSERT_TRUE(graph_->TryBuildingSsa()); ASSERT_EQ(entry_->GetLoopInformation(), nullptr); for (int d = 0; d < 1; d++) { ASSERT_EQ(loop_preheader_[d]->GetLoopInformation(), diff --git a/compiler/optimizing/induction_var_range_test.cc b/compiler/optimizing/induction_var_range_test.cc index eda9c01a01..5c0bdd7c4c 100644 --- a/compiler/optimizing/induction_var_range_test.cc +++ b/compiler/optimizing/induction_var_range_test.cc @@ -16,6 +16,7 @@ #include "base/arena_allocator.h" #include "builder.h" +#include "gtest/gtest.h" #include "induction_var_analysis.h" #include "induction_var_range.h" #include "nodes.h" @@ -28,7 +29,7 @@ using Value = InductionVarRange::Value; /** * Fixture class for the InductionVarRange tests. */ -class InductionVarRangeTest : public CommonCompilerTest { +class InductionVarRangeTest : public testing::Test { public: InductionVarRangeTest() : pool_(), @@ -112,7 +113,7 @@ class InductionVarRangeTest : public CommonCompilerTest { /** Constructs SSA and performs induction variable analysis. */ void PerformInductionVarAnalysis() { - TransformToSsa(graph_); + ASSERT_TRUE(graph_->TryBuildingSsa()); iva_->Run(); } diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index db1170909f..a4dcb3aeba 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -33,7 +33,6 @@ #include "reference_type_propagation.h" #include "register_allocator.h" #include "sharpening.h" -#include "ssa_builder.h" #include "ssa_phi_elimination.h" #include "scoped_thread_state_change.h" #include "thread.h" @@ -515,7 +514,7 @@ bool HInliner::TryBuildAndInline(ArtMethod* resolved_method, return false; } - if (callee_graph->TryBuildingSsa(handles_) != kBuildSsaSuccess) { + if (!callee_graph->TryBuildingSsa()) { VLOG(compiler) << "Method " << PrettyMethod(method_index, callee_dex_file) << " could not be transformed to SSA"; return false; @@ -550,12 +549,14 @@ bool HInliner::TryBuildAndInline(ArtMethod* resolved_method, // Run simple optimizations on the graph. HDeadCodeElimination dce(callee_graph, stats_); HConstantFolding fold(callee_graph); + ReferenceTypePropagation type_propagation(callee_graph, handles_); HSharpening sharpening(callee_graph, codegen_, dex_compilation_unit, compiler_driver_); InstructionSimplifier simplify(callee_graph, stats_); IntrinsicsRecognizer intrinsics(callee_graph, compiler_driver_); HOptimization* optimizations[] = { &intrinsics, + &type_propagation, &sharpening, &simplify, &fold, @@ -676,36 +677,42 @@ bool HInliner::TryBuildAndInline(ArtMethod* resolved_method, DCHECK_EQ(graph_, return_replacement->GetBlock()->GetGraph()); } + // When merging the graph we might create a new NullConstant in the caller graph which does + // not have the chance to be typed. We assign the correct type here so that we can keep the + // assertion that every reference has a valid type. This also simplifies checks along the way. + HNullConstant* null_constant = graph_->GetNullConstant(); + if (!null_constant->GetReferenceTypeInfo().IsValid()) { + ReferenceTypeInfo::TypeHandle obj_handle = + handles_->NewHandle(class_linker->GetClassRoot(ClassLinker::kJavaLangObject)); + null_constant->SetReferenceTypeInfo( + ReferenceTypeInfo::Create(obj_handle, false /* is_exact */)); + } + // Check the integrity of reference types and run another type propagation if needed. - if (return_replacement != nullptr) { - if (return_replacement->GetType() == Primitive::kPrimNot) { - if (!return_replacement->GetReferenceTypeInfo().IsValid()) { - // Make sure that we have a valid type for the return. We may get an invalid one when - // we inline invokes with multiple branches and create a Phi for the result. - // TODO: we could be more precise by merging the phi inputs but that requires - // some functionality from the reference type propagation. - DCHECK(return_replacement->IsPhi()); - size_t pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); - ReferenceTypeInfo::TypeHandle return_handle = - handles_->NewHandle(resolved_method->GetReturnType(true /* resolve */, pointer_size)); - return_replacement->SetReferenceTypeInfo(ReferenceTypeInfo::Create( - return_handle, return_handle->CannotBeAssignedFromOtherTypes() /* is_exact */)); - } + if ((return_replacement != nullptr) + && (return_replacement->GetType() == Primitive::kPrimNot)) { + if (!return_replacement->GetReferenceTypeInfo().IsValid()) { + // Make sure that we have a valid type for the return. We may get an invalid one when + // we inline invokes with multiple branches and create a Phi for the result. + // TODO: we could be more precise by merging the phi inputs but that requires + // some functionality from the reference type propagation. + DCHECK(return_replacement->IsPhi()); + size_t pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); + ReferenceTypeInfo::TypeHandle return_handle = + handles_->NewHandle(resolved_method->GetReturnType(true /* resolve */, pointer_size)); + return_replacement->SetReferenceTypeInfo(ReferenceTypeInfo::Create( + return_handle, return_handle->CannotBeAssignedFromOtherTypes() /* is_exact */)); + } - if (do_rtp) { - // If the return type is a refinement of the declared type run the type propagation again. - ReferenceTypeInfo return_rti = return_replacement->GetReferenceTypeInfo(); - ReferenceTypeInfo invoke_rti = invoke_instruction->GetReferenceTypeInfo(); - if (invoke_rti.IsStrictSupertypeOf(return_rti) - || (return_rti.IsExact() && !invoke_rti.IsExact()) - || !return_replacement->CanBeNull()) { - ReferenceTypePropagation(graph_, handles_).Run(); - } - } - } else if (return_replacement->IsInstanceOf()) { - if (do_rtp) { - // Inlining InstanceOf into an If may put a tighter bound on reference types. - ReferenceTypePropagation(graph_, handles_).Run(); + if (do_rtp) { + // If the return type is a refinement of the declared type run the type propagation again. + ReferenceTypeInfo return_rti = return_replacement->GetReferenceTypeInfo(); + ReferenceTypeInfo invoke_rti = invoke_instruction->GetReferenceTypeInfo(); + if (invoke_rti.IsStrictSupertypeOf(return_rti) + || (return_rti.IsExact() && !invoke_rti.IsExact()) + || !return_replacement->CanBeNull()) { + ReferenceTypePropagation rtp_fixup(graph_, handles_); + rtp_fixup.Run(); } } } diff --git a/compiler/optimizing/instruction_simplifier_arm64.cc b/compiler/optimizing/instruction_simplifier_arm64.cc index 6bbc751bee..6a34b13320 100644 --- a/compiler/optimizing/instruction_simplifier_arm64.cc +++ b/compiler/optimizing/instruction_simplifier_arm64.cc @@ -49,7 +49,6 @@ void InstructionSimplifierArm64Visitor::TryExtractArrayAccessAddress(HInstructio GetGraph()->GetIntConstant(mirror::Array::DataOffset(access_size).Uint32Value()); HArm64IntermediateAddress* address = new (arena) HArm64IntermediateAddress(array, offset, kNoDexPc); - address->SetReferenceTypeInfo(array->GetReferenceTypeInfo()); access->GetBlock()->InsertInstructionBefore(address, access); access->ReplaceInput(address, 0); // Both instructions must depend on GC to prevent any instruction that can diff --git a/compiler/optimizing/licm_test.cc b/compiler/optimizing/licm_test.cc index 956de2cb8a..2bb769a430 100644 --- a/compiler/optimizing/licm_test.cc +++ b/compiler/optimizing/licm_test.cc @@ -16,6 +16,7 @@ #include "base/arena_allocator.h" #include "builder.h" +#include "gtest/gtest.h" #include "licm.h" #include "nodes.h" #include "optimizing_unit_test.h" @@ -26,7 +27,7 @@ namespace art { /** * Fixture class for the LICM tests. */ -class LICMTest : public CommonCompilerTest { +class LICMTest : public testing::Test { public: LICMTest() : pool_(), allocator_(&pool_) { graph_ = CreateGraph(&allocator_); @@ -69,16 +70,16 @@ class LICMTest : public CommonCompilerTest { loop_preheader_->AddInstruction(new (&allocator_) HGoto()); loop_header_->AddInstruction(new (&allocator_) HIf(parameter_)); loop_body_->AddInstruction(new (&allocator_) HGoto()); - return_->AddInstruction(new (&allocator_) HReturnVoid()); exit_->AddInstruction(new (&allocator_) HExit()); } // Performs LICM optimizations (after proper set up). void PerformLICM() { - TransformToSsa(graph_); + ASSERT_TRUE(graph_->TryBuildingSsa()); SideEffectsAnalysis side_effects(graph_); side_effects.Run(); - LICM(graph_, side_effects).Run(); + LICM licm(graph_, side_effects); + licm.Run(); } // General building fields. @@ -168,10 +169,10 @@ TEST_F(LICMTest, ArrayHoisting) { // Populate the loop with instructions: set/get array with different types. HInstruction* get_array = new (&allocator_) HArrayGet( - parameter_, constant_, Primitive::kPrimByte, 0); + parameter_, constant_, Primitive::kPrimLong, 0); loop_body_->InsertInstructionBefore(get_array, loop_body_->GetLastInstruction()); HInstruction* set_array = new (&allocator_) HArraySet( - parameter_, constant_, constant_, Primitive::kPrimShort, 0); + parameter_, constant_, constant_, Primitive::kPrimInt, 0); loop_body_->InsertInstructionBefore(set_array, loop_body_->GetLastInstruction()); EXPECT_EQ(get_array->GetBlock(), loop_body_); @@ -186,10 +187,10 @@ TEST_F(LICMTest, NoArrayHoisting) { // Populate the loop with instructions: set/get array with same types. HInstruction* get_array = new (&allocator_) HArrayGet( - parameter_, constant_, Primitive::kPrimByte, 0); + parameter_, constant_, Primitive::kPrimLong, 0); loop_body_->InsertInstructionBefore(get_array, loop_body_->GetLastInstruction()); HInstruction* set_array = new (&allocator_) HArraySet( - parameter_, get_array, constant_, Primitive::kPrimByte, 0); + parameter_, get_array, constant_, Primitive::kPrimLong, 0); loop_body_->InsertInstructionBefore(set_array, loop_body_->GetLastInstruction()); EXPECT_EQ(get_array->GetBlock(), loop_body_); diff --git a/compiler/optimizing/linearize_test.cc b/compiler/optimizing/linearize_test.cc index ed275b1544..a059766e00 100644 --- a/compiler/optimizing/linearize_test.cc +++ b/compiler/optimizing/linearize_test.cc @@ -29,11 +29,12 @@ #include "nodes.h" #include "optimizing_unit_test.h" #include "pretty_printer.h" +#include "ssa_builder.h" #include "ssa_liveness_analysis.h" -namespace art { +#include "gtest/gtest.h" -class LinearizeTest : public CommonCompilerTest {}; +namespace art { template <size_t number_of_blocks> static void TestCode(const uint16_t* data, const uint32_t (&expected_order)[number_of_blocks]) { @@ -45,7 +46,7 @@ static void TestCode(const uint16_t* data, const uint32_t (&expected_order)[numb bool graph_built = builder.BuildGraph(*item); ASSERT_TRUE(graph_built); - TransformToSsa(graph); + graph->TryBuildingSsa(); std::unique_ptr<const X86InstructionSetFeatures> features_x86( X86InstructionSetFeatures::FromCppDefines()); @@ -59,7 +60,7 @@ static void TestCode(const uint16_t* data, const uint32_t (&expected_order)[numb } } -TEST_F(LinearizeTest, CFG1) { +TEST(LinearizeTest, CFG1) { // Structure of this graph (+ are back edges) // Block0 // | @@ -84,7 +85,7 @@ TEST_F(LinearizeTest, CFG1) { TestCode(data, blocks); } -TEST_F(LinearizeTest, CFG2) { +TEST(LinearizeTest, CFG2) { // Structure of this graph (+ are back edges) // Block0 // | @@ -109,7 +110,7 @@ TEST_F(LinearizeTest, CFG2) { TestCode(data, blocks); } -TEST_F(LinearizeTest, CFG3) { +TEST(LinearizeTest, CFG3) { // Structure of this graph (+ are back edges) // Block0 // | @@ -136,7 +137,7 @@ TEST_F(LinearizeTest, CFG3) { TestCode(data, blocks); } -TEST_F(LinearizeTest, CFG4) { +TEST(LinearizeTest, CFG4) { /* Structure of this graph (+ are back edges) // Block0 // | @@ -166,7 +167,7 @@ TEST_F(LinearizeTest, CFG4) { TestCode(data, blocks); } -TEST_F(LinearizeTest, CFG5) { +TEST(LinearizeTest, CFG5) { /* Structure of this graph (+ are back edges) // Block0 // | @@ -196,7 +197,7 @@ TEST_F(LinearizeTest, CFG5) { TestCode(data, blocks); } -TEST_F(LinearizeTest, CFG6) { +TEST(LinearizeTest, CFG6) { // Block0 // | // Block1 @@ -222,7 +223,7 @@ TEST_F(LinearizeTest, CFG6) { TestCode(data, blocks); } -TEST_F(LinearizeTest, CFG7) { +TEST(LinearizeTest, CFG7) { // Structure of this graph (+ are back edges) // Block0 // | diff --git a/compiler/optimizing/live_ranges_test.cc b/compiler/optimizing/live_ranges_test.cc index 926f9399a5..7f67560692 100644 --- a/compiler/optimizing/live_ranges_test.cc +++ b/compiler/optimizing/live_ranges_test.cc @@ -27,9 +27,9 @@ #include "prepare_for_register_allocation.h" #include "ssa_liveness_analysis.h" -namespace art { +#include "gtest/gtest.h" -class LiveRangesTest : public CommonCompilerTest {}; +namespace art { static HGraph* BuildGraph(const uint16_t* data, ArenaAllocator* allocator) { HGraph* graph = CreateGraph(allocator); @@ -39,13 +39,13 @@ static HGraph* BuildGraph(const uint16_t* data, ArenaAllocator* allocator) { // Suspend checks implementation may change in the future, and this test relies // on how instructions are ordered. RemoveSuspendChecks(graph); - TransformToSsa(graph); + graph->TryBuildingSsa(); // `Inline` conditions into ifs. PrepareForRegisterAllocation(graph).Run(); return graph; } -TEST_F(LiveRangesTest, CFG1) { +TEST(LiveRangesTest, CFG1) { /* * Test the following snippet: * return 0; @@ -83,7 +83,7 @@ TEST_F(LiveRangesTest, CFG1) { ASSERT_TRUE(range->GetNext() == nullptr); } -TEST_F(LiveRangesTest, CFG2) { +TEST(LiveRangesTest, CFG2) { /* * Test the following snippet: * var a = 0; @@ -131,7 +131,7 @@ TEST_F(LiveRangesTest, CFG2) { ASSERT_TRUE(range->GetNext() == nullptr); } -TEST_F(LiveRangesTest, CFG3) { +TEST(LiveRangesTest, CFG3) { /* * Test the following snippet: * var a = 0; @@ -204,7 +204,7 @@ TEST_F(LiveRangesTest, CFG3) { ASSERT_TRUE(range->GetNext() == nullptr); } -TEST_F(LiveRangesTest, Loop1) { +TEST(LiveRangesTest, Loop1) { /* * Test the following snippet: * var a = 0; @@ -284,7 +284,7 @@ TEST_F(LiveRangesTest, Loop1) { ASSERT_TRUE(range->GetNext() == nullptr); } -TEST_F(LiveRangesTest, Loop2) { +TEST(LiveRangesTest, Loop2) { /* * Test the following snippet: * var a = 0; @@ -360,7 +360,7 @@ TEST_F(LiveRangesTest, Loop2) { ASSERT_TRUE(range->GetNext() == nullptr); } -TEST_F(LiveRangesTest, CFG4) { +TEST(LiveRangesTest, CFG4) { /* * Test the following snippet: * var a = 0; diff --git a/compiler/optimizing/liveness_test.cc b/compiler/optimizing/liveness_test.cc index 7736eedae1..9d7d0b6c67 100644 --- a/compiler/optimizing/liveness_test.cc +++ b/compiler/optimizing/liveness_test.cc @@ -27,9 +27,9 @@ #include "prepare_for_register_allocation.h" #include "ssa_liveness_analysis.h" -namespace art { +#include "gtest/gtest.h" -class LivenessTest : public CommonCompilerTest {}; +namespace art { static void DumpBitVector(BitVector* vector, std::ostream& buffer, @@ -51,7 +51,7 @@ static void TestCode(const uint16_t* data, const char* expected) { const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data); bool graph_built = builder.BuildGraph(*item); ASSERT_TRUE(graph_built); - TransformToSsa(graph); + graph->TryBuildingSsa(); // `Inline` conditions into ifs. PrepareForRegisterAllocation(graph).Run(); std::unique_ptr<const X86InstructionSetFeatures> features_x86( @@ -75,7 +75,7 @@ static void TestCode(const uint16_t* data, const char* expected) { ASSERT_STREQ(expected, buffer.str().c_str()); } -TEST_F(LivenessTest, CFG1) { +TEST(LivenessTest, CFG1) { const char* expected = "Block 0\n" " live in: (0)\n" @@ -98,7 +98,7 @@ TEST_F(LivenessTest, CFG1) { TestCode(data, expected); } -TEST_F(LivenessTest, CFG2) { +TEST(LivenessTest, CFG2) { const char* expected = "Block 0\n" " live in: (0)\n" @@ -120,7 +120,7 @@ TEST_F(LivenessTest, CFG2) { TestCode(data, expected); } -TEST_F(LivenessTest, CFG3) { +TEST(LivenessTest, CFG3) { const char* expected = "Block 0\n" // entry block " live in: (000)\n" @@ -149,7 +149,7 @@ TEST_F(LivenessTest, CFG3) { TestCode(data, expected); } -TEST_F(LivenessTest, CFG4) { +TEST(LivenessTest, CFG4) { // var a; // if (0 == 0) { // a = 5; @@ -197,7 +197,7 @@ TEST_F(LivenessTest, CFG4) { TestCode(data, expected); } -TEST_F(LivenessTest, CFG5) { +TEST(LivenessTest, CFG5) { // var a = 0; // if (0 == 0) { // } else { @@ -242,7 +242,7 @@ TEST_F(LivenessTest, CFG5) { TestCode(data, expected); } -TEST_F(LivenessTest, Loop1) { +TEST(LivenessTest, Loop1) { // Simple loop with one preheader and one back edge. // var a = 0; // while (a == a) { @@ -288,7 +288,7 @@ TEST_F(LivenessTest, Loop1) { TestCode(data, expected); } -TEST_F(LivenessTest, Loop3) { +TEST(LivenessTest, Loop3) { // Test that the returned value stays live in a preceding loop. // var a = 0; // while (a == a) { @@ -335,7 +335,7 @@ TEST_F(LivenessTest, Loop3) { } -TEST_F(LivenessTest, Loop4) { +TEST(LivenessTest, Loop4) { // Make sure we support a preheader of a loop not being the first predecessor // in the predecessor list of the header. // var a = 0; @@ -387,7 +387,7 @@ TEST_F(LivenessTest, Loop4) { TestCode(data, expected); } -TEST_F(LivenessTest, Loop5) { +TEST(LivenessTest, Loop5) { // Make sure we create a preheader of a loop when a header originally has two // incoming blocks and one back edge. // Bitsets are made of: @@ -443,7 +443,7 @@ TEST_F(LivenessTest, Loop5) { TestCode(data, expected); } -TEST_F(LivenessTest, Loop6) { +TEST(LivenessTest, Loop6) { // Bitsets are made of: // (constant0, constant4, constant5, phi in block 2) const char* expected = @@ -494,7 +494,7 @@ TEST_F(LivenessTest, Loop6) { } -TEST_F(LivenessTest, Loop7) { +TEST(LivenessTest, Loop7) { // Bitsets are made of: // (constant0, constant4, constant5, phi in block 2, phi in block 6) const char* expected = @@ -548,7 +548,7 @@ TEST_F(LivenessTest, Loop7) { TestCode(data, expected); } -TEST_F(LivenessTest, Loop8) { +TEST(LivenessTest, Loop8) { // var a = 0; // while (a == a) { // a = a + a; diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index bb0b545c1e..926bc156cf 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -198,38 +198,10 @@ void HGraph::ComputeDominanceInformation() { } } -BuildSsaResult HGraph::TryBuildingSsa(StackHandleScopeCollection* handles) { - BuildDominatorTree(); - - // The SSA builder requires loops to all be natural. Specifically, the dead phi - // elimination phase checks the consistency of the graph when doing a post-order - // visit for eliminating dead phis: a dead phi can only have loop header phi - // users remaining when being visited. - BuildSsaResult result = AnalyzeNaturalLoops(); - if (result != kBuildSsaSuccess) { - return result; - } - - // Precompute per-block try membership before entering the SSA builder, - // which needs the information to build catch block phis from values of - // locals at throwing instructions inside try blocks. - ComputeTryBlockInformation(); - - // Create the inexact Object reference type and store it in the HGraph. - ScopedObjectAccess soa(Thread::Current()); - ClassLinker* linker = Runtime::Current()->GetClassLinker(); - inexact_object_rti_ = ReferenceTypeInfo::Create( - handles->NewHandle(linker->GetClassRoot(ClassLinker::kJavaLangObject)), - /* is_exact */ false); - - // Tranforms graph to SSA form. - result = SsaBuilder(this, handles).BuildSsa(); - if (result != kBuildSsaSuccess) { - return result; - } - - in_ssa_form_ = true; - return kBuildSsaSuccess; +void HGraph::TransformToSsa() { + DCHECK(!reverse_post_order_.empty()); + SsaBuilder ssa_builder(this); + ssa_builder.BuildSsa(); } HBasicBlock* HGraph::SplitEdge(HBasicBlock* block, HBasicBlock* successor) { @@ -438,7 +410,7 @@ void HGraph::SimplifyCFG() { } } -BuildSsaResult HGraph::AnalyzeNaturalLoops() const { +bool HGraph::AnalyzeNaturalLoops() const { // Order does not matter. for (HReversePostOrderIterator it(*this); !it.Done(); it.Advance()) { HBasicBlock* block = it.Current(); @@ -446,16 +418,16 @@ BuildSsaResult HGraph::AnalyzeNaturalLoops() const { if (block->IsCatchBlock()) { // TODO: Dealing with exceptional back edges could be tricky because // they only approximate the real control flow. Bail out for now. - return kBuildSsaFailThrowCatchLoop; + return false; } HLoopInformation* info = block->GetLoopInformation(); if (!info->Populate()) { // Abort if the loop is non natural. We currently bailout in such cases. - return kBuildSsaFailNonNaturalLoop; + return false; } } } - return kBuildSsaSuccess; + return true; } void HGraph::InsertConstant(HConstant* constant) { @@ -474,13 +446,8 @@ HNullConstant* HGraph::GetNullConstant(uint32_t dex_pc) { // id and/or any invariants the graph is assuming when adding new instructions. if ((cached_null_constant_ == nullptr) || (cached_null_constant_->GetBlock() == nullptr)) { cached_null_constant_ = new (arena_) HNullConstant(dex_pc); - cached_null_constant_->SetReferenceTypeInfo(inexact_object_rti_); InsertConstant(cached_null_constant_); } - if (kIsDebugBuild) { - ScopedObjectAccess soa(Thread::Current()); - DCHECK(cached_null_constant_->GetReferenceTypeInfo().IsValid()); - } return cached_null_constant_; } diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 55e436f0b7..1f8ef4717c 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -98,13 +98,6 @@ enum IfCondition { kCondAE, // >= }; -enum BuildSsaResult { - kBuildSsaFailNonNaturalLoop, - kBuildSsaFailThrowCatchLoop, - kBuildSsaFailAmbiguousArrayGet, - kBuildSsaSuccess, -}; - class HInstructionList : public ValueObject { public: HInstructionList() : first_instruction_(nullptr), last_instruction_(nullptr) {} @@ -150,122 +143,6 @@ class HInstructionList : public ValueObject { DISALLOW_COPY_AND_ASSIGN(HInstructionList); }; -class ReferenceTypeInfo : ValueObject { - public: - typedef Handle<mirror::Class> TypeHandle; - - static ReferenceTypeInfo Create(TypeHandle type_handle, bool is_exact) { - // The constructor will check that the type_handle is valid. - return ReferenceTypeInfo(type_handle, is_exact); - } - - static ReferenceTypeInfo CreateInvalid() { return ReferenceTypeInfo(); } - - static bool IsValidHandle(TypeHandle handle) SHARED_REQUIRES(Locks::mutator_lock_) { - return handle.GetReference() != nullptr; - } - - bool IsValid() const SHARED_REQUIRES(Locks::mutator_lock_) { - return IsValidHandle(type_handle_); - } - - bool IsExact() const { return is_exact_; } - - bool IsObjectClass() const SHARED_REQUIRES(Locks::mutator_lock_) { - DCHECK(IsValid()); - return GetTypeHandle()->IsObjectClass(); - } - - bool IsStringClass() const SHARED_REQUIRES(Locks::mutator_lock_) { - DCHECK(IsValid()); - return GetTypeHandle()->IsStringClass(); - } - - bool IsObjectArray() const SHARED_REQUIRES(Locks::mutator_lock_) { - DCHECK(IsValid()); - return IsArrayClass() && GetTypeHandle()->GetComponentType()->IsObjectClass(); - } - - bool IsInterface() const SHARED_REQUIRES(Locks::mutator_lock_) { - DCHECK(IsValid()); - return GetTypeHandle()->IsInterface(); - } - - bool IsArrayClass() const SHARED_REQUIRES(Locks::mutator_lock_) { - DCHECK(IsValid()); - return GetTypeHandle()->IsArrayClass(); - } - - bool IsPrimitiveArrayClass() const SHARED_REQUIRES(Locks::mutator_lock_) { - DCHECK(IsValid()); - return GetTypeHandle()->IsPrimitiveArray(); - } - - bool IsNonPrimitiveArrayClass() const SHARED_REQUIRES(Locks::mutator_lock_) { - DCHECK(IsValid()); - return GetTypeHandle()->IsArrayClass() && !GetTypeHandle()->IsPrimitiveArray(); - } - - bool CanArrayHold(ReferenceTypeInfo rti) const SHARED_REQUIRES(Locks::mutator_lock_) { - DCHECK(IsValid()); - if (!IsExact()) return false; - if (!IsArrayClass()) return false; - return GetTypeHandle()->GetComponentType()->IsAssignableFrom(rti.GetTypeHandle().Get()); - } - - bool CanArrayHoldValuesOf(ReferenceTypeInfo rti) const SHARED_REQUIRES(Locks::mutator_lock_) { - DCHECK(IsValid()); - if (!IsExact()) return false; - if (!IsArrayClass()) return false; - if (!rti.IsArrayClass()) return false; - return GetTypeHandle()->GetComponentType()->IsAssignableFrom( - rti.GetTypeHandle()->GetComponentType()); - } - - Handle<mirror::Class> GetTypeHandle() const { return type_handle_; } - - bool IsSupertypeOf(ReferenceTypeInfo rti) const SHARED_REQUIRES(Locks::mutator_lock_) { - DCHECK(IsValid()); - DCHECK(rti.IsValid()); - return GetTypeHandle()->IsAssignableFrom(rti.GetTypeHandle().Get()); - } - - bool IsStrictSupertypeOf(ReferenceTypeInfo rti) const SHARED_REQUIRES(Locks::mutator_lock_) { - DCHECK(IsValid()); - DCHECK(rti.IsValid()); - return GetTypeHandle().Get() != rti.GetTypeHandle().Get() && - GetTypeHandle()->IsAssignableFrom(rti.GetTypeHandle().Get()); - } - - // Returns true if the type information provide the same amount of details. - // Note that it does not mean that the instructions have the same actual type - // (because the type can be the result of a merge). - bool IsEqual(ReferenceTypeInfo rti) SHARED_REQUIRES(Locks::mutator_lock_) { - if (!IsValid() && !rti.IsValid()) { - // Invalid types are equal. - return true; - } - if (!IsValid() || !rti.IsValid()) { - // One is valid, the other not. - return false; - } - return IsExact() == rti.IsExact() - && GetTypeHandle().Get() == rti.GetTypeHandle().Get(); - } - - private: - ReferenceTypeInfo(); - ReferenceTypeInfo(TypeHandle type_handle, bool is_exact); - - // The class of the object. - TypeHandle type_handle_; - // Whether or not the type is exact or a superclass of the actual type. - // Whether or not we have any information about this type. - bool is_exact_; -}; - -std::ostream& operator<<(std::ostream& os, const ReferenceTypeInfo& rhs); - // Control-flow graph of a method. Contains a list of basic blocks. class HGraph : public ArenaObject<kArenaAllocGraph> { public: @@ -302,8 +179,7 @@ class HGraph : public ArenaObject<kArenaAllocGraph> { cached_float_constants_(std::less<int32_t>(), arena->Adapter(kArenaAllocConstantsMap)), cached_long_constants_(std::less<int64_t>(), arena->Adapter(kArenaAllocConstantsMap)), cached_double_constants_(std::less<int64_t>(), arena->Adapter(kArenaAllocConstantsMap)), - cached_current_method_(nullptr), - inexact_object_rti_(ReferenceTypeInfo::CreateInvalid()) { + cached_current_method_(nullptr) { blocks_.reserve(kDefaultNumberOfBlocks); } @@ -321,23 +197,36 @@ class HGraph : public ArenaObject<kArenaAllocGraph> { void AddBlock(HBasicBlock* block); - // Try building the SSA form of this graph, with dominance computation and - // loop recognition. Returns a code specifying that it was successful or the - // reason for failure. - BuildSsaResult TryBuildingSsa(StackHandleScopeCollection* handles); + // Try building the SSA form of this graph, with dominance computation and loop + // recognition. Returns whether it was successful in doing all these steps. + bool TryBuildingSsa() { + BuildDominatorTree(); + // The SSA builder requires loops to all be natural. Specifically, the dead phi + // elimination phase checks the consistency of the graph when doing a post-order + // visit for eliminating dead phis: a dead phi can only have loop header phi + // users remaining when being visited. + if (!AnalyzeNaturalLoops()) return false; + // Precompute per-block try membership before entering the SSA builder, + // which needs the information to build catch block phis from values of + // locals at throwing instructions inside try blocks. + ComputeTryBlockInformation(); + TransformToSsa(); + in_ssa_form_ = true; + return true; + } void ComputeDominanceInformation(); void ClearDominanceInformation(); void BuildDominatorTree(); + void TransformToSsa(); void SimplifyCFG(); void SimplifyCatchBlocks(); - // Analyze all natural loops in this graph. Returns a code specifying that it - // was successful or the reason for failure. The method will fail if a loop - // is not natural, that is the header does not dominate a back edge, or if it - // is a throw-catch loop, i.e. the header is a catch block. - BuildSsaResult AnalyzeNaturalLoops() const; + // Analyze all natural loops in this graph. Returns false if one + // loop is not natural, that is the header does not dominate the + // back edge. + bool AnalyzeNaturalLoops() const; // Iterate over blocks to compute try block membership. Needs reverse post // order and loop information. @@ -598,10 +487,6 @@ class HGraph : public ArenaObject<kArenaAllocGraph> { // (such as when the superclass could not be found). ArtMethod* art_method_; - // Keep the RTI of inexact Object to avoid having to pass stack handle - // collection pointer to passes which may create NullConstant. - ReferenceTypeInfo inexact_object_rti_; - friend class SsaBuilder; // For caching constants. friend class SsaLivenessAnalysis; // For the linear order. ART_FRIEND_TEST(GraphTest, IfSuccessorSimpleJoinBlock1); @@ -1789,6 +1674,122 @@ class HEnvironment : public ArenaObject<kArenaAllocEnvironment> { DISALLOW_COPY_AND_ASSIGN(HEnvironment); }; +class ReferenceTypeInfo : ValueObject { + public: + typedef Handle<mirror::Class> TypeHandle; + + static ReferenceTypeInfo Create(TypeHandle type_handle, bool is_exact) { + // The constructor will check that the type_handle is valid. + return ReferenceTypeInfo(type_handle, is_exact); + } + + static ReferenceTypeInfo CreateInvalid() { return ReferenceTypeInfo(); } + + static bool IsValidHandle(TypeHandle handle) SHARED_REQUIRES(Locks::mutator_lock_) { + return handle.GetReference() != nullptr; + } + + bool IsValid() const SHARED_REQUIRES(Locks::mutator_lock_) { + return IsValidHandle(type_handle_); + } + + bool IsExact() const { return is_exact_; } + + bool IsObjectClass() const SHARED_REQUIRES(Locks::mutator_lock_) { + DCHECK(IsValid()); + return GetTypeHandle()->IsObjectClass(); + } + + bool IsStringClass() const SHARED_REQUIRES(Locks::mutator_lock_) { + DCHECK(IsValid()); + return GetTypeHandle()->IsStringClass(); + } + + bool IsObjectArray() const SHARED_REQUIRES(Locks::mutator_lock_) { + DCHECK(IsValid()); + return IsArrayClass() && GetTypeHandle()->GetComponentType()->IsObjectClass(); + } + + bool IsInterface() const SHARED_REQUIRES(Locks::mutator_lock_) { + DCHECK(IsValid()); + return GetTypeHandle()->IsInterface(); + } + + bool IsArrayClass() const SHARED_REQUIRES(Locks::mutator_lock_) { + DCHECK(IsValid()); + return GetTypeHandle()->IsArrayClass(); + } + + bool IsPrimitiveArrayClass() const SHARED_REQUIRES(Locks::mutator_lock_) { + DCHECK(IsValid()); + return GetTypeHandle()->IsPrimitiveArray(); + } + + bool IsNonPrimitiveArrayClass() const SHARED_REQUIRES(Locks::mutator_lock_) { + DCHECK(IsValid()); + return GetTypeHandle()->IsArrayClass() && !GetTypeHandle()->IsPrimitiveArray(); + } + + bool CanArrayHold(ReferenceTypeInfo rti) const SHARED_REQUIRES(Locks::mutator_lock_) { + DCHECK(IsValid()); + if (!IsExact()) return false; + if (!IsArrayClass()) return false; + return GetTypeHandle()->GetComponentType()->IsAssignableFrom(rti.GetTypeHandle().Get()); + } + + bool CanArrayHoldValuesOf(ReferenceTypeInfo rti) const SHARED_REQUIRES(Locks::mutator_lock_) { + DCHECK(IsValid()); + if (!IsExact()) return false; + if (!IsArrayClass()) return false; + if (!rti.IsArrayClass()) return false; + return GetTypeHandle()->GetComponentType()->IsAssignableFrom( + rti.GetTypeHandle()->GetComponentType()); + } + + Handle<mirror::Class> GetTypeHandle() const { return type_handle_; } + + bool IsSupertypeOf(ReferenceTypeInfo rti) const SHARED_REQUIRES(Locks::mutator_lock_) { + DCHECK(IsValid()); + DCHECK(rti.IsValid()); + return GetTypeHandle()->IsAssignableFrom(rti.GetTypeHandle().Get()); + } + + bool IsStrictSupertypeOf(ReferenceTypeInfo rti) const SHARED_REQUIRES(Locks::mutator_lock_) { + DCHECK(IsValid()); + DCHECK(rti.IsValid()); + return GetTypeHandle().Get() != rti.GetTypeHandle().Get() && + GetTypeHandle()->IsAssignableFrom(rti.GetTypeHandle().Get()); + } + + // Returns true if the type information provide the same amount of details. + // Note that it does not mean that the instructions have the same actual type + // (because the type can be the result of a merge). + bool IsEqual(ReferenceTypeInfo rti) SHARED_REQUIRES(Locks::mutator_lock_) { + if (!IsValid() && !rti.IsValid()) { + // Invalid types are equal. + return true; + } + if (!IsValid() || !rti.IsValid()) { + // One is valid, the other not. + return false; + } + return IsExact() == rti.IsExact() + && GetTypeHandle().Get() == rti.GetTypeHandle().Get(); + } + + private: + ReferenceTypeInfo(); + ReferenceTypeInfo(TypeHandle type_handle, bool is_exact); + + // The class of the object. + TypeHandle type_handle_; + // Whether or not the type is exact or a superclass of the actual type. + // Whether or not we have any information about this type. + bool is_exact_; +}; + +std::ostream& operator<<(std::ostream& os, const ReferenceTypeInfo& rhs); + class HInstruction : public ArenaObject<kArenaAllocInstruction> { public: HInstruction(SideEffects side_effects, uint32_t dex_pc) @@ -4416,16 +4417,7 @@ class HPhi : public HInstruction { void RemoveInputAt(size_t index); Primitive::Type GetType() const OVERRIDE { return type_; } - void SetType(Primitive::Type new_type) { - // Make sure that only valid type changes occur. The following are allowed: - // (1) int -> float/ref (primitive type propagation), - // (2) long -> double (primitive type propagation). - DCHECK(type_ == new_type || - (type_ == Primitive::kPrimInt && new_type == Primitive::kPrimFloat) || - (type_ == Primitive::kPrimInt && new_type == Primitive::kPrimNot) || - (type_ == Primitive::kPrimLong && new_type == Primitive::kPrimDouble)); - type_ = new_type; - } + void SetType(Primitive::Type type) { type_ = type; } bool CanBeNull() const OVERRIDE { return can_be_null_; } void SetCanBeNull(bool can_be_null) { can_be_null_ = can_be_null; } @@ -4665,21 +4657,7 @@ class HArrayGet : public HExpression<2> { return false; } - bool IsEquivalentOf(HArrayGet* other) const { - bool result = (GetDexPc() == other->GetDexPc()); - if (kIsDebugBuild && result) { - DCHECK_EQ(GetBlock(), other->GetBlock()); - DCHECK_EQ(GetArray(), other->GetArray()); - DCHECK_EQ(GetIndex(), other->GetIndex()); - if (Primitive::IsIntOrLongType(GetType())) { - DCHECK(Primitive::IsFloatingPointType(other->GetType())); - } else { - DCHECK(Primitive::IsFloatingPointType(GetType())); - DCHECK(Primitive::IsIntOrLongType(other->GetType())); - } - } - return result; - } + void SetType(Primitive::Type type) { type_ = type; } HInstruction* GetArray() const { return InputAt(0); } HInstruction* GetIndex() const { return InputAt(1); } diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index ba435180e5..831b626c4f 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -501,8 +501,11 @@ static void RunOptimizations(HGraph* graph, CompilerDriver* driver, OptimizingCompilerStats* stats, const DexCompilationUnit& dex_compilation_unit, - PassObserver* pass_observer, - StackHandleScopeCollection* handles) { + PassObserver* pass_observer) { + ScopedObjectAccess soa(Thread::Current()); + StackHandleScopeCollection handles(soa.Self()); + ScopedThreadSuspension sts(soa.Self(), kNative); + ArenaAllocator* arena = graph->GetArena(); HDeadCodeElimination* dce1 = new (arena) HDeadCodeElimination( graph, stats, HDeadCodeElimination::kInitialDeadCodeEliminationPassName); @@ -519,23 +522,29 @@ static void RunOptimizations(HGraph* graph, LoadStoreElimination* lse = new (arena) LoadStoreElimination(graph, *side_effects); HInductionVarAnalysis* induction = new (arena) HInductionVarAnalysis(graph); BoundsCheckElimination* bce = new (arena) BoundsCheckElimination(graph, *side_effects, induction); + ReferenceTypePropagation* type_propagation = + new (arena) ReferenceTypePropagation(graph, &handles); HSharpening* sharpening = new (arena) HSharpening(graph, codegen, dex_compilation_unit, driver); InstructionSimplifier* simplify2 = new (arena) InstructionSimplifier( - graph, stats, "instruction_simplifier_after_bce"); + graph, stats, "instruction_simplifier_after_types"); InstructionSimplifier* simplify3 = new (arena) InstructionSimplifier( + graph, stats, "instruction_simplifier_after_bce"); + InstructionSimplifier* simplify4 = new (arena) InstructionSimplifier( graph, stats, "instruction_simplifier_before_codegen"); IntrinsicsRecognizer* intrinsics = new (arena) IntrinsicsRecognizer(graph, driver); HOptimization* optimizations1[] = { intrinsics, - sharpening, fold1, simplify1, + type_propagation, + sharpening, dce1, + simplify2 }; RunOptimizations(optimizations1, arraysize(optimizations1), pass_observer); - MaybeRunInliner(graph, codegen, driver, stats, dex_compilation_unit, pass_observer, handles); + MaybeRunInliner(graph, codegen, driver, stats, dex_compilation_unit, pass_observer, &handles); HOptimization* optimizations2[] = { // BooleanSimplifier depends on the InstructionSimplifier removing @@ -548,13 +557,13 @@ static void RunOptimizations(HGraph* graph, induction, bce, fold3, // evaluates code generated by dynamic bce - simplify2, + simplify3, lse, dce2, // The codegen has a few assumptions that only the instruction simplifier // can satisfy. For example, the code generator does not expect to see a // HTypeConversion from a type to the same type. - simplify3, + simplify4, }; RunOptimizations(optimizations2, arraysize(optimizations2), pass_observer); @@ -759,29 +768,14 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* arena, } VLOG(compiler) << "Optimizing " << pass_observer.GetMethodName(); - if (run_optimizations_) { - ScopedObjectAccess soa(Thread::Current()); - StackHandleScopeCollection handles(soa.Self()); - ScopedThreadSuspension sts(soa.Self(), kNative); - { PassScope scope(SsaBuilder::kSsaBuilderPassName, &pass_observer); - BuildSsaResult result = graph->TryBuildingSsa(&handles); - if (result != kBuildSsaSuccess) { - switch (result) { - case kBuildSsaFailNonNaturalLoop: - MaybeRecordStat(MethodCompilationStat::kNotCompiledNonNaturalLoop); - break; - case kBuildSsaFailThrowCatchLoop: - MaybeRecordStat(MethodCompilationStat::kNotCompiledThrowCatchLoop); - break; - case kBuildSsaFailAmbiguousArrayGet: - MaybeRecordStat(MethodCompilationStat::kNotCompiledAmbiguousArrayGet); - break; - case kBuildSsaSuccess: - UNREACHABLE(); - } + if (!graph->TryBuildingSsa()) { + // We could not transform the graph to SSA, bailout. + LOG(INFO) << "Skipping compilation of " << pass_observer.GetMethodName() + << ": it contains a non natural loop"; + MaybeRecordStat(MethodCompilationStat::kNotCompiledCannotBuildSSA); pass_observer.SetGraphInBadState(); return nullptr; } @@ -792,8 +786,7 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* arena, compiler_driver, compilation_stats_.get(), dex_compilation_unit, - &pass_observer, - &handles); + &pass_observer); codegen->CompileOptimized(code_allocator); } else { codegen->CompileBaseline(code_allocator); diff --git a/compiler/optimizing/optimizing_compiler_stats.h b/compiler/optimizing/optimizing_compiler_stats.h index 4713514bb2..6296eedfb0 100644 --- a/compiler/optimizing/optimizing_compiler_stats.h +++ b/compiler/optimizing/optimizing_compiler_stats.h @@ -38,9 +38,7 @@ enum MethodCompilationStat { kRemovedDeadInstruction, kRemovedNullCheck, kNotCompiledBranchOutsideMethodCode, - kNotCompiledNonNaturalLoop, - kNotCompiledThrowCatchLoop, - kNotCompiledAmbiguousArrayGet, + kNotCompiledCannotBuildSSA, kNotCompiledHugeMethod, kNotCompiledLargeMethodNoBranches, kNotCompiledMalformedOpcode, @@ -106,9 +104,7 @@ class OptimizingCompilerStats { case kRemovedDeadInstruction: name = "RemovedDeadInstruction"; break; case kRemovedNullCheck: name = "RemovedNullCheck"; break; case kNotCompiledBranchOutsideMethodCode: name = "NotCompiledBranchOutsideMethodCode"; break; - case kNotCompiledNonNaturalLoop : name = "NotCompiledNonNaturalLoop"; break; - case kNotCompiledThrowCatchLoop : name = "NotCompiledThrowCatchLoop"; break; - case kNotCompiledAmbiguousArrayGet : name = "NotCompiledAmbiguousArrayGet"; break; + case kNotCompiledCannotBuildSSA : name = "NotCompiledCannotBuildSSA"; break; case kNotCompiledHugeMethod : name = "NotCompiledHugeMethod"; break; case kNotCompiledLargeMethodNoBranches : name = "NotCompiledLargeMethodNoBranches"; break; case kNotCompiledMalformedOpcode : name = "NotCompiledMalformedOpcode"; break; diff --git a/compiler/optimizing/optimizing_unit_test.h b/compiler/optimizing/optimizing_unit_test.h index af3a005304..350f0b14ab 100644 --- a/compiler/optimizing/optimizing_unit_test.h +++ b/compiler/optimizing/optimizing_unit_test.h @@ -19,13 +19,9 @@ #include "nodes.h" #include "builder.h" -#include "common_compiler_test.h" #include "compiler/dex/pass_manager.h" #include "dex_file.h" #include "dex_instruction.h" -#include "handle_scope-inl.h" -#include "scoped_thread_state_change.h" -#include "ssa_builder.h" #include "ssa_liveness_analysis.h" #include "gtest/gtest.h" @@ -46,6 +42,7 @@ namespace art { #define FIVE_REGISTERS_CODE_ITEM(...) N_REGISTERS_CODE_ITEM(5, __VA_ARGS__) #define SIX_REGISTERS_CODE_ITEM(...) N_REGISTERS_CODE_ITEM(6, __VA_ARGS__) + LiveInterval* BuildInterval(const size_t ranges[][2], size_t number_of_ranges, ArenaAllocator* allocator, @@ -114,12 +111,6 @@ inline bool IsRemoved(HInstruction* instruction) { return instruction->GetBlock() == nullptr; } -inline void TransformToSsa(HGraph* graph) { - ScopedObjectAccess soa(Thread::Current()); - StackHandleScopeCollection handles(soa.Self()); - EXPECT_EQ(graph->TryBuildingSsa(&handles), kBuildSsaSuccess); -} - } // namespace art #endif // ART_COMPILER_OPTIMIZING_OPTIMIZING_UNIT_TEST_H_ diff --git a/compiler/optimizing/pc_relative_fixups_x86.cc b/compiler/optimizing/pc_relative_fixups_x86.cc index a385448104..b383f1e1ad 100644 --- a/compiler/optimizing/pc_relative_fixups_x86.cc +++ b/compiler/optimizing/pc_relative_fixups_x86.cc @@ -15,7 +15,6 @@ */ #include "pc_relative_fixups_x86.h" -#include "code_generator_x86.h" namespace art { namespace x86 { @@ -80,10 +79,6 @@ class PCRelativeHandlerVisitor : public HGraphVisitor { } void VisitPackedSwitch(HPackedSwitch* switch_insn) OVERRIDE { - if (switch_insn->GetNumEntries() <= - InstructionCodeGeneratorX86::kPackedSwitchJumpTableThreshold) { - return; - } // We need to replace the HPackedSwitch with a HX86PackedSwitch in order to // address the constant area. InitializePCRelativeBasePointer(); diff --git a/compiler/optimizing/primitive_type_propagation.cc b/compiler/optimizing/primitive_type_propagation.cc new file mode 100644 index 0000000000..bde54ee977 --- /dev/null +++ b/compiler/optimizing/primitive_type_propagation.cc @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "primitive_type_propagation.h" + +#include "nodes.h" +#include "ssa_builder.h" + +namespace art { + +static Primitive::Type MergeTypes(Primitive::Type existing, Primitive::Type new_type) { + // We trust the verifier has already done the necessary checking. + switch (existing) { + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + case Primitive::kPrimNot: + return existing; + default: + // Phis are initialized with a void type, so if we are asked + // to merge with a void type, we should use the existing one. + return new_type == Primitive::kPrimVoid + ? existing + : HPhi::ToPhiType(new_type); + } +} + +// Re-compute and update the type of the instruction. Returns +// whether or not the type was changed. +bool PrimitiveTypePropagation::UpdateType(HPhi* phi) { + DCHECK(phi->IsLive()); + Primitive::Type existing = phi->GetType(); + + Primitive::Type new_type = existing; + for (size_t i = 0, e = phi->InputCount(); i < e; ++i) { + Primitive::Type input_type = phi->InputAt(i)->GetType(); + new_type = MergeTypes(new_type, input_type); + } + phi->SetType(new_type); + + if (new_type == Primitive::kPrimDouble + || new_type == Primitive::kPrimFloat + || new_type == Primitive::kPrimNot) { + // If the phi is of floating point type, we need to update its inputs to that + // type. For inputs that are phis, we need to recompute their types. + for (size_t i = 0, e = phi->InputCount(); i < e; ++i) { + HInstruction* input = phi->InputAt(i); + if (input->GetType() != new_type) { + HInstruction* equivalent = (new_type == Primitive::kPrimNot) + ? SsaBuilder::GetReferenceTypeEquivalent(input) + : SsaBuilder::GetFloatOrDoubleEquivalent(phi, input, new_type); + phi->ReplaceInput(equivalent, i); + if (equivalent->IsPhi()) { + AddToWorklist(equivalent->AsPhi()); + } else if (equivalent == input) { + // The input has changed its type. It can be an input of other phis, + // so we need to put phi users in the work list. + AddDependentInstructionsToWorklist(equivalent); + } + } + } + } + + return existing != new_type; +} + +void PrimitiveTypePropagation::Run() { + for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) { + VisitBasicBlock(it.Current()); + } + ProcessWorklist(); +} + +void PrimitiveTypePropagation::VisitBasicBlock(HBasicBlock* block) { + if (block->IsLoopHeader()) { + for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) { + HPhi* phi = it.Current()->AsPhi(); + if (phi->IsLive()) { + AddToWorklist(phi); + } + } + } else { + for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) { + // Eagerly compute the type of the phi, for quicker convergence. Note + // that we don't need to add users to the worklist because we are + // doing a reverse post-order visit, therefore either the phi users are + // non-loop phi and will be visited later in the visit, or are loop-phis, + // and they are already in the work list. + HPhi* phi = it.Current()->AsPhi(); + if (phi->IsLive()) { + UpdateType(phi); + } + } + } +} + +void PrimitiveTypePropagation::ProcessWorklist() { + while (!worklist_.empty()) { + HPhi* instruction = worklist_.back(); + worklist_.pop_back(); + if (UpdateType(instruction)) { + AddDependentInstructionsToWorklist(instruction); + } + } +} + +void PrimitiveTypePropagation::AddToWorklist(HPhi* instruction) { + DCHECK(instruction->IsLive()); + worklist_.push_back(instruction); +} + +void PrimitiveTypePropagation::AddDependentInstructionsToWorklist(HInstruction* instruction) { + for (HUseIterator<HInstruction*> it(instruction->GetUses()); !it.Done(); it.Advance()) { + HPhi* phi = it.Current()->GetUser()->AsPhi(); + if (phi != nullptr && phi->IsLive() && phi->GetType() != instruction->GetType()) { + AddToWorklist(phi); + } + } +} + +} // namespace art diff --git a/compiler/optimizing/primitive_type_propagation.h b/compiler/optimizing/primitive_type_propagation.h new file mode 100644 index 0000000000..212fcfc69f --- /dev/null +++ b/compiler/optimizing/primitive_type_propagation.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_COMPILER_OPTIMIZING_PRIMITIVE_TYPE_PROPAGATION_H_ +#define ART_COMPILER_OPTIMIZING_PRIMITIVE_TYPE_PROPAGATION_H_ + +#include "base/arena_containers.h" +#include "nodes.h" + +namespace art { + +// Compute and propagate primitive types of phis in the graph. +class PrimitiveTypePropagation : public ValueObject { + public: + explicit PrimitiveTypePropagation(HGraph* graph) + : graph_(graph), worklist_(graph->GetArena()->Adapter(kArenaAllocPrimitiveTypePropagation)) { + worklist_.reserve(kDefaultWorklistSize); + } + + void Run(); + + private: + void VisitBasicBlock(HBasicBlock* block); + void ProcessWorklist(); + void AddToWorklist(HPhi* phi); + void AddDependentInstructionsToWorklist(HInstruction* instruction); + bool UpdateType(HPhi* phi); + + HGraph* const graph_; + ArenaVector<HPhi*> worklist_; + + static constexpr size_t kDefaultWorklistSize = 8; + + DISALLOW_COPY_AND_ASSIGN(PrimitiveTypePropagation); +}; + +} // namespace art + +#endif // ART_COMPILER_OPTIMIZING_PRIMITIVE_TYPE_PROPAGATION_H_ diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc index 94a297c9e6..fea903d9cf 100644 --- a/compiler/optimizing/reference_type_propagation.cc +++ b/compiler/optimizing/reference_type_propagation.cc @@ -40,6 +40,7 @@ class RTPVisitor : public HGraphDelegateVisitor { throwable_class_handle_(throwable_class_handle), worklist_(worklist) {} + void VisitNullConstant(HNullConstant* null_constant) OVERRIDE; void VisitNewInstance(HNewInstance* new_instance) OVERRIDE; void VisitLoadClass(HLoadClass* load_class) OVERRIDE; void VisitClinitCheck(HClinitCheck* clinit_check) OVERRIDE; @@ -70,6 +71,8 @@ class RTPVisitor : public HGraphDelegateVisitor { ReferenceTypeInfo::TypeHandle string_class_handle_; ReferenceTypeInfo::TypeHandle throwable_class_handle_; ArenaVector<HInstruction*>* worklist_; + + static constexpr size_t kDefaultWorklistSize = 8; }; ReferenceTypePropagation::ReferenceTypePropagation(HGraph* graph, @@ -168,13 +171,9 @@ static void ForEachUntypedInstruction(HGraph* graph, Functor fn) { ScopedObjectAccess soa(Thread::Current()); for (HReversePostOrderIterator block_it(*graph); !block_it.Done(); block_it.Advance()) { for (HInstructionIterator it(block_it.Current()->GetPhis()); !it.Done(); it.Advance()) { - HPhi* phi = it.Current()->AsPhi(); - // Note that the graph may contain dead phis when run from the SsaBuilder. - // Skip those as they might have a type conflict and will be removed anyway. - if (phi->IsLive() && - phi->GetType() == Primitive::kPrimNot && - !phi->GetReferenceTypeInfo().IsValid()) { - fn(phi); + HInstruction* instr = it.Current(); + if (instr->GetType() == Primitive::kPrimNot && !instr->GetReferenceTypeInfo().IsValid()) { + fn(instr); } } for (HInstructionIterator it(block_it.Current()->GetInstructions()); !it.Done(); it.Advance()) { @@ -377,75 +376,6 @@ void ReferenceTypePropagation::BoundTypeForIfNotNull(HBasicBlock* block) { } } -// Returns true if one of the patterns below has been recognized. If so, the -// InstanceOf instruction together with the true branch of `ifInstruction` will -// be returned using the out parameters. -// Recognized patterns: -// (1) patterns equivalent to `if (obj instanceof X)` -// (a) InstanceOf -> Equal to 1 -> If -// (b) InstanceOf -> NotEqual to 0 -> If -// (c) InstanceOf -> If -// (2) patterns equivalent to `if (!(obj instanceof X))` -// (a) InstanceOf -> Equal to 0 -> If -// (b) InstanceOf -> NotEqual to 1 -> If -// (c) InstanceOf -> BooleanNot -> If -static bool MatchIfInstanceOf(HIf* ifInstruction, - /* out */ HInstanceOf** instanceOf, - /* out */ HBasicBlock** trueBranch) { - HInstruction* input = ifInstruction->InputAt(0); - - if (input->IsEqual()) { - HInstruction* rhs = input->AsEqual()->GetConstantRight(); - if (rhs != nullptr) { - HInstruction* lhs = input->AsEqual()->GetLeastConstantLeft(); - if (lhs->IsInstanceOf() && rhs->IsIntConstant()) { - if (rhs->AsIntConstant()->IsOne()) { - // Case (1a) - *trueBranch = ifInstruction->IfTrueSuccessor(); - } else { - // Case (2a) - DCHECK(rhs->AsIntConstant()->IsZero()); - *trueBranch = ifInstruction->IfFalseSuccessor(); - } - *instanceOf = lhs->AsInstanceOf(); - return true; - } - } - } else if (input->IsNotEqual()) { - HInstruction* rhs = input->AsNotEqual()->GetConstantRight(); - if (rhs != nullptr) { - HInstruction* lhs = input->AsNotEqual()->GetLeastConstantLeft(); - if (lhs->IsInstanceOf() && rhs->IsIntConstant()) { - if (rhs->AsIntConstant()->IsZero()) { - // Case (1b) - *trueBranch = ifInstruction->IfTrueSuccessor(); - } else { - // Case (2b) - DCHECK(rhs->AsIntConstant()->IsOne()); - *trueBranch = ifInstruction->IfFalseSuccessor(); - } - *instanceOf = lhs->AsInstanceOf(); - return true; - } - } - } else if (input->IsInstanceOf()) { - // Case (1c) - *instanceOf = input->AsInstanceOf(); - *trueBranch = ifInstruction->IfTrueSuccessor(); - return true; - } else if (input->IsBooleanNot()) { - HInstruction* not_input = input->InputAt(0); - if (not_input->IsInstanceOf()) { - // Case (2c) - *instanceOf = not_input->AsInstanceOf(); - *trueBranch = ifInstruction->IfFalseSuccessor(); - return true; - } - } - - return false; -} - // Detects if `block` is the True block for the pattern // `if (x instanceof ClassX) { }` // If that's the case insert an HBoundType instruction to bound the type of `x` @@ -455,11 +385,22 @@ void ReferenceTypePropagation::BoundTypeForIfInstanceOf(HBasicBlock* block) { if (ifInstruction == nullptr) { return; } - - // Try to recognize common `if (instanceof)` and `if (!instanceof)` patterns. - HInstanceOf* instanceOf = nullptr; + HInstruction* ifInput = ifInstruction->InputAt(0); + HInstruction* instanceOf = nullptr; HBasicBlock* instanceOfTrueBlock = nullptr; - if (!MatchIfInstanceOf(ifInstruction, &instanceOf, &instanceOfTrueBlock)) { + + // The instruction simplifier has transformed: + // - `if (a instanceof A)` into an HIf with an HInstanceOf input + // - `if (!(a instanceof A)` into an HIf with an HBooleanNot input (which in turn + // has an HInstanceOf input) + // So we should not see the usual HEqual here. + if (ifInput->IsInstanceOf()) { + instanceOf = ifInput; + instanceOfTrueBlock = ifInstruction->IfTrueSuccessor(); + } else if (ifInput->IsBooleanNot() && ifInput->InputAt(0)->IsInstanceOf()) { + instanceOf = ifInput->InputAt(0); + instanceOfTrueBlock = ifInstruction->IfFalseSuccessor(); + } else { return; } @@ -564,6 +505,13 @@ void RTPVisitor::UpdateReferenceTypeInfo(HInstruction* instr, SetClassAsTypeInfo(instr, dex_cache->GetResolvedType(type_idx), is_exact); } +void RTPVisitor::VisitNullConstant(HNullConstant* instr) { + // TODO: The null constant could be bound contextually (e.g. based on return statements) + // to a more precise type. + instr->SetReferenceTypeInfo( + ReferenceTypeInfo::Create(object_class_handle_, /* is_exact */ false)); +} + void RTPVisitor::VisitNewInstance(HNewInstance* instr) { UpdateReferenceTypeInfo(instr, instr->GetTypeIndex(), instr->GetDexFile(), /* is_exact */ true); } @@ -575,11 +523,7 @@ void RTPVisitor::VisitNewArray(HNewArray* instr) { static mirror::Class* GetClassFromDexCache(Thread* self, const DexFile& dex_file, uint16_t type_idx) SHARED_REQUIRES(Locks::mutator_lock_) { mirror::DexCache* dex_cache = - Runtime::Current()->GetClassLinker()->FindDexCache(self, dex_file, /* allow_failure */ true); - if (dex_cache == nullptr) { - // Dex cache could not be found. This should only happen during gtests. - return nullptr; - } + Runtime::Current()->GetClassLinker()->FindDexCache(self, dex_file, false); // Get type from dex cache assuming it was populated by the verifier. return dex_cache->GetResolvedType(type_idx); } @@ -596,24 +540,17 @@ void RTPVisitor::VisitParameterValue(HParameterValue* instr) { void RTPVisitor::UpdateFieldAccessTypeInfo(HInstruction* instr, const FieldInfo& info) { - if (instr->GetType() != Primitive::kPrimNot) { + // The field index is unknown only during tests. + if (instr->GetType() != Primitive::kPrimNot || info.GetFieldIndex() == kUnknownFieldIndex) { return; } ScopedObjectAccess soa(Thread::Current()); - mirror::Class* klass = nullptr; - - // The field index is unknown only during tests. - if (info.GetFieldIndex() != kUnknownFieldIndex) { - ClassLinker* cl = Runtime::Current()->GetClassLinker(); - ArtField* field = cl->GetResolvedField(info.GetFieldIndex(), info.GetDexCache().Get()); - // TODO: There are certain cases where we can't resolve the field. - // b/21914925 is open to keep track of a repro case for this issue. - if (field != nullptr) { - klass = field->GetType<false>(); - } - } - + ClassLinker* cl = Runtime::Current()->GetClassLinker(); + ArtField* field = cl->GetResolvedField(info.GetFieldIndex(), info.GetDexCache().Get()); + // TODO: There are certain cases where we can't resolve the field. + // b/21914925 is open to keep track of a repro case for this issue. + mirror::Class* klass = (field == nullptr) ? nullptr : field->GetType<false>(); SetClassAsTypeInfo(instr, klass, /* is_exact */ false); } @@ -729,7 +666,7 @@ void RTPVisitor::VisitCheckCast(HCheckCast* check_cast) { } void ReferenceTypePropagation::VisitPhi(HPhi* phi) { - if (phi->IsDead() || phi->GetType() != Primitive::kPrimNot) { + if (phi->GetType() != Primitive::kPrimNot) { return; } @@ -887,8 +824,6 @@ void ReferenceTypePropagation::UpdateBoundType(HBoundType* instr) { // NullConstant inputs are ignored during merging as they do not provide any useful information. // If all the inputs are NullConstants then the type of the phi will be set to Object. void ReferenceTypePropagation::UpdatePhi(HPhi* instr) { - DCHECK(instr->IsLive()); - size_t input_count = instr->InputCount(); size_t first_input_index_not_null = 0; while (first_input_index_not_null < input_count && @@ -933,7 +868,7 @@ void ReferenceTypePropagation::UpdatePhi(HPhi* instr) { // Re-computes and updates the nullability of the instruction. Returns whether or // not the nullability was changed. bool ReferenceTypePropagation::UpdateNullability(HInstruction* instr) { - DCHECK((instr->IsPhi() && instr->AsPhi()->IsLive()) + DCHECK(instr->IsPhi() || instr->IsBoundType() || instr->IsNullCheck() || instr->IsArrayGet()); @@ -981,7 +916,7 @@ void ReferenceTypePropagation::AddToWorklist(HInstruction* instruction) { void ReferenceTypePropagation::AddDependentInstructionsToWorklist(HInstruction* instruction) { for (HUseIterator<HInstruction*> it(instruction->GetUses()); !it.Done(); it.Advance()) { HInstruction* user = it.Current()->GetUser(); - if ((user->IsPhi() && user->AsPhi()->IsLive()) + if (user->IsPhi() || user->IsBoundType() || user->IsNullCheck() || (user->IsArrayGet() && (user->GetType() == Primitive::kPrimNot))) { diff --git a/compiler/optimizing/register_allocator_test.cc b/compiler/optimizing/register_allocator_test.cc index b900ed0966..080f970756 100644 --- a/compiler/optimizing/register_allocator_test.cc +++ b/compiler/optimizing/register_allocator_test.cc @@ -28,13 +28,13 @@ #include "ssa_liveness_analysis.h" #include "ssa_phi_elimination.h" +#include "gtest/gtest.h" + namespace art { // Note: the register allocator tests rely on the fact that constants have live // intervals and registers get allocated to them. -class RegisterAllocatorTest : public CommonCompilerTest {}; - static bool Check(const uint16_t* data) { ArenaPool pool; ArenaAllocator allocator(&pool); @@ -42,7 +42,7 @@ static bool Check(const uint16_t* data) { HGraphBuilder builder(graph); const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data); builder.BuildGraph(*item); - TransformToSsa(graph); + graph->TryBuildingSsa(); std::unique_ptr<const X86InstructionSetFeatures> features_x86( X86InstructionSetFeatures::FromCppDefines()); x86::CodeGeneratorX86 codegen(graph, *features_x86.get(), CompilerOptions()); @@ -57,7 +57,7 @@ static bool Check(const uint16_t* data) { * Unit testing of RegisterAllocator::ValidateIntervals. Register allocator * tests are based on this validation method. */ -TEST_F(RegisterAllocatorTest, ValidateIntervals) { +TEST(RegisterAllocatorTest, ValidateIntervals) { ArenaPool pool; ArenaAllocator allocator(&pool); HGraph* graph = CreateGraph(&allocator); @@ -146,7 +146,7 @@ TEST_F(RegisterAllocatorTest, ValidateIntervals) { } } -TEST_F(RegisterAllocatorTest, CFG1) { +TEST(RegisterAllocatorTest, CFG1) { /* * Test the following snippet: * return 0; @@ -166,7 +166,7 @@ TEST_F(RegisterAllocatorTest, CFG1) { ASSERT_TRUE(Check(data)); } -TEST_F(RegisterAllocatorTest, Loop1) { +TEST(RegisterAllocatorTest, Loop1) { /* * Test the following snippet: * int a = 0; @@ -205,7 +205,7 @@ TEST_F(RegisterAllocatorTest, Loop1) { ASSERT_TRUE(Check(data)); } -TEST_F(RegisterAllocatorTest, Loop2) { +TEST(RegisterAllocatorTest, Loop2) { /* * Test the following snippet: * int a = 0; @@ -259,11 +259,11 @@ static HGraph* BuildSSAGraph(const uint16_t* data, ArenaAllocator* allocator) { HGraphBuilder builder(graph); const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data); builder.BuildGraph(*item); - TransformToSsa(graph); + graph->TryBuildingSsa(); return graph; } -TEST_F(RegisterAllocatorTest, Loop3) { +TEST(RegisterAllocatorTest, Loop3) { /* * Test the following snippet: * int a = 0 @@ -326,7 +326,7 @@ TEST_F(RegisterAllocatorTest, Loop3) { ASSERT_EQ(phi_interval->GetRegister(), ret->InputAt(0)->GetLiveInterval()->GetRegister()); } -TEST_F(RegisterAllocatorTest, FirstRegisterUse) { +TEST(RegisterAllocatorTest, FirstRegisterUse) { const uint16_t data[] = THREE_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::XOR_INT_LIT8 | 1 << 8, 1 << 8, @@ -366,7 +366,7 @@ TEST_F(RegisterAllocatorTest, FirstRegisterUse) { ASSERT_EQ(new_interval->FirstRegisterUse(), last_xor->GetLifetimePosition()); } -TEST_F(RegisterAllocatorTest, DeadPhi) { +TEST(RegisterAllocatorTest, DeadPhi) { /* Test for a dead loop phi taking as back-edge input a phi that also has * this loop phi as input. Walking backwards in SsaDeadPhiElimination * does not solve the problem because the loop phi will be visited last. @@ -407,7 +407,7 @@ TEST_F(RegisterAllocatorTest, DeadPhi) { * that share the same register. It should split the interval it is currently * allocating for at the minimum lifetime position between the two inactive intervals. */ -TEST_F(RegisterAllocatorTest, FreeUntil) { +TEST(RegisterAllocatorTest, FreeUntil) { const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::RETURN); @@ -539,7 +539,7 @@ static HGraph* BuildIfElseWithPhi(ArenaAllocator* allocator, return graph; } -TEST_F(RegisterAllocatorTest, PhiHint) { +TEST(RegisterAllocatorTest, PhiHint) { ArenaPool pool; ArenaAllocator allocator(&pool); HPhi *phi; @@ -658,7 +658,7 @@ static HGraph* BuildFieldReturn(ArenaAllocator* allocator, return graph; } -TEST_F(RegisterAllocatorTest, ExpectedInRegisterHint) { +TEST(RegisterAllocatorTest, ExpectedInRegisterHint) { ArenaPool pool; ArenaAllocator allocator(&pool); HInstruction *field, *ret; @@ -726,7 +726,7 @@ static HGraph* BuildTwoSubs(ArenaAllocator* allocator, return graph; } -TEST_F(RegisterAllocatorTest, SameAsFirstInputHint) { +TEST(RegisterAllocatorTest, SameAsFirstInputHint) { ArenaPool pool; ArenaAllocator allocator(&pool); HInstruction *first_sub, *second_sub; @@ -795,7 +795,7 @@ static HGraph* BuildDiv(ArenaAllocator* allocator, return graph; } -TEST_F(RegisterAllocatorTest, ExpectedExactInRegisterAndSameOutputHint) { +TEST(RegisterAllocatorTest, ExpectedExactInRegisterAndSameOutputHint) { ArenaPool pool; ArenaAllocator allocator(&pool); HInstruction *div; @@ -819,7 +819,7 @@ TEST_F(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_F(RegisterAllocatorTest, SpillInactive) { +TEST(RegisterAllocatorTest, SpillInactive) { ArenaPool pool; // Create a synthesized graph to please the register_allocator and diff --git a/compiler/optimizing/ssa_builder.cc b/compiler/optimizing/ssa_builder.cc index 9e869e18e9..9e6cfbe653 100644 --- a/compiler/optimizing/ssa_builder.cc +++ b/compiler/optimizing/ssa_builder.cc @@ -17,11 +17,214 @@ #include "ssa_builder.h" #include "nodes.h" -#include "reference_type_propagation.h" +#include "primitive_type_propagation.h" #include "ssa_phi_elimination.h" namespace art { +// Returns whether this is a loop header phi which was eagerly created but later +// found inconsistent due to the vreg being undefined in one of its predecessors. +// Such phi is marked dead and should be ignored until its removal in SsaPhiElimination. +static bool IsUndefinedLoopHeaderPhi(HPhi* phi) { + return phi->IsLoopHeaderPhi() && phi->InputCount() != phi->GetBlock()->GetPredecessors().size(); +} + +/** + * A debuggable application may require to reviving phis, to ensure their + * associated DEX register is available to a debugger. This class implements + * the logic for statement (c) of the SsaBuilder (see ssa_builder.h). It + * also makes sure that phis with incompatible input types are not revived + * (statement (b) of the SsaBuilder). + * + * This phase must be run after detecting dead phis through the + * DeadPhiElimination phase, and before deleting the dead phis. + */ +class DeadPhiHandling : public ValueObject { + public: + explicit DeadPhiHandling(HGraph* graph) + : graph_(graph), worklist_(graph->GetArena()->Adapter(kArenaAllocSsaBuilder)) { + worklist_.reserve(kDefaultWorklistSize); + } + + void Run(); + + private: + void VisitBasicBlock(HBasicBlock* block); + void ProcessWorklist(); + void AddToWorklist(HPhi* phi); + void AddDependentInstructionsToWorklist(HPhi* phi); + bool UpdateType(HPhi* phi); + + HGraph* const graph_; + ArenaVector<HPhi*> worklist_; + + static constexpr size_t kDefaultWorklistSize = 8; + + DISALLOW_COPY_AND_ASSIGN(DeadPhiHandling); +}; + +static bool HasConflictingEquivalent(HPhi* phi) { + if (phi->GetNext() == nullptr) { + return false; + } + HPhi* next = phi->GetNext()->AsPhi(); + if (next->GetRegNumber() == phi->GetRegNumber()) { + if (next->GetType() == Primitive::kPrimVoid) { + // We only get a void type for an equivalent phi we processed and found out + // it was conflicting. + return true; + } else { + // Go to the next phi, in case it is also an equivalent. + return HasConflictingEquivalent(next); + } + } + return false; +} + +bool DeadPhiHandling::UpdateType(HPhi* phi) { + if (phi->IsDead()) { + // Phi was rendered dead while waiting in the worklist because it was replaced + // with an equivalent. + return false; + } + + Primitive::Type existing = phi->GetType(); + + bool conflict = false; + Primitive::Type new_type = existing; + for (size_t i = 0, e = phi->InputCount(); i < e; ++i) { + HInstruction* input = phi->InputAt(i); + if (input->IsPhi() && input->AsPhi()->IsDead()) { + // We are doing a reverse post order visit of the graph, reviving + // phis that have environment uses and updating their types. If an + // input is a phi, and it is dead (because its input types are + // conflicting), this phi must be marked dead as well. + conflict = true; + break; + } + Primitive::Type input_type = HPhi::ToPhiType(input->GetType()); + + // The only acceptable transitions are: + // - From void to typed: first time we update the type of this phi. + // - From int to reference (or reference to int): the phi has to change + // to reference type. If the integer input cannot be converted to a + // reference input, the phi will remain dead. + if (new_type == Primitive::kPrimVoid) { + new_type = input_type; + } else if (new_type == Primitive::kPrimNot && input_type == Primitive::kPrimInt) { + if (input->IsPhi() && HasConflictingEquivalent(input->AsPhi())) { + // If we already asked for an equivalent of the input phi, but that equivalent + // ended up conflicting, make this phi conflicting too. + conflict = true; + break; + } + HInstruction* equivalent = SsaBuilder::GetReferenceTypeEquivalent(input); + if (equivalent == nullptr) { + conflict = true; + break; + } + phi->ReplaceInput(equivalent, i); + if (equivalent->IsPhi()) { + DCHECK_EQ(equivalent->GetType(), Primitive::kPrimNot); + // We created a new phi, but that phi has the same inputs as the old phi. We + // add it to the worklist to ensure its inputs can also be converted to reference. + // If not, it will remain dead, and the algorithm will make the current phi dead + // as well. + equivalent->AsPhi()->SetLive(); + AddToWorklist(equivalent->AsPhi()); + } + } else if (new_type == Primitive::kPrimInt && input_type == Primitive::kPrimNot) { + new_type = Primitive::kPrimNot; + // Start over, we may request reference equivalents for the inputs of the phi. + i = -1; + } else if (new_type != input_type) { + conflict = true; + break; + } + } + + if (conflict) { + phi->SetType(Primitive::kPrimVoid); + phi->SetDead(); + return true; + } else if (existing == new_type) { + return false; + } + + DCHECK(phi->IsLive()); + phi->SetType(new_type); + + // There might exist a `new_type` equivalent of `phi` already. In that case, + // we replace the equivalent with the, now live, `phi`. + HPhi* equivalent = phi->GetNextEquivalentPhiWithSameType(); + if (equivalent != nullptr) { + // There cannot be more than two equivalents with the same type. + DCHECK(equivalent->GetNextEquivalentPhiWithSameType() == nullptr); + // If doing fix-point iteration, the equivalent might be in `worklist_`. + // Setting it dead will make UpdateType skip it. + equivalent->SetDead(); + equivalent->ReplaceWith(phi); + } + + return true; +} + +void DeadPhiHandling::VisitBasicBlock(HBasicBlock* block) { + for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) { + HPhi* phi = it.Current()->AsPhi(); + if (IsUndefinedLoopHeaderPhi(phi)) { + DCHECK(phi->IsDead()); + continue; + } + if (phi->IsDead() && phi->HasEnvironmentUses()) { + phi->SetLive(); + if (block->IsLoopHeader()) { + // Loop phis must have a type to guarantee convergence of the algorithm. + DCHECK_NE(phi->GetType(), Primitive::kPrimVoid); + AddToWorklist(phi); + } else { + // Because we are doing a reverse post order visit, all inputs of + // this phi have been visited and therefore had their (initial) type set. + UpdateType(phi); + } + } + } +} + +void DeadPhiHandling::ProcessWorklist() { + while (!worklist_.empty()) { + HPhi* instruction = worklist_.back(); + worklist_.pop_back(); + // Note that the same equivalent phi can be added multiple times in the work list, if + // used by multiple phis. The first call to `UpdateType` will know whether the phi is + // dead or live. + if (instruction->IsLive() && UpdateType(instruction)) { + AddDependentInstructionsToWorklist(instruction); + } + } +} + +void DeadPhiHandling::AddToWorklist(HPhi* instruction) { + DCHECK(instruction->IsLive()); + worklist_.push_back(instruction); +} + +void DeadPhiHandling::AddDependentInstructionsToWorklist(HPhi* instruction) { + for (HUseIterator<HInstruction*> it(instruction->GetUses()); !it.Done(); it.Advance()) { + HPhi* phi = it.Current()->GetUser()->AsPhi(); + if (phi != nullptr && !phi->IsDead()) { + AddToWorklist(phi); + } + } +} + +void DeadPhiHandling::Run() { + for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) { + VisitBasicBlock(it.Current()); + } + ProcessWorklist(); +} + void SsaBuilder::SetLoopHeaderPhiInputs() { for (size_t i = loop_headers_.size(); i > 0; --i) { HBasicBlock* block = loop_headers_[i - 1]; @@ -82,11 +285,10 @@ void SsaBuilder::EquivalentPhisCleanup() { HPhi* phi = it.Current()->AsPhi(); HPhi* next = phi->GetNextEquivalentPhiWithSameType(); if (next != nullptr) { - // Make sure we do not replace a live phi with a dead phi. A live phi - // has been handled by the type propagation phase, unlike a dead phi. + // Make sure we do not replace a live phi with a dead phi. A live phi has been + // handled by the type propagation phase, unlike a dead phi. if (next->IsLive()) { phi->ReplaceWith(next); - phi->SetDead(); } else { next->ReplaceWith(phi); } @@ -98,7 +300,64 @@ void SsaBuilder::EquivalentPhisCleanup() { } } -void SsaBuilder::FixEnvironmentPhis() { +void SsaBuilder::BuildSsa() { + // 1) Visit in reverse post order. We need to have all predecessors of a block visited + // (with the exception of loops) in order to create the right environment for that + // block. For loops, we create phis whose inputs will be set in 2). + for (HReversePostOrderIterator it(*GetGraph()); !it.Done(); it.Advance()) { + VisitBasicBlock(it.Current()); + } + + // 2) Set inputs of loop phis. + SetLoopHeaderPhiInputs(); + + // 3) Mark dead phis. This will mark phis that are only used by environments: + // at the DEX level, the type of these phis does not need to be consistent, but + // our code generator will complain if the inputs of a phi do not have the same + // type. The marking allows the type propagation to know which phis it needs + // to handle. We mark but do not eliminate: the elimination will be done in + // step 9). + SsaDeadPhiElimination dead_phis_for_type_propagation(GetGraph()); + dead_phis_for_type_propagation.MarkDeadPhis(); + + // 4) Propagate types of phis. At this point, phis are typed void in the general + // case, or float/double/reference when we created an equivalent phi. So we + // need to propagate the types across phis to give them a correct type. + PrimitiveTypePropagation type_propagation(GetGraph()); + type_propagation.Run(); + + // 5) When creating equivalent phis we copy the inputs of the original phi which + // may be improperly typed. This was fixed during the type propagation in 4) but + // as a result we may end up with two equivalent phis with the same type for + // the same dex register. This pass cleans them up. + EquivalentPhisCleanup(); + + // 6) Mark dead phis again. Step 4) may have introduced new phis. + // Step 5) might enable the death of new phis. + SsaDeadPhiElimination dead_phis(GetGraph()); + dead_phis.MarkDeadPhis(); + + // 7) Now that the graph is correctly typed, we can get rid of redundant phis. + // Note that we cannot do this phase before type propagation, otherwise + // we could get rid of phi equivalents, whose presence is a requirement for the + // type propagation phase. Note that this is to satisfy statement (a) of the + // SsaBuilder (see ssa_builder.h). + SsaRedundantPhiElimination redundant_phi(GetGraph()); + redundant_phi.Run(); + + // 8) Fix the type for null constants which are part of an equality comparison. + // We need to do this after redundant phi elimination, to ensure the only cases + // that we can see are reference comparison against 0. The redundant phi + // elimination ensures we do not see a phi taking two 0 constants in a HEqual + // or HNotEqual. + FixNullConstantType(); + + // 9) Make sure environments use the right phi "equivalent": a phi marked dead + // can have a phi equivalent that is not dead. We must therefore update + // all environment uses of the dead phi to use its equivalent. Note that there + // can be multiple phis for the same Dex register that are live (for example + // when merging constants), in which case it is OK for the environments + // to just reference one. for (HReversePostOrderIterator it(*GetGraph()); !it.Done(); it.Advance()) { HBasicBlock* block = it.Current(); for (HInstructionIterator it_phis(block->GetPhis()); !it_phis.Done(); it_phis.Advance()) { @@ -119,345 +378,24 @@ void SsaBuilder::FixEnvironmentPhis() { phi->ReplaceWith(next); } } -} -static void AddDependentInstructionsToWorklist(HInstruction* instruction, - ArenaVector<HPhi*>* worklist) { - // If `instruction` is a dead phi, type conflict was just identified. All its - // live phi users, and transitively users of those users, therefore need to be - // marked dead/conflicting too, so we add them to the worklist. Otherwise we - // add users whose type does not match and needs to be updated. - bool add_all_live_phis = instruction->IsPhi() && instruction->AsPhi()->IsDead(); - for (HUseIterator<HInstruction*> it(instruction->GetUses()); !it.Done(); it.Advance()) { - HInstruction* user = it.Current()->GetUser(); - if (user->IsPhi() && user->AsPhi()->IsLive()) { - if (add_all_live_phis || user->GetType() != instruction->GetType()) { - worklist->push_back(user->AsPhi()); - } - } + // 10) Deal with phis to guarantee liveness of phis in case of a debuggable + // application. This is for satisfying statement (c) of the SsaBuilder + // (see ssa_builder.h). + if (GetGraph()->IsDebuggable()) { + DeadPhiHandling dead_phi_handler(GetGraph()); + dead_phi_handler.Run(); } -} - -// Find a candidate primitive type for `phi` by merging the type of its inputs. -// Return false if conflict is identified. -static bool TypePhiFromInputs(HPhi* phi) { - Primitive::Type common_type = phi->GetType(); - for (HInputIterator it(phi); !it.Done(); it.Advance()) { - HInstruction* input = it.Current(); - if (input->IsPhi() && input->AsPhi()->IsDead()) { - // Phis are constructed live so if an input is a dead phi, it must have - // been made dead due to type conflict. Mark this phi conflicting too. - return false; - } - - Primitive::Type input_type = HPhi::ToPhiType(input->GetType()); - if (common_type == input_type) { - // No change in type. - } else if (Primitive::ComponentSize(common_type) != Primitive::ComponentSize(input_type)) { - // Types are of different sizes, e.g. int vs. long. Must be a conflict. - return false; - } else if (Primitive::IsIntegralType(common_type)) { - // Previous inputs were integral, this one is not but is of the same size. - // This does not imply conflict since some bytecode instruction types are - // ambiguous. TypeInputsOfPhi will either type them or detect a conflict. - DCHECK(Primitive::IsFloatingPointType(input_type) || input_type == Primitive::kPrimNot); - common_type = input_type; - } else if (Primitive::IsIntegralType(input_type)) { - // Input is integral, common type is not. Same as in the previous case, if - // there is a conflict, it will be detected during TypeInputsOfPhi. - DCHECK(Primitive::IsFloatingPointType(common_type) || common_type == Primitive::kPrimNot); - } else { - // Combining float and reference types. Clearly a conflict. - DCHECK((common_type == Primitive::kPrimFloat && input_type == Primitive::kPrimNot) || - (common_type == Primitive::kPrimNot && input_type == Primitive::kPrimFloat)); - return false; - } - } - - // We have found a candidate type for the phi. Set it and return true. We may - // still discover conflict whilst typing the individual inputs in TypeInputsOfPhi. - phi->SetType(common_type); - return true; -} - -// Replace inputs of `phi` to match its type. Return false if conflict is identified. -bool SsaBuilder::TypeInputsOfPhi(HPhi* phi, ArenaVector<HPhi*>* worklist) { - Primitive::Type common_type = phi->GetType(); - if (common_type == Primitive::kPrimVoid || Primitive::IsIntegralType(common_type)) { - // Phi either contains only other untyped phis (common_type == kPrimVoid), - // or `common_type` is integral and we do not need to retype ambiguous inputs - // because they are always constructed with the integral type candidate. - if (kIsDebugBuild) { - for (size_t i = 0, e = phi->InputCount(); i < e; ++i) { - HInstruction* input = phi->InputAt(i); - if (common_type == Primitive::kPrimVoid) { - DCHECK(input->IsPhi() && input->GetType() == Primitive::kPrimVoid); - } else { - DCHECK((input->IsPhi() && input->GetType() == Primitive::kPrimVoid) || - HPhi::ToPhiType(input->GetType()) == common_type); - } - } - } - // Inputs did not need to be replaced, hence no conflict. Report success. - return true; - } else { - DCHECK(common_type == Primitive::kPrimNot || Primitive::IsFloatingPointType(common_type)); - for (size_t i = 0, e = phi->InputCount(); i < e; ++i) { - HInstruction* input = phi->InputAt(i); - if (input->GetType() != common_type) { - // Input type does not match phi's type. Try to retype the input or - // generate a suitably typed equivalent. - HInstruction* equivalent = (common_type == Primitive::kPrimNot) - ? GetReferenceTypeEquivalent(input) - : GetFloatOrDoubleEquivalent(input, common_type); - if (equivalent == nullptr) { - // Input could not be typed. Report conflict. - return false; - } - // Make sure the input did not change its type and we do not need to - // update its users. - DCHECK_NE(input, equivalent); - - phi->ReplaceInput(equivalent, i); - if (equivalent->IsPhi()) { - worklist->push_back(equivalent->AsPhi()); - } - } - } - // All inputs either matched the type of the phi or we successfully replaced - // them with a suitable equivalent. Report success. - return true; - } -} - -// Attempt to set the primitive type of `phi` to match its inputs. Return whether -// it was changed by the algorithm or not. -bool SsaBuilder::UpdatePrimitiveType(HPhi* phi, ArenaVector<HPhi*>* worklist) { - DCHECK(phi->IsLive()); - Primitive::Type original_type = phi->GetType(); - - // Try to type the phi in two stages: - // (1) find a candidate type for the phi by merging types of all its inputs, - // (2) try to type the phi's inputs to that candidate type. - // Either of these stages may detect a type conflict and fail, in which case - // we immediately abort. - if (!TypePhiFromInputs(phi) || !TypeInputsOfPhi(phi, worklist)) { - // Conflict detected. Mark the phi dead and return true because it changed. - phi->SetDead(); - return true; - } - - // Return true if the type of the phi has changed. - return phi->GetType() != original_type; -} - -void SsaBuilder::RunPrimitiveTypePropagation() { - ArenaVector<HPhi*> worklist(GetGraph()->GetArena()->Adapter()); - - for (HReversePostOrderIterator it(*GetGraph()); !it.Done(); it.Advance()) { - HBasicBlock* block = it.Current(); - if (block->IsLoopHeader()) { - for (HInstructionIterator phi_it(block->GetPhis()); !phi_it.Done(); phi_it.Advance()) { - HPhi* phi = phi_it.Current()->AsPhi(); - if (phi->IsLive()) { - worklist.push_back(phi); - } - } - } else { - for (HInstructionIterator phi_it(block->GetPhis()); !phi_it.Done(); phi_it.Advance()) { - // Eagerly compute the type of the phi, for quicker convergence. Note - // that we don't need to add users to the worklist because we are - // doing a reverse post-order visit, therefore either the phi users are - // non-loop phi and will be visited later in the visit, or are loop-phis, - // and they are already in the work list. - HPhi* phi = phi_it.Current()->AsPhi(); - if (phi->IsLive()) { - UpdatePrimitiveType(phi, &worklist); - } - } - } - } - - ProcessPrimitiveTypePropagationWorklist(&worklist); - EquivalentPhisCleanup(); -} - -void SsaBuilder::ProcessPrimitiveTypePropagationWorklist(ArenaVector<HPhi*>* worklist) { - // Process worklist - while (!worklist->empty()) { - HPhi* phi = worklist->back(); - worklist->pop_back(); - // The phi could have been made dead as a result of conflicts while in the - // worklist. If it is now dead, there is no point in updating its type. - if (phi->IsLive() && UpdatePrimitiveType(phi, worklist)) { - AddDependentInstructionsToWorklist(phi, worklist); - } - } -} - -static HArrayGet* FindFloatOrDoubleEquivalentOfArrayGet(HArrayGet* aget) { - Primitive::Type type = aget->GetType(); - DCHECK(Primitive::IsIntOrLongType(type)); - HArrayGet* next = aget->GetNext()->AsArrayGet(); - return (next != nullptr && next->IsEquivalentOf(aget)) ? next : nullptr; -} - -static HArrayGet* CreateFloatOrDoubleEquivalentOfArrayGet(HArrayGet* aget) { - Primitive::Type type = aget->GetType(); - DCHECK(Primitive::IsIntOrLongType(type)); - DCHECK(FindFloatOrDoubleEquivalentOfArrayGet(aget) == nullptr); - - HArrayGet* equivalent = new (aget->GetBlock()->GetGraph()->GetArena()) HArrayGet( - aget->GetArray(), - aget->GetIndex(), - type == Primitive::kPrimInt ? Primitive::kPrimFloat : Primitive::kPrimDouble, - aget->GetDexPc()); - aget->GetBlock()->InsertInstructionAfter(equivalent, aget); - return equivalent; -} - -// Returns true if the array input of `aget` is either of type int[] or long[]. -// Should only be called on ArrayGets with ambiguous type (int/float, long/double) -// on arrays which were typed to an array class by RTP. -static bool IsArrayGetOnIntegralArray(HArrayGet* aget) SHARED_REQUIRES(Locks::mutator_lock_) { - ReferenceTypeInfo array_type = aget->GetArray()->GetReferenceTypeInfo(); - DCHECK(array_type.IsPrimitiveArrayClass()); - ReferenceTypeInfo::TypeHandle array_type_handle = array_type.GetTypeHandle(); - - bool is_integral_type; - if (Primitive::Is64BitType(aget->GetType())) { - is_integral_type = array_type_handle->GetComponentType()->IsPrimitiveLong(); - DCHECK(is_integral_type || array_type_handle->GetComponentType()->IsPrimitiveDouble()); - } else { - is_integral_type = array_type_handle->GetComponentType()->IsPrimitiveInt(); - DCHECK(is_integral_type || array_type_handle->GetComponentType()->IsPrimitiveFloat()); - } - return is_integral_type; -} - -bool SsaBuilder::FixAmbiguousArrayGets() { - if (ambiguous_agets_.empty()) { - return true; - } - - // The wrong ArrayGet equivalent may still have Phi uses coming from ArraySet - // uses (because they are untyped) and environment uses (if --debuggable). - // After resolving all ambiguous ArrayGets, we will re-run primitive type - // propagation on the Phis which need to be updated. - ArenaVector<HPhi*> worklist(GetGraph()->GetArena()->Adapter()); - - { - ScopedObjectAccess soa(Thread::Current()); - - for (HArrayGet* aget_int : ambiguous_agets_) { - if (!aget_int->GetArray()->GetReferenceTypeInfo().IsPrimitiveArrayClass()) { - // RTP did not type the input array. Bail. - return false; - } - - HArrayGet* aget_float = FindFloatOrDoubleEquivalentOfArrayGet(aget_int); - if (IsArrayGetOnIntegralArray(aget_int)) { - if (aget_float != nullptr) { - // There is a float/double equivalent. We must replace it and re-run - // primitive type propagation on all dependent instructions. - aget_float->ReplaceWith(aget_int); - aget_float->GetBlock()->RemoveInstruction(aget_float); - AddDependentInstructionsToWorklist(aget_int, &worklist); - } - } else { - if (aget_float == nullptr) { - // This is a float/double ArrayGet but there were no typed uses which - // would create the typed equivalent. Create it now. - aget_float = CreateFloatOrDoubleEquivalentOfArrayGet(aget_int); - } - // Replace the original int/long instruction. Note that it may have phi - // uses, environment uses, as well as real uses (from untyped ArraySets). - // We need to re-run primitive type propagation on its dependent instructions. - aget_int->ReplaceWith(aget_float); - aget_int->GetBlock()->RemoveInstruction(aget_int); - AddDependentInstructionsToWorklist(aget_float, &worklist); - } - } - } - - // Set a flag stating that types of ArrayGets have been resolved. This is used - // by GetFloatOrDoubleEquivalentOfArrayGet to report conflict. - agets_fixed_ = true; - - if (!worklist.empty()) { - ProcessPrimitiveTypePropagationWorklist(&worklist); - EquivalentPhisCleanup(); - } - - return true; -} - -BuildSsaResult SsaBuilder::BuildSsa() { - // 1) Visit in reverse post order. We need to have all predecessors of a block - // visited (with the exception of loops) in order to create the right environment - // for that block. For loops, we create phis whose inputs will be set in 2). - for (HReversePostOrderIterator it(*GetGraph()); !it.Done(); it.Advance()) { - VisitBasicBlock(it.Current()); - } - - // 2) Set inputs of loop header phis. - SetLoopHeaderPhiInputs(); - - // 3) Propagate types of phis. At this point, phis are typed void in the general - // case, or float/double/reference if we created an equivalent phi. So we need - // to propagate the types across phis to give them a correct type. If a type - // conflict is detected in this stage, the phi is marked dead. - RunPrimitiveTypePropagation(); - - // 4) Now that the correct primitive types have been assigned, we can get rid - // of redundant phis. Note that we cannot do this phase before type propagation, - // otherwise we could get rid of phi equivalents, whose presence is a requirement - // for the type propagation phase. Note that this is to satisfy statement (a) - // of the SsaBuilder (see ssa_builder.h). - SsaRedundantPhiElimination(GetGraph()).Run(); - - // 5) Fix the type for null constants which are part of an equality comparison. - // We need to do this after redundant phi elimination, to ensure the only cases - // that we can see are reference comparison against 0. The redundant phi - // elimination ensures we do not see a phi taking two 0 constants in a HEqual - // or HNotEqual. - FixNullConstantType(); - - // 6) Compute type of reference type instructions. The pass assumes that - // NullConstant has been fixed up. - ReferenceTypePropagation(GetGraph(), handles_).Run(); - - // 7) Step 1) duplicated ArrayGet instructions with ambiguous type (int/float - // or long/double). Now that RTP computed the type of the array input, the - // ambiguity can be resolved and the correct equivalent kept. - if (!FixAmbiguousArrayGets()) { - return kBuildSsaFailAmbiguousArrayGet; - } - - // 8) Mark dead phis. This will mark phis which are not used by instructions - // or other live phis. If compiling as debuggable code, phis will also be kept - // live if they have an environment use. - SsaDeadPhiElimination dead_phi_elimimation(GetGraph()); - dead_phi_elimimation.MarkDeadPhis(); - - // 9) Make sure environments use the right phi equivalent: a phi marked dead - // can have a phi equivalent that is not dead. In that case we have to replace - // it with the live equivalent because deoptimization and try/catch rely on - // environments containing values of all live vregs at that point. Note that - // there can be multiple phis for the same Dex register that are live - // (for example when merging constants), in which case it is okay for the - // environments to just reference one. - FixEnvironmentPhis(); - - // 10) Now that the right phis are used for the environments, we can eliminate - // phis we do not need. Regardless of the debuggable status, this phase is - /// necessary for statement (b) of the SsaBuilder (see ssa_builder.h), as well - // as for the code generation, which does not deal with phis of conflicting + // 11) Now that the right phis are used for the environments, and we + // have potentially revive dead phis in case of a debuggable application, + // we can eliminate phis we do not need. Regardless of the debuggable status, + // this phase is necessary for statement (b) of the SsaBuilder (see ssa_builder.h), + // as well as for the code generation, which does not deal with phis of conflicting // input types. - dead_phi_elimimation.EliminateDeadPhis(); + dead_phis.EliminateDeadPhis(); - // 11) Clear locals. + // 12) Clear locals. for (HInstructionIterator it(GetGraph()->GetEntryBlock()->GetInstructions()); !it.Done(); it.Advance()) { @@ -466,8 +404,6 @@ BuildSsaResult SsaBuilder::BuildSsa() { current->GetBlock()->RemoveInstruction(current); } } - - return kBuildSsaSuccess; } ArenaVector<HInstruction*>* SsaBuilder::GetLocalsFor(HBasicBlock* block) { @@ -655,8 +591,6 @@ HDoubleConstant* SsaBuilder::GetDoubleEquivalent(HLongConstant* constant) { * phi with a floating point / reference type. */ HPhi* SsaBuilder::GetFloatDoubleOrReferenceEquivalentOfPhi(HPhi* phi, Primitive::Type type) { - DCHECK(phi->IsLive()) << "Cannot get equivalent of a dead phi since it would create a live one."; - // We place the floating point /reference phi next to this phi. HInstruction* next = phi->GetNext(); if (next != nullptr @@ -672,50 +606,35 @@ HPhi* SsaBuilder::GetFloatDoubleOrReferenceEquivalentOfPhi(HPhi* phi, Primitive: ArenaAllocator* allocator = phi->GetBlock()->GetGraph()->GetArena(); HPhi* new_phi = new (allocator) HPhi(allocator, phi->GetRegNumber(), phi->InputCount(), type); for (size_t i = 0, e = phi->InputCount(); i < e; ++i) { - // Copy the inputs. Note that the graph may not be correctly typed - // by doing this copy, but the type propagation phase will fix it. + // Copy the inputs. Note that the graph may not be correctly typed by doing this copy, + // but the type propagation phase will fix it. new_phi->SetRawInputAt(i, phi->InputAt(i)); } phi->GetBlock()->InsertPhiAfter(new_phi, phi); - DCHECK(new_phi->IsLive()); return new_phi; } else { - // An existing equivalent was found. If it is dead, conflict was previously - // identified and we return nullptr instead. HPhi* next_phi = next->AsPhi(); DCHECK_EQ(next_phi->GetType(), type); - return next_phi->IsLive() ? next_phi : nullptr; - } -} - -HArrayGet* SsaBuilder::GetFloatOrDoubleEquivalentOfArrayGet(HArrayGet* aget) { - DCHECK(Primitive::IsIntegralType(aget->GetType())); - - if (!Primitive::IsIntOrLongType(aget->GetType())) { - // Cannot type boolean, char, byte, short to float/double. - return nullptr; - } - - DCHECK(ContainsElement(ambiguous_agets_, aget)); - if (agets_fixed_) { - // This used to be an ambiguous ArrayGet but its type has been resolved to - // int/long. Requesting a float/double equivalent should lead to a conflict. - if (kIsDebugBuild) { - ScopedObjectAccess soa(Thread::Current()); - DCHECK(IsArrayGetOnIntegralArray(aget)); + if (next_phi->IsDead()) { + // TODO(dbrazdil): Remove this SetLive (we should not need to revive phis) + // once we stop running MarkDeadPhis before PrimitiveTypePropagation. This + // cannot revive undefined loop header phis because they cannot have uses. + DCHECK(!IsUndefinedLoopHeaderPhi(next_phi)); + next_phi->SetLive(); } - return nullptr; - } else { - // This is an ambiguous ArrayGet which has not been resolved yet. Return an - // equivalent float/double instruction to use until it is resolved. - HArrayGet* equivalent = FindFloatOrDoubleEquivalentOfArrayGet(aget); - return (equivalent == nullptr) ? CreateFloatOrDoubleEquivalentOfArrayGet(aget) : equivalent; + return next_phi; } } -HInstruction* SsaBuilder::GetFloatOrDoubleEquivalent(HInstruction* value, Primitive::Type type) { +HInstruction* SsaBuilder::GetFloatOrDoubleEquivalent(HInstruction* user, + HInstruction* value, + Primitive::Type type) { if (value->IsArrayGet()) { - return GetFloatOrDoubleEquivalentOfArrayGet(value->AsArrayGet()); + // The verifier has checked that values in arrays cannot be used for both + // floating point and non-floating point operations. It is therefore safe to just + // change the type of the operation. + value->AsArrayGet()->SetType(type); + return value; } else if (value->IsLongConstant()) { return GetDoubleEquivalent(value->AsLongConstant()); } else if (value->IsIntConstant()) { @@ -723,7 +642,12 @@ HInstruction* SsaBuilder::GetFloatOrDoubleEquivalent(HInstruction* value, Primit } else if (value->IsPhi()) { return GetFloatDoubleOrReferenceEquivalentOfPhi(value->AsPhi(), type); } else { - return nullptr; + // For other instructions, we assume the verifier has checked that the dex format is correctly + // typed and the value in a dex register will not be used for both floating point and + // non-floating point operations. So the only reason an instruction would want a floating + // point equivalent is for an unused phi that will be removed by the dead phi elimination phase. + DCHECK(user->IsPhi()) << "is actually " << user->DebugName() << " (" << user->GetId() << ")"; + return value; } } @@ -738,17 +662,15 @@ HInstruction* SsaBuilder::GetReferenceTypeEquivalent(HInstruction* value) { } void SsaBuilder::VisitLoadLocal(HLoadLocal* load) { - Primitive::Type load_type = load->GetType(); HInstruction* value = (*current_locals_)[load->GetLocal()->GetRegNumber()]; // If the operation requests a specific type, we make sure its input is of that type. - if (load_type != value->GetType()) { - if (load_type == Primitive::kPrimFloat || load_type == Primitive::kPrimDouble) { - value = GetFloatOrDoubleEquivalent(value, load_type); - } else if (load_type == Primitive::kPrimNot) { + if (load->GetType() != value->GetType()) { + if (load->GetType() == Primitive::kPrimFloat || load->GetType() == Primitive::kPrimDouble) { + value = GetFloatOrDoubleEquivalent(load, value, load->GetType()); + } else if (load->GetType() == Primitive::kPrimNot) { value = GetReferenceTypeEquivalent(value); } } - load->ReplaceWith(value); load->GetBlock()->RemoveInstruction(load); } @@ -838,13 +760,4 @@ void SsaBuilder::VisitTemporary(HTemporary* temp) { temp->GetBlock()->RemoveInstruction(temp); } -void SsaBuilder::VisitArrayGet(HArrayGet* aget) { - Primitive::Type type = aget->GetType(); - DCHECK(!Primitive::IsFloatingPointType(type)); - if (Primitive::IsIntOrLongType(type)) { - ambiguous_agets_.push_back(aget); - } - VisitInstruction(aget); -} - } // namespace art diff --git a/compiler/optimizing/ssa_builder.h b/compiler/optimizing/ssa_builder.h index ed6f5cab51..dcce5e4c2c 100644 --- a/compiler/optimizing/ssa_builder.h +++ b/compiler/optimizing/ssa_builder.h @@ -49,20 +49,17 @@ static constexpr int kDefaultNumberOfLoops = 2; */ class SsaBuilder : public HGraphVisitor { public: - explicit SsaBuilder(HGraph* graph, StackHandleScopeCollection* handles) + explicit SsaBuilder(HGraph* graph) : HGraphVisitor(graph), - handles_(handles), - agets_fixed_(false), current_locals_(nullptr), loop_headers_(graph->GetArena()->Adapter(kArenaAllocSsaBuilder)), - ambiguous_agets_(graph->GetArena()->Adapter(kArenaAllocSsaBuilder)), locals_for_(graph->GetBlocks().size(), ArenaVector<HInstruction*>(graph->GetArena()->Adapter(kArenaAllocSsaBuilder)), graph->GetArena()->Adapter(kArenaAllocSsaBuilder)) { loop_headers_.reserve(kDefaultNumberOfLoops); } - BuildSsaResult BuildSsa(); + void BuildSsa(); // Returns locals vector for `block`. If it is a catch block, the vector will be // prepopulated with catch phis for vregs which are defined in `current_locals_`. @@ -74,38 +71,23 @@ class SsaBuilder : public HGraphVisitor { void VisitStoreLocal(HStoreLocal* store); void VisitInstruction(HInstruction* instruction); void VisitTemporary(HTemporary* instruction); - void VisitArrayGet(HArrayGet* aget); + + static HInstruction* GetFloatOrDoubleEquivalent(HInstruction* user, + HInstruction* instruction, + Primitive::Type type); + + static HInstruction* GetReferenceTypeEquivalent(HInstruction* instruction); static constexpr const char* kSsaBuilderPassName = "ssa_builder"; private: void SetLoopHeaderPhiInputs(); - void FixEnvironmentPhis(); void FixNullConstantType(); void EquivalentPhisCleanup(); - void RunPrimitiveTypePropagation(); - - // Attempts to resolve types of aget and aget-wide instructions from reference - // type information on the input array. Returns false if the type of the array - // is unknown. - bool FixAmbiguousArrayGets(); - - bool TypeInputsOfPhi(HPhi* phi, ArenaVector<HPhi*>* worklist); - bool UpdatePrimitiveType(HPhi* phi, ArenaVector<HPhi*>* worklist); - void ProcessPrimitiveTypePropagationWorklist(ArenaVector<HPhi*>* worklist); - HInstruction* GetFloatOrDoubleEquivalent(HInstruction* instruction, Primitive::Type type); - HInstruction* GetReferenceTypeEquivalent(HInstruction* instruction); - - HFloatConstant* GetFloatEquivalent(HIntConstant* constant); - HDoubleConstant* GetDoubleEquivalent(HLongConstant* constant); - HPhi* GetFloatDoubleOrReferenceEquivalentOfPhi(HPhi* phi, Primitive::Type type); - HArrayGet* GetFloatOrDoubleEquivalentOfArrayGet(HArrayGet* aget); - - StackHandleScopeCollection* const handles_; - - // True if types of ambiguous ArrayGets have been resolved. - bool agets_fixed_; + static HFloatConstant* GetFloatEquivalent(HIntConstant* constant); + static HDoubleConstant* GetDoubleEquivalent(HLongConstant* constant); + static HPhi* GetFloatDoubleOrReferenceEquivalentOfPhi(HPhi* phi, Primitive::Type type); // Locals for the current block being visited. ArenaVector<HInstruction*>* current_locals_; @@ -114,8 +96,6 @@ class SsaBuilder : public HGraphVisitor { // over these blocks to set the inputs of their phis. ArenaVector<HBasicBlock*> loop_headers_; - ArenaVector<HArrayGet*> ambiguous_agets_; - // HEnvironment for each block. ArenaVector<ArenaVector<HInstruction*>> locals_for_; diff --git a/compiler/optimizing/ssa_phi_elimination.cc b/compiler/optimizing/ssa_phi_elimination.cc index 63aba88c2b..a3219dcc38 100644 --- a/compiler/optimizing/ssa_phi_elimination.cc +++ b/compiler/optimizing/ssa_phi_elimination.cc @@ -40,17 +40,15 @@ void SsaDeadPhiElimination::MarkDeadPhis() { continue; } - bool keep_alive = (graph_->IsDebuggable() && phi->HasEnvironmentUses()); - if (!keep_alive) { - for (HUseIterator<HInstruction*> use_it(phi->GetUses()); !use_it.Done(); use_it.Advance()) { - if (!use_it.Current()->GetUser()->IsPhi()) { - keep_alive = true; - break; - } + bool has_non_phi_use = false; + for (HUseIterator<HInstruction*> use_it(phi->GetUses()); !use_it.Done(); use_it.Advance()) { + if (!use_it.Current()->GetUser()->IsPhi()) { + has_non_phi_use = true; + break; } } - if (keep_alive) { + if (has_non_phi_use) { worklist_.push_back(phi); } else { phi->SetDead(); @@ -96,8 +94,8 @@ void SsaDeadPhiElimination::EliminateDeadPhis() { for (HUseIterator<HInstruction*> use_it(phi->GetUses()); !use_it.Done(); use_it.Advance()) { HInstruction* user = use_it.Current()->GetUser(); - DCHECK(user->IsLoopHeaderPhi()); - DCHECK(user->AsPhi()->IsDead()); + DCHECK(user->IsLoopHeaderPhi()) << user->GetId(); + DCHECK(user->AsPhi()->IsDead()) << user->GetId(); } } // Remove the phi from use lists of its inputs. diff --git a/compiler/optimizing/ssa_test.cc b/compiler/optimizing/ssa_test.cc index d2885a8fd7..024278f4b2 100644 --- a/compiler/optimizing/ssa_test.cc +++ b/compiler/optimizing/ssa_test.cc @@ -28,8 +28,6 @@ namespace art { -class SsaTest : public CommonCompilerTest {}; - class SsaPrettyPrinter : public HPrettyPrinter { public: explicit SsaPrettyPrinter(HGraph* graph) : HPrettyPrinter(graph), str_("") {} @@ -85,10 +83,11 @@ static void TestCode(const uint16_t* data, const char* expected) { bool graph_built = builder.BuildGraph(*item); ASSERT_TRUE(graph_built); - TransformToSsa(graph); + graph->BuildDominatorTree(); // Suspend checks implementation may change in the future, and this test relies // on how instructions are ordered. RemoveSuspendChecks(graph); + graph->TransformToSsa(); ReNumberInstructions(graph); // Test that phis had their type set. @@ -104,7 +103,7 @@ static void TestCode(const uint16_t* data, const char* expected) { ASSERT_STREQ(expected, printer.str().c_str()); } -TEST_F(SsaTest, CFG1) { +TEST(SsaTest, CFG1) { // Test that we get rid of loads and stores. const char* expected = "BasicBlock 0, succ: 1\n" @@ -132,7 +131,7 @@ TEST_F(SsaTest, CFG1) { TestCode(data, expected); } -TEST_F(SsaTest, CFG2) { +TEST(SsaTest, CFG2) { // Test that we create a phi for the join block of an if control flow instruction // when there is only code in the else branch. const char* expected = @@ -163,7 +162,7 @@ TEST_F(SsaTest, CFG2) { TestCode(data, expected); } -TEST_F(SsaTest, CFG3) { +TEST(SsaTest, CFG3) { // Test that we create a phi for the join block of an if control flow instruction // when both branches update a local. const char* expected = @@ -196,7 +195,7 @@ TEST_F(SsaTest, CFG3) { TestCode(data, expected); } -TEST_F(SsaTest, Loop1) { +TEST(SsaTest, Loop1) { // Test that we create a phi for an initialized local at entry of a loop. const char* expected = "BasicBlock 0, succ: 1\n" @@ -229,7 +228,7 @@ TEST_F(SsaTest, Loop1) { TestCode(data, expected); } -TEST_F(SsaTest, Loop2) { +TEST(SsaTest, Loop2) { // Simple loop with one preheader and one back edge. const char* expected = "BasicBlock 0, succ: 1\n" @@ -259,7 +258,7 @@ TEST_F(SsaTest, Loop2) { TestCode(data, expected); } -TEST_F(SsaTest, Loop3) { +TEST(SsaTest, Loop3) { // Test that a local not yet defined at the entry of a loop is handled properly. const char* expected = "BasicBlock 0, succ: 1\n" @@ -291,7 +290,7 @@ TEST_F(SsaTest, Loop3) { TestCode(data, expected); } -TEST_F(SsaTest, Loop4) { +TEST(SsaTest, Loop4) { // Make sure we support a preheader of a loop not being the first predecessor // in the predecessor list of the header. const char* expected = @@ -326,7 +325,7 @@ TEST_F(SsaTest, Loop4) { TestCode(data, expected); } -TEST_F(SsaTest, Loop5) { +TEST(SsaTest, Loop5) { // Make sure we create a preheader of a loop when a header originally has two // incoming blocks and one back edge. const char* expected = @@ -368,7 +367,7 @@ TEST_F(SsaTest, Loop5) { TestCode(data, expected); } -TEST_F(SsaTest, Loop6) { +TEST(SsaTest, Loop6) { // Test a loop with one preheader and two back edges (e.g. continue). const char* expected = "BasicBlock 0, succ: 1\n" @@ -407,7 +406,7 @@ TEST_F(SsaTest, Loop6) { TestCode(data, expected); } -TEST_F(SsaTest, Loop7) { +TEST(SsaTest, Loop7) { // Test a loop with one preheader, one back edge, and two exit edges (e.g. break). const char* expected = "BasicBlock 0, succ: 1\n" @@ -449,7 +448,7 @@ TEST_F(SsaTest, Loop7) { TestCode(data, expected); } -TEST_F(SsaTest, DeadLocal) { +TEST(SsaTest, DeadLocal) { // Test that we correctly handle a local not being used. const char* expected = "BasicBlock 0, succ: 1\n" @@ -467,7 +466,7 @@ TEST_F(SsaTest, DeadLocal) { TestCode(data, expected); } -TEST_F(SsaTest, LocalInIf) { +TEST(SsaTest, LocalInIf) { // Test that we do not create a phi in the join block when one predecessor // does not update the local. const char* expected = @@ -497,7 +496,7 @@ TEST_F(SsaTest, LocalInIf) { TestCode(data, expected); } -TEST_F(SsaTest, MultiplePredecessors) { +TEST(SsaTest, MultiplePredecessors) { // Test that we do not create a phi when one predecessor // does not update the local. const char* expected = diff --git a/runtime/Android.mk b/runtime/Android.mk index 993f37f3f7..40961179d3 100644 --- a/runtime/Android.mk +++ b/runtime/Android.mk @@ -42,6 +42,7 @@ LIBART_COMMON_SRC_FILES := \ check_jni.cc \ class_linker.cc \ class_table.cc \ + code_simulator_container.cc \ common_throws.cc \ debugger.cc \ dex_file.cc \ diff --git a/runtime/asm_support.h b/runtime/asm_support.h index 2bc6c79d57..c86614c0fd 100644 --- a/runtime/asm_support.h +++ b/runtime/asm_support.h @@ -163,13 +163,13 @@ ADD_TEST_EQ(size_t(MIRROR_OBJECT_HEADER_SIZE), sizeof(art::mirror::Object)) #define MIRROR_CLASS_COMPONENT_TYPE_OFFSET (4 + MIRROR_OBJECT_HEADER_SIZE) ADD_TEST_EQ(MIRROR_CLASS_COMPONENT_TYPE_OFFSET, art::mirror::Class::ComponentTypeOffset().Int32Value()) -#define MIRROR_CLASS_ACCESS_FLAGS_OFFSET (64 + MIRROR_OBJECT_HEADER_SIZE) +#define MIRROR_CLASS_ACCESS_FLAGS_OFFSET (72 + MIRROR_OBJECT_HEADER_SIZE) ADD_TEST_EQ(MIRROR_CLASS_ACCESS_FLAGS_OFFSET, art::mirror::Class::AccessFlagsOffset().Int32Value()) -#define MIRROR_CLASS_OBJECT_SIZE_OFFSET (96 + MIRROR_OBJECT_HEADER_SIZE) +#define MIRROR_CLASS_OBJECT_SIZE_OFFSET (104 + MIRROR_OBJECT_HEADER_SIZE) ADD_TEST_EQ(MIRROR_CLASS_OBJECT_SIZE_OFFSET, art::mirror::Class::ObjectSizeOffset().Int32Value()) -#define MIRROR_CLASS_STATUS_OFFSET (108 + MIRROR_OBJECT_HEADER_SIZE) +#define MIRROR_CLASS_STATUS_OFFSET (116 + MIRROR_OBJECT_HEADER_SIZE) ADD_TEST_EQ(MIRROR_CLASS_STATUS_OFFSET, art::mirror::Class::StatusOffset().Int32Value()) diff --git a/runtime/base/array_slice.h b/runtime/base/array_slice.h deleted file mode 100644 index 19ad302c9d..0000000000 --- a/runtime/base/array_slice.h +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ART_RUNTIME_BASE_ARRAY_SLICE_H_ -#define ART_RUNTIME_BASE_ARRAY_SLICE_H_ - -#include "length_prefixed_array.h" -#include "stride_iterator.h" -#include "base/bit_utils.h" -#include "base/casts.h" -#include "base/iteration_range.h" - -namespace art { - -// An ArraySlice is an abstraction over an array or a part of an array of a particular type. It does -// bounds checking and can be made from several common array-like structures in Art. -template<typename T> -class ArraySlice { - public: - // Create an empty array slice. - ArraySlice() : array_(nullptr), size_(0), element_size_(0) {} - - // Create an array slice of the first 'length' elements of the array, with each element being - // element_size bytes long. - ArraySlice(T* array, - size_t length, - size_t element_size = sizeof(T)) - : array_(array), - size_(dchecked_integral_cast<uint32_t>(length)), - element_size_(element_size) { - DCHECK(array_ != nullptr || length == 0); - } - - // Create an array slice of the elements between start_offset and end_offset of the array with - // each element being element_size bytes long. Both start_offset and end_offset are in - // element_size units. - ArraySlice(T* array, - uint32_t start_offset, - uint32_t end_offset, - size_t element_size = sizeof(T)) - : array_(nullptr), - size_(end_offset - start_offset), - element_size_(element_size) { - DCHECK(array_ != nullptr || size_ == 0); - DCHECK_LE(start_offset, end_offset); - if (size_ != 0) { - uintptr_t offset = start_offset * element_size_; - array_ = *reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(array) + offset); - } - } - - // Create an array slice of the elements between start_offset and end_offset of the array with - // each element being element_size bytes long and having the given alignment. Both start_offset - // and end_offset are in element_size units. - ArraySlice(LengthPrefixedArray<T>* array, - uint32_t start_offset, - uint32_t end_offset, - size_t element_size = sizeof(T), - size_t alignment = alignof(T)) - : array_(nullptr), - size_(end_offset - start_offset), - element_size_(element_size) { - DCHECK(array != nullptr || size_ == 0); - if (size_ != 0) { - DCHECK_LE(start_offset, end_offset); - DCHECK_LE(start_offset, array->size()); - DCHECK_LE(end_offset, array->size()); - array_ = &array->At(start_offset, element_size_, alignment); - } - } - - T& At(size_t index) { - DCHECK_LT(index, size_); - return AtUnchecked(index); - } - - const T& At(size_t index) const { - DCHECK_LT(index, size_); - return AtUnchecked(index); - } - - T& operator[](size_t index) { - return At(index); - } - - const T& operator[](size_t index) const { - return At(index); - } - - StrideIterator<T> begin() { - return StrideIterator<T>(&AtUnchecked(0), element_size_); - } - - StrideIterator<const T> begin() const { - return StrideIterator<const T>(&AtUnchecked(0), element_size_); - } - - StrideIterator<T> end() { - return StrideIterator<T>(&AtUnchecked(size_), element_size_); - } - - StrideIterator<const T> end() const { - return StrideIterator<const T>(&AtUnchecked(size_), element_size_); - } - - IterationRange<StrideIterator<T>> AsRange() { - return size() != 0 ? MakeIterationRange(begin(), end()) - : MakeEmptyIterationRange(StrideIterator<T>(nullptr, 0)); - } - - size_t size() const { - return size_; - } - - size_t ElementSize() const { - return element_size_; - } - - private: - T& AtUnchecked(size_t index) { - return *reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(array_) + index * element_size_); - } - - const T& AtUnchecked(size_t index) const { - return *reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(array_) + index * element_size_); - } - - T* array_; - size_t size_; - size_t element_size_; -}; - -} // namespace art - -#endif // ART_RUNTIME_BASE_ARRAY_SLICE_H_ diff --git a/runtime/base/logging.h b/runtime/base/logging.h index 2cd1a4de9f..115c26073d 100644 --- a/runtime/base/logging.h +++ b/runtime/base/logging.h @@ -48,6 +48,7 @@ struct LogVerbosity { bool oat; bool profiler; bool signals; + bool simulator; bool startup; bool third_party_jni; // Enabled with "-verbose:third-party-jni". bool threads; diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index a82c1dce0a..f5085ed417 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -807,7 +807,10 @@ static void SanityCheckObjectsCallback(mirror::Object* obj, void* arg ATTRIBUTE_ auto* runtime = Runtime::Current(); auto* image_space = runtime->GetHeap()->GetBootImageSpace(); auto pointer_size = runtime->GetClassLinker()->GetImagePointerSize(); - for (auto& m : klass->GetMethods(pointer_size)) { + for (auto& m : klass->GetDirectMethods(pointer_size)) { + SanityCheckArtMethod(&m, klass, image_space); + } + for (auto& m : klass->GetVirtualMethods(pointer_size)) { SanityCheckArtMethod(&m, klass, image_space); } auto* vtable = klass->GetVTable(); @@ -2242,14 +2245,11 @@ void ClassLinker::LoadClassMembers(Thread* self, klass->SetIFieldsPtr(ifields); DCHECK_EQ(klass->NumInstanceFields(), num_ifields); // Load methods. - klass->SetMethodsPtr( - AllocArtMethodArray(self, allocator, it.NumDirectMethods() + it.NumVirtualMethods()), - it.NumDirectMethods(), - it.NumVirtualMethods()); + klass->SetDirectMethodsPtr(AllocArtMethodArray(self, allocator, it.NumDirectMethods())); + klass->SetVirtualMethodsPtr(AllocArtMethodArray(self, allocator, it.NumVirtualMethods())); size_t class_def_method_index = 0; uint32_t last_dex_method_index = DexFile::kDexNoIndex; size_t last_class_def_method_index = 0; - // TODO These should really use the iterators. for (size_t i = 0; it.HasNextDirectMethod(); i++, it.Next()) { ArtMethod* method = klass->GetDirectMethodUnchecked(i, image_pointer_size_); LoadMethod(self, dex_file, it, klass, method); @@ -2728,12 +2728,9 @@ mirror::Class* ClassLinker::InsertClass(const char* descriptor, mirror::Class* k return nullptr; } -// TODO This should really be in mirror::Class. -void ClassLinker::UpdateClassMethods(mirror::Class* klass, - LengthPrefixedArray<ArtMethod>* new_methods) { - klass->SetMethodsPtrUnchecked(new_methods, - klass->NumDirectMethods(), - klass->NumDeclaredVirtualMethods()); +void ClassLinker::UpdateClassVirtualMethods(mirror::Class* klass, + LengthPrefixedArray<ArtMethod>* new_methods) { + klass->SetVirtualMethodsPtr(new_methods); // Need to mark the card so that the remembered sets and mod union tables get updated. Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(klass); } @@ -3305,30 +3302,29 @@ mirror::Class* ClassLinker::CreateProxyClass(ScopedObjectAccessAlreadyRunnable& throws_sfield.SetAccessFlags(kAccStatic | kAccPublic | kAccFinal); // Proxies have 1 direct method, the constructor - const size_t num_direct_methods = 1; + LengthPrefixedArray<ArtMethod>* directs = AllocArtMethodArray(self, allocator, 1); + // Currently AllocArtMethodArray cannot return null, but the OOM logic is left there in case we + // want to throw OOM in the future. + if (UNLIKELY(directs == nullptr)) { + self->AssertPendingOOMException(); + return nullptr; + } + klass->SetDirectMethodsPtr(directs); + CreateProxyConstructor(klass, klass->GetDirectMethodUnchecked(0, image_pointer_size_)); - // They have as many virtual methods as the array + // Create virtual method using specified prototypes. auto h_methods = hs.NewHandle(soa.Decode<mirror::ObjectArray<mirror::Method>*>(methods)); DCHECK_EQ(h_methods->GetClass(), mirror::Method::ArrayClass()) << PrettyClass(h_methods->GetClass()); const size_t num_virtual_methods = h_methods->GetLength(); - - // Create the methods array. - LengthPrefixedArray<ArtMethod>* proxy_class_methods = AllocArtMethodArray( - self, allocator, num_direct_methods + num_virtual_methods); + auto* virtuals = AllocArtMethodArray(self, allocator, num_virtual_methods); // Currently AllocArtMethodArray cannot return null, but the OOM logic is left there in case we // want to throw OOM in the future. - if (UNLIKELY(proxy_class_methods == nullptr)) { + if (UNLIKELY(virtuals == nullptr)) { self->AssertPendingOOMException(); return nullptr; } - klass->SetMethodsPtr(proxy_class_methods, num_direct_methods, num_virtual_methods); - - // Create the single direct method. - CreateProxyConstructor(klass, klass->GetDirectMethodUnchecked(0, image_pointer_size_)); - - // Create virtual method using specified prototypes. - // TODO These should really use the iterators. + klass->SetVirtualMethodsPtr(virtuals); for (size_t i = 0; i < num_virtual_methods; ++i) { auto* virtual_method = klass->GetVirtualMethodUnchecked(i, image_pointer_size_); auto* prototype = h_methods->Get(i)->GetArtMethod(); @@ -4106,8 +4102,14 @@ void ClassLinker::FixupTemporaryDeclaringClass(mirror::Class* temp_class, } DCHECK_EQ(temp_class->NumDirectMethods(), 0u); + for (auto& method : new_class->GetDirectMethods(image_pointer_size_)) { + if (method.GetDeclaringClass() == temp_class) { + method.SetDeclaringClass(new_class); + } + } + DCHECK_EQ(temp_class->NumVirtualMethods(), 0u); - for (auto& method : new_class->GetMethods(image_pointer_size_)) { + for (auto& method : new_class->GetVirtualMethods(image_pointer_size_)) { if (method.GetDeclaringClass() == temp_class) { method.SetDeclaringClass(new_class); } @@ -4191,7 +4193,8 @@ bool ClassLinker::LinkClass(Thread* self, // ArtMethod array pointers. If this occurs, it causes bugs in remembered sets since the GC // may not see any references to the target space and clean the card for a class if another // class had the same array pointer. - klass->SetMethodsPtrUnchecked(nullptr, 0, 0); + klass->SetDirectMethodsPtrUnchecked(nullptr); + klass->SetVirtualMethodsPtr(nullptr); klass->SetSFieldsPtrUnchecked(nullptr); klass->SetIFieldsPtrUnchecked(nullptr); if (UNLIKELY(h_new_class.Get() == nullptr)) { @@ -4956,10 +4959,12 @@ static bool ContainsOverridingMethodOf(Thread* self, for (size_t k = ifstart + 1; k < iftable_count; k++) { // Skip ifstart since our current interface obviously cannot override itself. current_iface.Assign(iftable->GetInterface(k)); - // Iterate through every method on this interface. The order does not matter. - for (ArtMethod& current_method : current_iface->GetDeclaredVirtualMethods(image_pointer_size)) { + size_t num_instance_methods = current_iface->NumVirtualMethods(); + // Iterate through every method on this interface. The order does not matter so we go forwards. + for (size_t m = 0; m < num_instance_methods; m++) { + ArtMethod* current_method = current_iface->GetVirtualMethodUnchecked(m, image_pointer_size); if (UNLIKELY(target.HasSameNameAndSignature( - current_method.GetInterfaceMethodIfProxy(image_pointer_size)))) { + current_method->GetInterfaceMethodIfProxy(image_pointer_size)))) { // Check if the i'th interface is a subtype of this one. if (iface->IsAssignableFrom(current_iface.Get())) { return true; @@ -5012,9 +5017,10 @@ ClassLinker::DefaultMethodSearchResult ClassLinker::FindDefaultMethodImplementat DCHECK_LT(k, iftable->Count()); iface.Assign(iftable->GetInterface(k)); - // Iterate through every declared method on this interface. The order does not matter. - for (auto& method_iter : iface->GetDeclaredVirtualMethods(image_pointer_size_)) { - ArtMethod* current_method = &method_iter; + size_t num_instance_methods = iface->NumVirtualMethods(); + // Iterate through every method on this interface. The order does not matter so we go forwards. + for (size_t m = 0; m < num_instance_methods; m++) { + ArtMethod* current_method = iface->GetVirtualMethodUnchecked(m, image_pointer_size_); // Skip abstract methods and methods with different names. if (current_method->IsAbstract() || !target_name_comparator.HasSameNameAndSignature( @@ -5321,27 +5327,6 @@ static ArtMethod* FindSameNameAndSignature(MethodNameAndSignatureComparator& cmp return nullptr; } -static void SanityCheckVTable(Handle<mirror::Class> klass, uint32_t pointer_size) - SHARED_REQUIRES(Locks::mutator_lock_) { - mirror::PointerArray* check_vtable = klass->GetVTableDuringLinking(); - mirror::Class* superclass = (klass->HasSuperClass()) ? klass->GetSuperClass() : nullptr; - int32_t super_vtable_length = (superclass != nullptr) ? superclass->GetVTableLength() : 0; - for (int32_t i = 0; i < check_vtable->GetLength(); ++i) { - ArtMethod* m = check_vtable->GetElementPtrSize<ArtMethod*>(i, pointer_size); - CHECK(m != nullptr); - - ArraySlice<ArtMethod> virtuals = klass->GetVirtualMethodsSliceUnchecked(pointer_size); - auto is_same_method = [m] (const ArtMethod& meth) { - return &meth == m; - }; - CHECK((super_vtable_length > i && superclass->GetVTableEntry(i, pointer_size) == m) || - std::find_if(virtuals.begin(), virtuals.end(), is_same_method) != virtuals.end()) - << "While linking class '" << PrettyClass(klass.Get()) << "' unable to find owning class " - << "of '" << PrettyMethod(m) << "' (vtable index: " << i << ")."; - } -} - -// TODO Combine virtuals and directs. bool ClassLinker::LinkInterfaceMethods( Thread* self, Handle<mirror::Class> klass, @@ -5464,27 +5449,25 @@ bool ClassLinker::LinkInterfaceMethods( const bool super_interface = is_super && extend_super_iftable; auto method_array(hs2.NewHandle(iftable->GetMethodArray(i))); - ArraySlice<ArtMethod> input_virtual_methods; + LengthPrefixedArray<ArtMethod>* input_virtual_methods = nullptr; Handle<mirror::PointerArray> input_vtable_array = NullHandle<mirror::PointerArray>(); int32_t input_array_length = 0; - // TODO Cleanup Needed: In the presence of default methods this optimization is rather dirty // and confusing. Default methods should always look through all the superclasses // because they are the last choice of an implementation. We get around this by looking // at the super-classes iftable methods (copied into method_array previously) when we are // looking for the implementation of a super-interface method but that is rather dirty. if (super_interface) { - // If we are overwriting a super class interface, try to only virtual methods instead of the + // We are overwriting a super class interface, try to only virtual methods instead of the // whole vtable. - input_virtual_methods = klass->GetDeclaredMethodsSlice(image_pointer_size_); - input_array_length = input_virtual_methods.size(); + input_virtual_methods = klass->GetVirtualMethodsPtr(); + input_array_length = klass->NumVirtualMethods(); } else { - // For a new interface, however, we need the whole vtable in case a new - // interface method is implemented in the whole superclass. + // A new interface, we need the whole vtable in case a new interface method is implemented + // in the whole superclass. input_vtable_array = vtable; input_array_length = input_vtable_array->GetLength(); } - // For each method in interface for (size_t j = 0; j < num_methods; ++j) { auto* interface_method = iftable->GetInterface(i)->GetVirtualMethod(j, image_pointer_size_); @@ -5505,8 +5488,8 @@ bool ClassLinker::LinkInterfaceMethods( bool found_impl = false; ArtMethod* vtable_impl = nullptr; for (int32_t k = input_array_length - 1; k >= 0; --k) { - ArtMethod* vtable_method = input_vtable_array.Get() == nullptr ? - &input_virtual_methods[k] : + ArtMethod* vtable_method = input_virtual_methods != nullptr ? + &input_virtual_methods->At(k, method_size, method_alignment) : input_vtable_array->GetElementPtrSize<ArtMethod*>(k, image_pointer_size_); ArtMethod* vtable_method_for_name_comparison = vtable_method->GetInterfaceMethodIfProxy(image_pointer_size_); @@ -5667,39 +5650,38 @@ bool ClassLinker::LinkInterfaceMethods( VLOG(class_linker) << PrettyClass(klass.Get()) << ": miranda_methods=" << miranda_methods.size() << " default_methods=" << default_methods.size() << " default_conflict_methods=" << default_conflict_methods.size(); - const size_t old_method_count = klass->NumMethods(); + const size_t old_method_count = klass->NumVirtualMethods(); const size_t new_method_count = old_method_count + miranda_methods.size() + default_methods.size() + default_conflict_methods.size(); // Attempt to realloc to save RAM if possible. - LengthPrefixedArray<ArtMethod>* old_methods = klass->GetMethodsPtr(); - // The Realloced virtual methods aren't visible from the class roots, so there is no issue + LengthPrefixedArray<ArtMethod>* old_virtuals = klass->GetVirtualMethodsPtr(); + // The Realloced virtual methods aren't visiblef from the class roots, so there is no issue // where GCs could attempt to mark stale pointers due to memcpy. And since we overwrite the // realloced memory with out->CopyFrom, we are guaranteed to have objects in the to space since // CopyFrom has internal read barriers. - // - // TODO We should maybe move some of this into mirror::Class or at least into another method. - const size_t old_size = LengthPrefixedArray<ArtMethod>::ComputeSize(old_method_count, - method_size, - method_alignment); + const size_t old_size = old_virtuals != nullptr + ? LengthPrefixedArray<ArtMethod>::ComputeSize(old_method_count, + method_size, + method_alignment) + : 0u; const size_t new_size = LengthPrefixedArray<ArtMethod>::ComputeSize(new_method_count, method_size, method_alignment); - const size_t old_methods_ptr_size = (old_methods != nullptr) ? old_size : 0; - auto* methods = reinterpret_cast<LengthPrefixedArray<ArtMethod>*>( - runtime->GetLinearAlloc()->Realloc(self, old_methods, old_methods_ptr_size, new_size)); - if (UNLIKELY(methods == nullptr)) { + auto* virtuals = reinterpret_cast<LengthPrefixedArray<ArtMethod>*>( + runtime->GetLinearAlloc()->Realloc(self, old_virtuals, old_size, new_size)); + if (UNLIKELY(virtuals == nullptr)) { self->AssertPendingOOMException(); self->EndAssertNoThreadSuspension(old_cause); return false; } ScopedArenaUnorderedMap<ArtMethod*, ArtMethod*> move_table(allocator.Adapter()); - if (methods != old_methods) { + if (virtuals != old_virtuals) { // Maps from heap allocated miranda method to linear alloc miranda method. - StrideIterator<ArtMethod> out = methods->begin(method_size, method_alignment); - // Copy over the old methods. - for (auto& m : klass->GetMethods(image_pointer_size_)) { + StrideIterator<ArtMethod> out = virtuals->begin(method_size, method_alignment); + // Copy over the old methods + miranda methods. + for (auto& m : klass->GetVirtualMethods(image_pointer_size_)) { move_table.emplace(&m, &*out); // The CopyFrom is only necessary to not miss read barriers since Realloc won't do read // barriers when it copies. @@ -5707,7 +5689,8 @@ bool ClassLinker::LinkInterfaceMethods( ++out; } } - StrideIterator<ArtMethod> out(methods->begin(method_size, method_alignment) + old_method_count); + StrideIterator<ArtMethod> out(virtuals->begin(method_size, method_alignment) + + old_method_count); // Copy over miranda methods before copying vtable since CopyOf may cause thread suspension and // we want the roots of the miranda methods to get visited. for (ArtMethod* mir_method : miranda_methods) { @@ -5719,8 +5702,9 @@ bool ClassLinker::LinkInterfaceMethods( move_table.emplace(mir_method, &new_method); ++out; } - // We need to copy the default methods into our own method table since the runtime requires that - // every method on a class's vtable be in that respective class's virtual method table. + // We need to copy the default methods into our own virtual method table since the runtime + // requires that every method on a class's vtable be in that respective class's virtual method + // table. // NOTE This means that two classes might have the same implementation of a method from the same // interface but will have different ArtMethod*s for them. This also means we cannot compare a // default method found on a class with one found on the declaring interface directly and must @@ -5754,8 +5738,8 @@ bool ClassLinker::LinkInterfaceMethods( move_table.emplace(conf_method, &new_method); ++out; } - methods->SetSize(new_method_count); - UpdateClassMethods(klass.Get(), methods); + virtuals->SetSize(new_method_count); + UpdateClassVirtualMethods(klass.Get(), virtuals); // Done copying methods, they are all roots in the class now, so we can end the no thread // suspension assert. self->EndAssertNoThreadSuspension(old_cause); @@ -5771,7 +5755,7 @@ bool ClassLinker::LinkInterfaceMethods( self->AssertPendingOOMException(); return false; } - out = methods->begin(method_size, method_alignment) + old_method_count; + out = virtuals->begin(method_size, method_alignment) + old_method_count; size_t vtable_pos = old_vtable_count; for (size_t i = old_method_count; i < new_method_count; ++i) { // Leave the declaring class alone as type indices are relative to it @@ -5825,16 +5809,8 @@ bool ClassLinker::LinkInterfaceMethods( } } - if (kIsDebugBuild) { - for (size_t i = 0; i < new_vtable_count; ++i) { - CHECK(move_table.find(vtable->GetElementPtrSize<ArtMethod*>(i, image_pointer_size_)) == - move_table.end()); - } - } - klass->SetVTable(vtable.Get()); - // Go fix up all the stale (old miranda or default method) pointers. - // First do it on the iftable. + // Go fix up all the stale miranda pointers. for (size_t i = 0; i < ifcount; ++i) { for (size_t j = 0, count = iftable->GetMethodArrayCount(i); j < count; ++j) { auto* method_array = iftable->GetMethodArray(i); @@ -5848,7 +5824,7 @@ bool ClassLinker::LinkInterfaceMethods( } } } - // Fix up IMT next + // Fix up IMT in case it has any miranda methods in it. for (size_t i = 0; i < mirror::Class::kImtSize; ++i) { auto it = move_table.find(out_imt[i]); if (it != move_table.end()) { @@ -5860,26 +5836,25 @@ bool ClassLinker::LinkInterfaceMethods( auto* resolved_methods = klass->GetDexCache()->GetResolvedMethods(); for (size_t i = 0, count = klass->GetDexCache()->NumResolvedMethods(); i < count; ++i) { auto* m = mirror::DexCache::GetElementPtrSize(resolved_methods, i, image_pointer_size_); - CHECK(move_table.find(m) == move_table.end() || - // The original versions of copied methods will still be present so allow those too. - // Note that if the first check passes this might fail to GetDeclaringClass(). - std::find_if(m->GetDeclaringClass()->GetMethods(image_pointer_size_).begin(), - m->GetDeclaringClass()->GetMethods(image_pointer_size_).end(), - [m] (ArtMethod& meth) { - return &meth == m; - }) != m->GetDeclaringClass()->GetMethods(image_pointer_size_).end()) - << "Obsolete methods " << PrettyMethod(m) << " is in dex cache!"; + // We don't remove default methods from the move table since we need them to update the + // vtable. Therefore just skip them for this check. + if (!m->IsDefault()) { + CHECK(move_table.find(m) == move_table.end()) << PrettyMethod(m); + } } } - // Put some random garbage in old methods to help find stale pointers. - if (methods != old_methods && old_methods != nullptr) { - memset(old_methods, 0xFEu, old_size); + // Put some random garbage in old virtuals to help find stale pointers. + if (virtuals != old_virtuals) { + memset(old_virtuals, 0xFEu, old_size); } } else { self->EndAssertNoThreadSuspension(old_cause); } if (kIsDebugBuild) { - SanityCheckVTable(klass, image_pointer_size_); + auto* check_vtable = klass->GetVTableDuringLinking(); + for (int i = 0; i < check_vtable->GetLength(); ++i) { + CHECK(check_vtable->GetElementPtrSize<ArtMethod*>(i, image_pointer_size_) != nullptr); + } } return true; } @@ -5954,20 +5929,6 @@ bool ClassLinker::LinkFields(Thread* self, // we want a relatively stable order so that adding new fields // minimizes disruption of C++ version such as Class and Method. - // - // The overall sort order order is: - // 1) All object reference fields, sorted alphabetically. - // 2) All java long (64-bit) integer fields, sorted alphabetically. - // 3) All java double (64-bit) floating point fields, sorted alphabetically. - // 4) All java int (32-bit) integer fields, sorted alphabetically. - // 5) All java float (32-bit) floating point fields, sorted alphabetically. - // 6) All java char (16-bit) integer fields, sorted alphabetically. - // 7) All java short (16-bit) integer fields, sorted alphabetically. - // 8) All java boolean (8-bit) integer fields, sorted alphabetically. - // 9) All java byte (8-bit) integer fields, sorted alphabetically. - // - // Once the fields are sorted in this order we will attempt to fill any gaps that might be present - // in the memory layout of the structure. See ShuffleForward for how this is done. std::deque<ArtField*> grouped_and_sorted_fields; const char* old_no_suspend_cause = self->StartAssertNoThreadSuspension( "Naked ArtField references in deque"); diff --git a/runtime/class_linker.h b/runtime/class_linker.h index f16fe92d80..0d3bc1e2c3 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -983,8 +983,8 @@ class ClassLinker { bool CanWeInitializeClass(mirror::Class* klass, bool can_init_statics, bool can_init_parents) SHARED_REQUIRES(Locks::mutator_lock_); - void UpdateClassMethods(mirror::Class* klass, - LengthPrefixedArray<ArtMethod>* new_methods) + void UpdateClassVirtualMethods(mirror::Class* klass, + LengthPrefixedArray<ArtMethod>* new_methods) SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!Locks::classlinker_classes_lock_); diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc index 387ac0aee2..2c086c59f0 100644 --- a/runtime/class_linker_test.cc +++ b/runtime/class_linker_test.cc @@ -254,20 +254,10 @@ class ClassLinkerTest : public CommonRuntimeTest { EXPECT_EQ(klass.Get(), method.GetDeclaringClass()); } - for (ArtMethod& method : klass->GetDeclaredVirtualMethods(sizeof(void*))) { + for (ArtMethod& method : klass->GetVirtualMethods(sizeof(void*))) { AssertMethod(&method); EXPECT_FALSE(method.IsDirect()); - EXPECT_EQ(klass.Get(), method.GetDeclaringClass()); - } - - for (ArtMethod& method : klass->GetCopiedMethods(sizeof(void*))) { - AssertMethod(&method); - EXPECT_FALSE(method.IsDirect()); - EXPECT_TRUE(method.IsMiranda() || method.IsDefault() || method.IsDefaultConflicting()); - EXPECT_TRUE(method.GetDeclaringClass()->IsInterface()) - << "declaring class: " << PrettyClass(method.GetDeclaringClass()); - EXPECT_TRUE(method.GetDeclaringClass()->IsAssignableFrom(klass.Get())) - << "declaring class: " << PrettyClass(method.GetDeclaringClass()); + EXPECT_TRUE(method.GetDeclaringClass()->IsAssignableFrom(klass.Get())); } for (size_t i = 0; i < klass->NumInstanceFields(); i++) { @@ -506,14 +496,13 @@ struct ClassOffsets : public CheckOffsets<mirror::Class> { addOffset(OFFSETOF_MEMBER(mirror::Class, class_size_), "classSize"); addOffset(OFFSETOF_MEMBER(mirror::Class, clinit_thread_id_), "clinitThreadId"); addOffset(OFFSETOF_MEMBER(mirror::Class, component_type_), "componentType"); - addOffset(OFFSETOF_MEMBER(mirror::Class, copied_methods_offset_), "copiedMethodsOffset"); addOffset(OFFSETOF_MEMBER(mirror::Class, dex_cache_), "dexCache"); addOffset(OFFSETOF_MEMBER(mirror::Class, dex_cache_strings_), "dexCacheStrings"); addOffset(OFFSETOF_MEMBER(mirror::Class, dex_class_def_idx_), "dexClassDefIndex"); addOffset(OFFSETOF_MEMBER(mirror::Class, dex_type_idx_), "dexTypeIndex"); + addOffset(OFFSETOF_MEMBER(mirror::Class, direct_methods_), "directMethods"); addOffset(OFFSETOF_MEMBER(mirror::Class, ifields_), "iFields"); addOffset(OFFSETOF_MEMBER(mirror::Class, iftable_), "ifTable"); - addOffset(OFFSETOF_MEMBER(mirror::Class, methods_), "methods"); addOffset(OFFSETOF_MEMBER(mirror::Class, name_), "name"); addOffset(OFFSETOF_MEMBER(mirror::Class, num_reference_instance_fields_), "numReferenceInstanceFields"); @@ -527,7 +516,7 @@ struct ClassOffsets : public CheckOffsets<mirror::Class> { addOffset(OFFSETOF_MEMBER(mirror::Class, status_), "status"); addOffset(OFFSETOF_MEMBER(mirror::Class, super_class_), "superClass"); addOffset(OFFSETOF_MEMBER(mirror::Class, verify_error_), "verifyError"); - addOffset(OFFSETOF_MEMBER(mirror::Class, virtual_methods_offset_), "virtualMethodsOffset"); + addOffset(OFFSETOF_MEMBER(mirror::Class, virtual_methods_), "virtualMethods"); addOffset(OFFSETOF_MEMBER(mirror::Class, vtable_), "vtable"); }; }; @@ -1133,7 +1122,10 @@ static void CheckPreverified(mirror::Class* c, bool preverified) SHARED_REQUIRES(Locks::mutator_lock_) { EXPECT_EQ((c->GetAccessFlags() & kAccPreverified) != 0U, preverified) << "Class " << PrettyClass(c) << " not as expected"; - for (auto& m : c->GetMethods(sizeof(void*))) { + for (auto& m : c->GetDirectMethods(sizeof(void*))) { + CheckMethod(&m, preverified); + } + for (auto& m : c->GetVirtualMethods(sizeof(void*))) { CheckMethod(&m, preverified); } } diff --git a/runtime/code_simulator_container.cc b/runtime/code_simulator_container.cc new file mode 100644 index 0000000000..d884c58782 --- /dev/null +++ b/runtime/code_simulator_container.cc @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <dlfcn.h> + +#include "code_simulator_container.h" +#include "globals.h" + +namespace art { + +CodeSimulatorContainer::CodeSimulatorContainer(InstructionSet target_isa) + : libart_simulator_handle_(nullptr), + simulator_(nullptr) { + const char* libart_simulator_so_name = + kIsDebugBuild ? "libartd-simulator.so" : "libart-simulator.so"; + libart_simulator_handle_ = dlopen(libart_simulator_so_name, RTLD_NOW); + // It is not a real error when libart-simulator does not exist, e.g., on target. + if (libart_simulator_handle_ == nullptr) { + VLOG(simulator) << "Could not load " << libart_simulator_so_name << ": " << dlerror(); + } else { + typedef CodeSimulator* (*create_code_simulator_ptr_)(InstructionSet target_isa); + create_code_simulator_ptr_ create_code_simulator_ = + reinterpret_cast<create_code_simulator_ptr_>( + dlsym(libart_simulator_handle_, "CreateCodeSimulator")); + DCHECK(create_code_simulator_ != nullptr) << "Fail to find symbol of CreateCodeSimulator: " + << dlerror(); + simulator_ = create_code_simulator_(target_isa); + } +} + +CodeSimulatorContainer::~CodeSimulatorContainer() { + // Free simulator object before closing libart-simulator because destructor of + // CodeSimulator lives in it. + if (simulator_ != nullptr) { + delete simulator_; + } + if (libart_simulator_handle_ != nullptr) { + dlclose(libart_simulator_handle_); + } +} + +} // namespace art diff --git a/runtime/code_simulator_container.h b/runtime/code_simulator_container.h new file mode 100644 index 0000000000..655a2472f4 --- /dev/null +++ b/runtime/code_simulator_container.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_CODE_SIMULATOR_CONTAINER_H_ +#define ART_RUNTIME_CODE_SIMULATOR_CONTAINER_H_ + +#include "arch/instruction_set.h" +#include "simulator/code_simulator.h" + +namespace art { + +// This container dynamically opens and closes libart-simulator. +class CodeSimulatorContainer { + public: + explicit CodeSimulatorContainer(InstructionSet target_isa); + ~CodeSimulatorContainer(); + + bool CanSimulate() const { + return simulator_ != nullptr; + } + + CodeSimulator* Get() { + DCHECK(CanSimulate()); + return simulator_; + } + + const CodeSimulator* Get() const { + DCHECK(CanSimulate()); + return simulator_; + } + + private: + void* libart_simulator_handle_; + CodeSimulator* simulator_; + + DISALLOW_COPY_AND_ASSIGN(CodeSimulatorContainer); +}; + +} // namespace art + +#endif // ART_RUNTIME_CODE_SIMULATOR_CONTAINER_H_ diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc index 915d9ab5e7..87e29ae3c3 100644 --- a/runtime/entrypoints/entrypoint_utils.cc +++ b/runtime/entrypoints/entrypoint_utils.cc @@ -306,13 +306,11 @@ JValue InvokeProxyInvocationHandler(ScopedObjectAccessAlreadyRunnable& soa, cons mirror::Method* interface_method = soa.Decode<mirror::Method*>(interface_method_jobj); ArtMethod* proxy_method = rcvr->GetClass()->FindVirtualMethodForInterface( interface_method->GetArtMethod(), sizeof(void*)); - auto virtual_methods = proxy_class->GetVirtualMethodsSlice(sizeof(void*)); + auto* virtual_methods = proxy_class->GetVirtualMethodsPtr(); size_t num_virtuals = proxy_class->NumVirtualMethods(); size_t method_size = ArtMethod::Size(sizeof(void*)); - // Rely on the fact that the methods are contiguous to determine the index of the method in - // the slice. int throws_index = (reinterpret_cast<uintptr_t>(proxy_method) - - reinterpret_cast<uintptr_t>(&virtual_methods.At(0))) / method_size; + reinterpret_cast<uintptr_t>(virtual_methods)) / method_size; CHECK_LT(throws_index, static_cast<int>(num_virtuals)); mirror::ObjectArray<mirror::Class>* declared_exceptions = proxy_class->GetThrows()->Get(throws_index); diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc index cb67ee3b39..5e3fa199e5 100644 --- a/runtime/jni_internal.cc +++ b/runtime/jni_internal.cc @@ -316,7 +316,12 @@ template <bool kNative> static ArtMethod* FindMethod(mirror::Class* c, const StringPiece& name, const StringPiece& sig) SHARED_REQUIRES(Locks::mutator_lock_) { auto pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); - for (auto& method : c->GetMethods(pointer_size)) { + for (auto& method : c->GetDirectMethods(pointer_size)) { + if (kNative == method.IsNative() && name == method.GetName() && method.GetSignature() == sig) { + return &method; + } + } + for (auto& method : c->GetVirtualMethods(pointer_size)) { if (kNative == method.IsNative() && name == method.GetName() && method.GetSignature() == sig) { return &method; } @@ -2215,7 +2220,13 @@ class JNI { size_t unregistered_count = 0; auto pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); - for (auto& m : c->GetMethods(pointer_size)) { + for (auto& m : c->GetDirectMethods(pointer_size)) { + if (m.IsNative()) { + m.UnregisterNative(); + unregistered_count++; + } + } + for (auto& m : c->GetVirtualMethods(pointer_size)) { if (m.IsNative()) { m.UnregisterNative(); unregistered_count++; diff --git a/runtime/base/length_prefixed_array.h b/runtime/length_prefixed_array.h index d6328717e6..e01b6ccd26 100644 --- a/runtime/base/length_prefixed_array.h +++ b/runtime/length_prefixed_array.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_BASE_LENGTH_PREFIXED_ARRAY_H_ -#define ART_RUNTIME_BASE_LENGTH_PREFIXED_ARRAY_H_ +#ifndef ART_RUNTIME_LENGTH_PREFIXED_ARRAY_H_ +#define ART_RUNTIME_LENGTH_PREFIXED_ARRAY_H_ #include <stddef.h> // for offsetof() @@ -110,4 +110,4 @@ IterationRange<StrideIterator<T>> MakeIterationRangeFromLengthPrefixedArray( } // namespace art -#endif // ART_RUNTIME_BASE_LENGTH_PREFIXED_ARRAY_H_ +#endif // ART_RUNTIME_LENGTH_PREFIXED_ARRAY_H_ diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h index ef4fe15cc1..9e416dc888 100644 --- a/runtime/mirror/class-inl.h +++ b/runtime/mirror/class-inl.h @@ -22,14 +22,13 @@ #include "art_field-inl.h" #include "art_method.h" #include "art_method-inl.h" -#include "base/array_slice.h" -#include "base/length_prefixed_array.h" #include "class_loader.h" #include "common_throws.h" #include "dex_cache.h" #include "dex_file.h" #include "gc/heap-inl.h" #include "iftable.h" +#include "length_prefixed_array.h" #include "object_array-inl.h" #include "read_barrier-inl.h" #include "reference-inl.h" @@ -63,148 +62,61 @@ inline DexCache* Class::GetDexCache() { return GetFieldObject<DexCache, kVerifyFlags>(OFFSET_OF_OBJECT_MEMBER(Class, dex_cache_)); } -inline uint32_t Class::GetCopiedMethodsStartOffset() { - return GetFieldShort(OFFSET_OF_OBJECT_MEMBER(Class, copied_methods_offset_)); -} - -inline uint32_t Class::GetDirectMethodsStartOffset() { - return 0; -} - -inline uint32_t Class::GetVirtualMethodsStartOffset() { - return GetFieldShort(OFFSET_OF_OBJECT_MEMBER(Class, virtual_methods_offset_)); -} - -template<VerifyObjectFlags kVerifyFlags> -inline ArraySlice<ArtMethod> Class::GetDirectMethodsSlice(size_t pointer_size) { +inline LengthPrefixedArray<ArtMethod>* Class::GetDirectMethodsPtr() { DCHECK(IsLoaded() || IsErroneous()); - DCHECK(ValidPointerSize(pointer_size)) << pointer_size; - return GetDirectMethodsSliceUnchecked(pointer_size); + return GetDirectMethodsPtrUnchecked(); } -inline ArraySlice<ArtMethod> Class::GetDirectMethodsSliceUnchecked(size_t pointer_size) { - return ArraySlice<ArtMethod>(GetMethodsPtr(), - GetDirectMethodsStartOffset(), - GetVirtualMethodsStartOffset(), - ArtMethod::Size(pointer_size), - ArtMethod::Alignment(pointer_size)); -} - -template<VerifyObjectFlags kVerifyFlags> -inline ArraySlice<ArtMethod> Class::GetDeclaredMethodsSlice(size_t pointer_size) { - DCHECK(IsLoaded() || IsErroneous()); - DCHECK(ValidPointerSize(pointer_size)) << pointer_size; - return GetDeclaredMethodsSliceUnchecked(pointer_size); -} - -inline ArraySlice<ArtMethod> Class::GetDeclaredMethodsSliceUnchecked(size_t pointer_size) { - return ArraySlice<ArtMethod>(GetMethodsPtr(), - GetDirectMethodsStartOffset(), - GetCopiedMethodsStartOffset(), - ArtMethod::Size(pointer_size), - ArtMethod::Alignment(pointer_size)); -} -template<VerifyObjectFlags kVerifyFlags> -inline ArraySlice<ArtMethod> Class::GetDeclaredVirtualMethodsSlice(size_t pointer_size) { - DCHECK(IsLoaded() || IsErroneous()); - DCHECK(ValidPointerSize(pointer_size)) << pointer_size; - return GetDeclaredVirtualMethodsSliceUnchecked(pointer_size); -} - -inline ArraySlice<ArtMethod> Class::GetDeclaredVirtualMethodsSliceUnchecked(size_t pointer_size) { - return ArraySlice<ArtMethod>(GetMethodsPtr(), - GetVirtualMethodsStartOffset(), - GetCopiedMethodsStartOffset(), - ArtMethod::Size(pointer_size), - ArtMethod::Alignment(pointer_size)); -} - -template<VerifyObjectFlags kVerifyFlags> -inline ArraySlice<ArtMethod> Class::GetVirtualMethodsSlice(size_t pointer_size) { - DCHECK(IsLoaded() || IsErroneous()); - DCHECK(ValidPointerSize(pointer_size)) << pointer_size; - return GetVirtualMethodsSliceUnchecked(pointer_size); -} - -inline ArraySlice<ArtMethod> Class::GetVirtualMethodsSliceUnchecked(size_t pointer_size) { - LengthPrefixedArray<ArtMethod>* methods = GetMethodsPtr(); - return ArraySlice<ArtMethod>(methods, - GetVirtualMethodsStartOffset(), - NumMethods(), - ArtMethod::Size(pointer_size), - ArtMethod::Alignment(pointer_size)); -} - -template<VerifyObjectFlags kVerifyFlags> -inline ArraySlice<ArtMethod> Class::GetCopiedMethodsSlice(size_t pointer_size) { - DCHECK(IsLoaded() || IsErroneous()); - DCHECK(ValidPointerSize(pointer_size)) << pointer_size; - return GetCopiedMethodsSliceUnchecked(pointer_size); -} - -inline ArraySlice<ArtMethod> Class::GetCopiedMethodsSliceUnchecked(size_t pointer_size) { - LengthPrefixedArray<ArtMethod>* methods = GetMethodsPtr(); - return ArraySlice<ArtMethod>(methods, - GetCopiedMethodsStartOffset(), - NumMethods(), - ArtMethod::Size(pointer_size), - ArtMethod::Alignment(pointer_size)); +inline LengthPrefixedArray<ArtMethod>* Class::GetDirectMethodsPtrUnchecked() { + return reinterpret_cast<LengthPrefixedArray<ArtMethod>*>( + GetField64(OFFSET_OF_OBJECT_MEMBER(Class, direct_methods_))); } -inline LengthPrefixedArray<ArtMethod>* Class::GetMethodsPtr() { +inline LengthPrefixedArray<ArtMethod>* Class::GetVirtualMethodsPtrUnchecked() { return reinterpret_cast<LengthPrefixedArray<ArtMethod>*>( - GetField64(OFFSET_OF_OBJECT_MEMBER(Class, methods_))); + GetField64(OFFSET_OF_OBJECT_MEMBER(Class, virtual_methods_))); } -template<VerifyObjectFlags kVerifyFlags> -inline ArraySlice<ArtMethod> Class::GetMethodsSlice(size_t pointer_size) { - DCHECK(IsLoaded() || IsErroneous()); - LengthPrefixedArray<ArtMethod>* methods = GetMethodsPtr(); - return ArraySlice<ArtMethod>(methods, - 0, - NumMethods(), - ArtMethod::Size(pointer_size), - ArtMethod::Alignment(pointer_size)); +inline void Class::SetDirectMethodsPtr(LengthPrefixedArray<ArtMethod>* new_direct_methods) { + DCHECK(GetDirectMethodsPtrUnchecked() == nullptr); + SetDirectMethodsPtrUnchecked(new_direct_methods); } - -inline uint32_t Class::NumMethods() { - LengthPrefixedArray<ArtMethod>* methods = GetMethodsPtr(); - return (methods == nullptr) ? 0 : methods->size(); +inline void Class::SetDirectMethodsPtrUnchecked( + LengthPrefixedArray<ArtMethod>* new_direct_methods) { + SetField64<false>(OFFSET_OF_OBJECT_MEMBER(Class, direct_methods_), + reinterpret_cast<uint64_t>(new_direct_methods)); } inline ArtMethod* Class::GetDirectMethodUnchecked(size_t i, size_t pointer_size) { CheckPointerSize(pointer_size); - return &GetDirectMethodsSliceUnchecked(pointer_size).At(i); + auto* methods = GetDirectMethodsPtrUnchecked(); + DCHECK(methods != nullptr); + return &methods->At(i, + ArtMethod::Size(pointer_size), + ArtMethod::Alignment(pointer_size)); } inline ArtMethod* Class::GetDirectMethod(size_t i, size_t pointer_size) { CheckPointerSize(pointer_size); - return &GetDirectMethodsSlice(pointer_size).At(i); -} - -inline void Class::SetMethodsPtr(LengthPrefixedArray<ArtMethod>* new_methods, - uint32_t num_direct, - uint32_t num_virtual) { - DCHECK(GetMethodsPtr() == nullptr); - SetMethodsPtrUnchecked(new_methods, num_direct, num_virtual); + auto* methods = GetDirectMethodsPtr(); + DCHECK(methods != nullptr); + return &methods->At(i, + ArtMethod::Size(pointer_size), + ArtMethod::Alignment(pointer_size)); } - -inline void Class::SetMethodsPtrUnchecked(LengthPrefixedArray<ArtMethod>* new_methods, - uint32_t num_direct, - uint32_t num_virtual) { - DCHECK_LE(num_direct + num_virtual, (new_methods == nullptr) ? 0 : new_methods->size()); - SetMethodsPtrInternal(new_methods); - SetFieldShort<false>(OFFSET_OF_OBJECT_MEMBER(Class, copied_methods_offset_), - dchecked_integral_cast<uint16_t>(num_direct + num_virtual)); - SetFieldShort<false>(OFFSET_OF_OBJECT_MEMBER(Class, virtual_methods_offset_), - dchecked_integral_cast<uint16_t>(num_direct)); +template<VerifyObjectFlags kVerifyFlags> +inline LengthPrefixedArray<ArtMethod>* Class::GetVirtualMethodsPtr() { + DCHECK(IsLoaded<kVerifyFlags>() || IsErroneous<kVerifyFlags>()); + return GetVirtualMethodsPtrUnchecked(); } -inline void Class::SetMethodsPtrInternal(LengthPrefixedArray<ArtMethod>* new_methods) { - SetField64<false>(OFFSET_OF_OBJECT_MEMBER(Class, methods_), - reinterpret_cast<uint64_t>(new_methods)); +inline void Class::SetVirtualMethodsPtr(LengthPrefixedArray<ArtMethod>* new_virtual_methods) { + // TODO: we reassign virtual methods to grow the table for miranda + // methods.. they should really just be assigned once. + SetField64<false>(OFFSET_OF_OBJECT_MEMBER(Class, virtual_methods_), + reinterpret_cast<uint64_t>(new_virtual_methods)); } template<VerifyObjectFlags kVerifyFlags> @@ -223,7 +135,11 @@ inline ArtMethod* Class::GetVirtualMethodDuringLinking(size_t i, size_t pointer_ inline ArtMethod* Class::GetVirtualMethodUnchecked(size_t i, size_t pointer_size) { CheckPointerSize(pointer_size); - return &GetVirtualMethodsSliceUnchecked(pointer_size).At(i); + LengthPrefixedArray<ArtMethod>* methods = GetVirtualMethodsPtrUnchecked(); + DCHECK(methods != nullptr); + return &methods->At(i, + ArtMethod::Size(pointer_size), + ArtMethod::Alignment(pointer_size)); } inline PointerArray* Class::GetVTable() { @@ -917,42 +833,24 @@ void mirror::Class::VisitNativeRoots(Visitor& visitor, size_t pointer_size) { CHECK_EQ(field.GetDeclaringClass(), this) << GetStatus(); } } - for (ArtMethod& method : GetMethods(pointer_size)) { + for (ArtMethod& method : GetDirectMethods(pointer_size)) { + method.VisitRoots(visitor, pointer_size); + } + for (ArtMethod& method : GetVirtualMethods(pointer_size)) { method.VisitRoots(visitor, pointer_size); } } inline IterationRange<StrideIterator<ArtMethod>> Class::GetDirectMethods(size_t pointer_size) { CheckPointerSize(pointer_size); - return GetDirectMethodsSliceUnchecked(pointer_size).AsRange(); -} - -inline IterationRange<StrideIterator<ArtMethod>> Class::GetDeclaredMethods( - size_t pointer_size) { - CheckPointerSize(pointer_size); - return GetDeclaredMethodsSliceUnchecked(pointer_size).AsRange(); -} - -inline IterationRange<StrideIterator<ArtMethod>> Class::GetDeclaredVirtualMethods( - size_t pointer_size) { - CheckPointerSize(pointer_size); - return GetDeclaredVirtualMethodsSliceUnchecked(pointer_size).AsRange(); + return MakeIterationRangeFromLengthPrefixedArray(GetDirectMethodsPtrUnchecked(), + ArtMethod::Size(pointer_size), + ArtMethod::Alignment(pointer_size)); } inline IterationRange<StrideIterator<ArtMethod>> Class::GetVirtualMethods(size_t pointer_size) { CheckPointerSize(pointer_size); - return GetVirtualMethodsSliceUnchecked(pointer_size).AsRange(); -} - -inline IterationRange<StrideIterator<ArtMethod>> Class::GetCopiedMethods(size_t pointer_size) { - CheckPointerSize(pointer_size); - return GetCopiedMethodsSliceUnchecked(pointer_size).AsRange(); -} - - -inline IterationRange<StrideIterator<ArtMethod>> Class::GetMethods(size_t pointer_size) { - CheckPointerSize(pointer_size); - return MakeIterationRangeFromLengthPrefixedArray(GetMethodsPtr(), + return MakeIterationRangeFromLengthPrefixedArray(GetVirtualMethodsPtrUnchecked(), ArtMethod::Size(pointer_size), ArtMethod::Alignment(pointer_size)); } @@ -1020,15 +918,13 @@ inline bool Class::IsAssignableFrom(Class* src) { } inline uint32_t Class::NumDirectMethods() { - return GetVirtualMethodsStartOffset(); -} - -inline uint32_t Class::NumDeclaredVirtualMethods() { - return GetCopiedMethodsStartOffset() - GetVirtualMethodsStartOffset(); + LengthPrefixedArray<ArtMethod>* arr = GetDirectMethodsPtrUnchecked(); + return arr != nullptr ? arr->size() : 0u; } inline uint32_t Class::NumVirtualMethods() { - return NumMethods() - GetVirtualMethodsStartOffset(); + LengthPrefixedArray<ArtMethod>* arr = GetVirtualMethodsPtrUnchecked(); + return arr != nullptr ? arr->size() : 0u; } inline uint32_t Class::NumInstanceFields() { @@ -1056,11 +952,16 @@ inline void Class::FixupNativePointers(mirror::Class* dest, if (ifields != new_ifields) { dest->SetIFieldsPtrUnchecked(new_ifields); } - // Update method array. - LengthPrefixedArray<ArtMethod>* methods = GetMethodsPtr(); - LengthPrefixedArray<ArtMethod>* new_methods = visitor(methods); - if (methods != new_methods) { - dest->SetMethodsPtrInternal(new_methods); + // Update direct and virtual method arrays. + LengthPrefixedArray<ArtMethod>* direct_methods = GetDirectMethodsPtr(); + LengthPrefixedArray<ArtMethod>* new_direct_methods = visitor(direct_methods); + if (direct_methods != new_direct_methods) { + dest->SetDirectMethodsPtrUnchecked(new_direct_methods); + } + LengthPrefixedArray<ArtMethod>* virtual_methods = GetVirtualMethodsPtr(); + LengthPrefixedArray<ArtMethod>* new_virtual_methods = visitor(virtual_methods); + if (virtual_methods != new_virtual_methods) { + dest->SetVirtualMethodsPtr(new_virtual_methods); } // Update dex cache strings. GcRoot<mirror::String>* strings = GetDexCacheStrings(); diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc index 66060f2221..05a9039ae9 100644 --- a/runtime/mirror/class.cc +++ b/runtime/mirror/class.cc @@ -457,10 +457,6 @@ ArtMethod* Class::FindDirectMethod( return nullptr; } -// TODO These should maybe be changed to be named FindOwnedVirtualMethod or something similar -// because they do not only find 'declared' methods and will return copied methods. This behavior is -// desired and correct but the naming can lead to confusion because in the java language declared -// excludes interface methods which might be found by this. ArtMethod* Class::FindDeclaredVirtualMethod(const StringPiece& name, const StringPiece& signature, size_t pointer_size) { for (auto& method : GetVirtualMethods(pointer_size)) { @@ -486,8 +482,10 @@ ArtMethod* Class::FindDeclaredVirtualMethod(const StringPiece& name, const Signa ArtMethod* Class::FindDeclaredVirtualMethod(const DexCache* dex_cache, uint32_t dex_method_idx, size_t pointer_size) { if (GetDexCache() == dex_cache) { - for (auto& method : GetDeclaredVirtualMethods(pointer_size)) { - if (method.GetDexMethodIndex() == dex_method_idx) { + for (auto& method : GetVirtualMethods(pointer_size)) { + // A miranda method may have a different DexCache and is always created by linking, + // never *declared* in the class. + if (method.GetDexMethodIndex() == dex_method_idx && !method.IsMiranda()) { return &method; } } @@ -727,7 +725,12 @@ ArtField* Class::FindField(Thread* self, Handle<Class> klass, const StringPiece& void Class::SetPreverifiedFlagOnAllMethods(size_t pointer_size) { DCHECK(IsVerified()); - for (auto& m : GetMethods(pointer_size)) { + for (auto& m : GetDirectMethods(pointer_size)) { + if (!m.IsNative() && m.IsInvokable()) { + m.SetPreverified(); + } + } + for (auto& m : GetVirtualMethods(pointer_size)) { if (!m.IsNative() && m.IsInvokable()) { m.SetPreverified(); } diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h index ce879ba2ee..0ab5b97d72 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -44,7 +44,6 @@ class ArtMethod; struct ClassOffsets; template<class T> class Handle; template<typename T> class LengthPrefixedArray; -template<typename T> class ArraySlice; class Signature; class StringPiece; template<size_t kNumReferences> class PACKED(4) StackHandleScope; @@ -703,24 +702,12 @@ class MANAGED Class FINAL : public Object { ALWAYS_INLINE IterationRange<StrideIterator<ArtMethod>> GetDirectMethods(size_t pointer_size) SHARED_REQUIRES(Locks::mutator_lock_); - ALWAYS_INLINE LengthPrefixedArray<ArtMethod>* GetMethodsPtr() - SHARED_REQUIRES(Locks::mutator_lock_); - - ALWAYS_INLINE IterationRange<StrideIterator<ArtMethod>> GetMethods(size_t pointer_size) - SHARED_REQUIRES(Locks::mutator_lock_); + LengthPrefixedArray<ArtMethod>* GetDirectMethodsPtr() SHARED_REQUIRES(Locks::mutator_lock_); - void SetMethodsPtr(LengthPrefixedArray<ArtMethod>* new_methods, - uint32_t num_direct, - uint32_t num_virtual) + void SetDirectMethodsPtr(LengthPrefixedArray<ArtMethod>* new_direct_methods) SHARED_REQUIRES(Locks::mutator_lock_); // Used by image writer. - void SetMethodsPtrUnchecked(LengthPrefixedArray<ArtMethod>* new_methods, - uint32_t num_direct, - uint32_t num_virtual) - SHARED_REQUIRES(Locks::mutator_lock_); - - template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> - ALWAYS_INLINE ArraySlice<ArtMethod> GetDirectMethodsSlice(size_t pointer_size) + void SetDirectMethodsPtrUnchecked(LengthPrefixedArray<ArtMethod>* new_direct_methods) SHARED_REQUIRES(Locks::mutator_lock_); ALWAYS_INLINE ArtMethod* GetDirectMethod(size_t i, size_t pointer_size) @@ -736,50 +723,18 @@ class MANAGED Class FINAL : public Object { ALWAYS_INLINE uint32_t NumDirectMethods() SHARED_REQUIRES(Locks::mutator_lock_); template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> - ALWAYS_INLINE ArraySlice<ArtMethod> GetMethodsSlice(size_t pointer_size) - SHARED_REQUIRES(Locks::mutator_lock_); - - template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> - ALWAYS_INLINE ArraySlice<ArtMethod> GetDeclaredMethodsSlice(size_t pointer_size) - SHARED_REQUIRES(Locks::mutator_lock_); - - ALWAYS_INLINE IterationRange<StrideIterator<ArtMethod>> GetDeclaredMethods( - size_t pointer_size) - SHARED_REQUIRES(Locks::mutator_lock_); - - template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> - ALWAYS_INLINE ArraySlice<ArtMethod> GetDeclaredVirtualMethodsSlice(size_t pointer_size) - SHARED_REQUIRES(Locks::mutator_lock_); - - ALWAYS_INLINE IterationRange<StrideIterator<ArtMethod>> GetDeclaredVirtualMethods( - size_t pointer_size) - SHARED_REQUIRES(Locks::mutator_lock_); - - template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> - ALWAYS_INLINE ArraySlice<ArtMethod> GetCopiedMethodsSlice(size_t pointer_size) - SHARED_REQUIRES(Locks::mutator_lock_); - - ALWAYS_INLINE IterationRange<StrideIterator<ArtMethod>> GetCopiedMethods(size_t pointer_size) + ALWAYS_INLINE LengthPrefixedArray<ArtMethod>* GetVirtualMethodsPtr() SHARED_REQUIRES(Locks::mutator_lock_); - template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> - ALWAYS_INLINE ArraySlice<ArtMethod> GetVirtualMethodsSlice(size_t pointer_size) + ALWAYS_INLINE IterationRange<StrideIterator<ArtMethod>> GetVirtualMethods(size_t pointer_size) SHARED_REQUIRES(Locks::mutator_lock_); - ALWAYS_INLINE IterationRange<StrideIterator<ArtMethod>> GetVirtualMethods(size_t pointer_size) + void SetVirtualMethodsPtr(LengthPrefixedArray<ArtMethod>* new_virtual_methods) SHARED_REQUIRES(Locks::mutator_lock_); - // Returns the number of non-inherited virtual methods (sum of declared and copied methods). + // Returns the number of non-inherited virtual methods. ALWAYS_INLINE uint32_t NumVirtualMethods() SHARED_REQUIRES(Locks::mutator_lock_); - // Returns the number of copied virtual methods. - ALWAYS_INLINE uint32_t NumCopiedVirtualMethods() SHARED_REQUIRES(Locks::mutator_lock_); - - // Returns the number of declared virtual methods. - ALWAYS_INLINE uint32_t NumDeclaredVirtualMethods() SHARED_REQUIRES(Locks::mutator_lock_); - - ALWAYS_INLINE uint32_t NumMethods() SHARED_REQUIRES(Locks::mutator_lock_); - template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> ArtMethod* GetVirtualMethod(size_t i, size_t pointer_size) SHARED_REQUIRES(Locks::mutator_lock_); @@ -1200,19 +1155,10 @@ class MANAGED Class FINAL : public Object { return pointer_size; } - ALWAYS_INLINE ArraySlice<ArtMethod> GetDirectMethodsSliceUnchecked(size_t pointer_size) - SHARED_REQUIRES(Locks::mutator_lock_); - - ALWAYS_INLINE ArraySlice<ArtMethod> GetVirtualMethodsSliceUnchecked(size_t pointer_size) - SHARED_REQUIRES(Locks::mutator_lock_); - - ALWAYS_INLINE ArraySlice<ArtMethod> GetDeclaredMethodsSliceUnchecked(size_t pointer_size) - SHARED_REQUIRES(Locks::mutator_lock_); - - ALWAYS_INLINE ArraySlice<ArtMethod> GetDeclaredVirtualMethodsSliceUnchecked(size_t pointer_size) + ALWAYS_INLINE LengthPrefixedArray<ArtMethod>* GetDirectMethodsPtrUnchecked() SHARED_REQUIRES(Locks::mutator_lock_); - ALWAYS_INLINE ArraySlice<ArtMethod> GetCopiedMethodsSliceUnchecked(size_t pointer_size) + ALWAYS_INLINE LengthPrefixedArray<ArtMethod>* GetVirtualMethodsPtrUnchecked() SHARED_REQUIRES(Locks::mutator_lock_); // Fix up all of the native pointers in the class by running them through the visitor. Only sets @@ -1223,9 +1169,6 @@ class MANAGED Class FINAL : public Object { SHARED_REQUIRES(Locks::mutator_lock_); private: - ALWAYS_INLINE void SetMethodsPtrInternal(LengthPrefixedArray<ArtMethod>* new_methods) - SHARED_REQUIRES(Locks::mutator_lock_); - void SetVerifyError(Object* klass) SHARED_REQUIRES(Locks::mutator_lock_); template <bool throw_on_failure, bool use_referrers_cache> @@ -1251,15 +1194,6 @@ class MANAGED Class FINAL : public Object { IterationRange<StrideIterator<ArtField>> GetIFieldsUnchecked() SHARED_REQUIRES(Locks::mutator_lock_); - // The index in the methods_ array where the first declared virtual method is. - ALWAYS_INLINE uint32_t GetVirtualMethodsStartOffset() SHARED_REQUIRES(Locks::mutator_lock_); - - // The index in the methods_ array where the first direct method is. - ALWAYS_INLINE uint32_t GetDirectMethodsStartOffset() SHARED_REQUIRES(Locks::mutator_lock_); - - // The index in the methods_ array where the first copied method is. - ALWAYS_INLINE uint32_t GetCopiedMethodsStartOffset() SHARED_REQUIRES(Locks::mutator_lock_); - bool ProxyDescriptorEquals(const char* match) SHARED_REQUIRES(Locks::mutator_lock_); // Check that the pointer size matches the one in the class linker. @@ -1272,9 +1206,6 @@ class MANAGED Class FINAL : public Object { void VisitReferences(mirror::Class* klass, const Visitor& visitor) SHARED_REQUIRES(Locks::mutator_lock_); - // 'Class' Object Fields - // Order governed by java field ordering. See art::ClassLinker::LinkFields. - // Defining class loader, or null for the "bootstrap" system loader. HeapReference<ClassLoader> class_loader_; @@ -1323,6 +1254,9 @@ class MANAGED Class FINAL : public Object { // Short cuts to dex_cache_ member for fast compiled code access. uint64_t dex_cache_strings_; + // static, private, and <init> methods. Pointer to an ArtMethod length-prefixed array. + uint64_t direct_methods_; + // instance fields // // These describe the layout of the contents of an Object. @@ -1334,24 +1268,13 @@ class MANAGED Class FINAL : public Object { // ArtFields. uint64_t ifields_; - // Pointer to an ArtMethod length-prefixed array. All the methods where this class is the place - // where they are logically defined. This includes all private, static, final and virtual methods - // as well as inherited default methods and miranda methods. - // - // The slice methods_ [0, virtual_methods_offset_) are the direct (static, private, init) methods - // declared by this class. - // - // The slice methods_ [virtual_methods_offset_, copied_methods_offset_) are the virtual methods - // declared by this class. - // - // The slice methods_ [copied_methods_offset_, |methods_|) are the methods that are copied from - // interfaces such as miranda or default methods. These are copied for resolution purposes as this - // class is where they are (logically) declared as far as the virtual dispatch is concerned. - uint64_t methods_; - // Static fields length-prefixed array. uint64_t sfields_; + // Virtual methods defined in this class; invoked through vtable. Pointer to an ArtMethod + // length-prefixed array. + uint64_t virtual_methods_; + // Access flags; low 16 bits are defined by VM spec. uint32_t access_flags_; @@ -1394,14 +1317,6 @@ class MANAGED Class FINAL : public Object { // State of class initialization. Status status_; - // The offset of the first virtual method that is copied from an interface. This includes miranda, - // default, and default-conflict methods. Having a hard limit of ((2 << 16) - 1) for methods - // defined on a single class is well established in Java so we will use only uint16_t's here. - uint16_t copied_methods_offset_; - - // The offset of the first declared virtual methods in the methods_ array. - uint16_t virtual_methods_offset_; - // TODO: ? // initiating class loader list // NOTE: for classes with low serialNumber, these are unused, and the diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc index 14d284e003..5e423920c0 100644 --- a/runtime/native/java_lang_Class.cc +++ b/runtime/native/java_lang_Class.cc @@ -439,9 +439,16 @@ static jobjectArray Class_getDeclaredMethodsUnchecked(JNIEnv* env, jobject javaT StackHandleScope<2> hs(soa.Self()); Handle<mirror::Class> klass = hs.NewHandle(DecodeClass(soa, javaThis)); size_t num_methods = 0; - for (auto& m : klass->GetDeclaredMethods(sizeof(void*))) { + for (auto& m : klass->GetVirtualMethods(sizeof(void*))) { auto modifiers = m.GetAccessFlags(); - // Add non-constructor declared methods. + if ((publicOnly == JNI_FALSE || (modifiers & kAccPublic) != 0) && + (modifiers & kAccMiranda) == 0) { + ++num_methods; + } + } + for (auto& m : klass->GetDirectMethods(sizeof(void*))) { + auto modifiers = m.GetAccessFlags(); + // Add non-constructor direct/static methods. if ((publicOnly == JNI_FALSE || (modifiers & kAccPublic) != 0) && (modifiers & kAccConstructor) == 0) { ++num_methods; @@ -450,8 +457,21 @@ static jobjectArray Class_getDeclaredMethodsUnchecked(JNIEnv* env, jobject javaT auto ret = hs.NewHandle(mirror::ObjectArray<mirror::Method>::Alloc( soa.Self(), mirror::Method::ArrayClass(), num_methods)); num_methods = 0; - for (auto& m : klass->GetDeclaredMethods(sizeof(void*))) { + for (auto& m : klass->GetVirtualMethods(sizeof(void*))) { + auto modifiers = m.GetAccessFlags(); + if ((publicOnly == JNI_FALSE || (modifiers & kAccPublic) != 0) && + (modifiers & kAccMiranda) == 0) { + auto* method = mirror::Method::CreateFromArtMethod(soa.Self(), &m); + if (method == nullptr) { + soa.Self()->AssertPendingException(); + return nullptr; + } + ret->SetWithoutChecks<false>(num_methods++, method); + } + } + for (auto& m : klass->GetDirectMethods(sizeof(void*))) { auto modifiers = m.GetAccessFlags(); + // Add non-constructor direct/static methods. if ((publicOnly == JNI_FALSE || (modifiers & kAccPublic) != 0) && (modifiers & kAccConstructor) == 0) { auto* method = mirror::Method::CreateFromArtMethod(soa.Self(), &m); diff --git a/runtime/native/java_lang_reflect_Method.cc b/runtime/native/java_lang_reflect_Method.cc index d7cf62e994..caacba6ec3 100644 --- a/runtime/native/java_lang_reflect_Method.cc +++ b/runtime/native/java_lang_reflect_Method.cc @@ -71,7 +71,7 @@ static jobjectArray Method_getExceptionTypes(JNIEnv* env, jobject javaMethod) { mirror::Class* klass = method->GetDeclaringClass(); int throws_index = -1; size_t i = 0; - for (const auto& m : klass->GetDeclaredVirtualMethods(sizeof(void*))) { + for (const auto& m : klass->GetVirtualMethods(sizeof(void*))) { if (&m == method) { throws_index = i; break; diff --git a/runtime/native_bridge_art_interface.cc b/runtime/native_bridge_art_interface.cc index 61a1085c0e..46cc5aaff8 100644 --- a/runtime/native_bridge_art_interface.cc +++ b/runtime/native_bridge_art_interface.cc @@ -45,7 +45,10 @@ static uint32_t GetNativeMethodCount(JNIEnv* env, jclass clazz) { mirror::Class* c = soa.Decode<mirror::Class*>(clazz); uint32_t native_method_count = 0; - for (auto& m : c->GetMethods(sizeof(void*))) { + for (auto& m : c->GetDirectMethods(sizeof(void*))) { + native_method_count += m.IsNative() ? 1u : 0u; + } + for (auto& m : c->GetVirtualMethods(sizeof(void*))) { native_method_count += m.IsNative() ? 1u : 0u; } return native_method_count; @@ -60,7 +63,19 @@ static uint32_t GetNativeMethods(JNIEnv* env, jclass clazz, JNINativeMethod* met mirror::Class* c = soa.Decode<mirror::Class*>(clazz); uint32_t count = 0; - for (auto& m : c->GetMethods(sizeof(void*))) { + for (auto& m : c->GetDirectMethods(sizeof(void*))) { + if (m.IsNative()) { + if (count < method_count) { + methods[count].name = m.GetName(); + methods[count].signature = m.GetShorty(); + methods[count].fnPtr = m.GetEntryPointFromJni(); + count++; + } else { + LOG(WARNING) << "Output native method array too small. Skipping " << PrettyMethod(&m); + } + } + } + for (auto& m : c->GetVirtualMethods(sizeof(void*))) { if (m.IsNative()) { if (count < method_count) { methods[count].name = m.GetName(); diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc index 5b1061087d..2b92303fe2 100644 --- a/runtime/parsed_options.cc +++ b/runtime/parsed_options.cc @@ -399,6 +399,7 @@ static void MaybeOverrideVerbosity() { // gLogVerbosity.oat = true; // TODO: don't check this in! // gLogVerbosity.profiler = true; // TODO: don't check this in! // gLogVerbosity.signals = true; // TODO: don't check this in! + // gLogVerbosity.simulator = true; // TODO: don't check this in! // gLogVerbosity.startup = true; // TODO: don't check this in! // gLogVerbosity.third_party_jni = true; // TODO: don't check this in! // gLogVerbosity.threads = true; // TODO: don't check this in! diff --git a/runtime/parsed_options_test.cc b/runtime/parsed_options_test.cc index fad00c73d8..06b40fd925 100644 --- a/runtime/parsed_options_test.cc +++ b/runtime/parsed_options_test.cc @@ -87,6 +87,8 @@ TEST_F(ParsedOptionsTest, ParsedOptions) { EXPECT_FALSE(VLOG_IS_ON(jdwp)); EXPECT_TRUE(VLOG_IS_ON(jni)); EXPECT_FALSE(VLOG_IS_ON(monitor)); + EXPECT_FALSE(VLOG_IS_ON(signals)); + EXPECT_FALSE(VLOG_IS_ON(simulator)); EXPECT_FALSE(VLOG_IS_ON(startup)); EXPECT_FALSE(VLOG_IS_ON(third_party_jni)); EXPECT_FALSE(VLOG_IS_ON(threads)); diff --git a/runtime/proxy_test.cc b/runtime/proxy_test.cc index 4d9ca6d440..57472adb64 100644 --- a/runtime/proxy_test.cc +++ b/runtime/proxy_test.cc @@ -79,7 +79,7 @@ class ProxyTest : public CommonCompilerTest { mirror::Method::CreateFromArtMethod(soa.Self(), method))); // Now adds all interfaces virtual methods. for (mirror::Class* interface : interfaces) { - for (auto& m : interface->GetDeclaredVirtualMethods(sizeof(void*))) { + for (auto& m : interface->GetVirtualMethods(sizeof(void*))) { soa.Env()->SetObjectArrayElement( proxyClassMethods, array_index++, soa.AddLocalReference<jobject>( mirror::Method::CreateFromArtMethod(soa.Self(), &m))); diff --git a/runtime/simulator/Android.mk b/runtime/simulator/Android.mk new file mode 100644 index 0000000000..c154eb6346 --- /dev/null +++ b/runtime/simulator/Android.mk @@ -0,0 +1,105 @@ +# +# Copyright (C) 2015 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. +# + +LOCAL_PATH := $(call my-dir) + +include art/build/Android.common_build.mk + +LIBART_SIMULATOR_SRC_FILES := \ + code_simulator.cc \ + code_simulator_arm64.cc + +# $(1): target or host +# $(2): ndebug or debug +define build-libart-simulator + ifneq ($(1),target) + ifneq ($(1),host) + $$(error expected target or host for argument 1, received $(1)) + endif + endif + ifneq ($(2),ndebug) + ifneq ($(2),debug) + $$(error expected ndebug or debug for argument 2, received $(2)) + endif + endif + + art_target_or_host := $(1) + art_ndebug_or_debug := $(2) + + include $(CLEAR_VARS) + ifeq ($$(art_target_or_host),host) + LOCAL_IS_HOST_MODULE := true + endif + LOCAL_CPP_EXTENSION := $(ART_CPP_EXTENSION) + ifeq ($$(art_ndebug_or_debug),ndebug) + LOCAL_MODULE := libart-simulator + else # debug + LOCAL_MODULE := libartd-simulator + endif + + LOCAL_MODULE_TAGS := optional + LOCAL_MODULE_CLASS := SHARED_LIBRARIES + + LOCAL_SRC_FILES := $$(LIBART_SIMULATOR_SRC_FILES) + + ifeq ($$(art_target_or_host),target) + $(call set-target-local-clang-vars) + $(call set-target-local-cflags-vars,$(2)) + else # host + LOCAL_CLANG := $(ART_HOST_CLANG) + LOCAL_LDLIBS := $(ART_HOST_LDLIBS) + LOCAL_CFLAGS += $(ART_HOST_CFLAGS) + LOCAL_ASFLAGS += $(ART_HOST_ASFLAGS) + ifeq ($$(art_ndebug_or_debug),debug) + LOCAL_CFLAGS += $(ART_HOST_DEBUG_CFLAGS) + else + LOCAL_CFLAGS += $(ART_HOST_NON_DEBUG_CFLAGS) + endif + endif + + LOCAL_SHARED_LIBRARIES += liblog + ifeq ($$(art_ndebug_or_debug),debug) + LOCAL_SHARED_LIBRARIES += libartd + else + LOCAL_SHARED_LIBRARIES += libart + endif + + LOCAL_C_INCLUDES += $(ART_C_INCLUDES) art/runtime + LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH) + LOCAL_MULTILIB := both + + LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common_build.mk + LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk + LOCAL_NATIVE_COVERAGE := $(ART_COVERAGE) + # For simulator_arm64. + ifeq ($$(art_ndebug_or_debug),debug) + LOCAL_SHARED_LIBRARIES += libvixld + else + LOCAL_SHARED_LIBRARIES += libvixl + endif + ifeq ($$(art_target_or_host),target) + include $(BUILD_SHARED_LIBRARY) + else # host + include $(BUILD_HOST_SHARED_LIBRARY) + endif +endef + +ifeq ($(ART_BUILD_HOST_NDEBUG),true) + $(eval $(call build-libart-simulator,host,ndebug)) +endif +ifeq ($(ART_BUILD_HOST_DEBUG),true) + $(eval $(call build-libart-simulator,host,debug)) +endif diff --git a/runtime/simulator/code_simulator.cc b/runtime/simulator/code_simulator.cc new file mode 100644 index 0000000000..1a1116050e --- /dev/null +++ b/runtime/simulator/code_simulator.cc @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "simulator/code_simulator.h" +#include "simulator/code_simulator_arm64.h" + +namespace art { + +CodeSimulator* CodeSimulator::CreateCodeSimulator(InstructionSet target_isa) { + switch (target_isa) { + case kArm64: + return arm64::CodeSimulatorArm64::CreateCodeSimulatorArm64(); + default: + return nullptr; + } +} + +CodeSimulator* CreateCodeSimulator(InstructionSet target_isa) { + return CodeSimulator::CreateCodeSimulator(target_isa); +} + +} // namespace art diff --git a/runtime/simulator/code_simulator.h b/runtime/simulator/code_simulator.h new file mode 100644 index 0000000000..bd48909e41 --- /dev/null +++ b/runtime/simulator/code_simulator.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_SIMULATOR_CODE_SIMULATOR_H_ +#define ART_RUNTIME_SIMULATOR_CODE_SIMULATOR_H_ + +#include "arch/instruction_set.h" + +namespace art { + +class CodeSimulator { + public: + CodeSimulator() {} + virtual ~CodeSimulator() {} + // Returns a null pointer if a simulator cannot be found for target_isa. + static CodeSimulator* CreateCodeSimulator(InstructionSet target_isa); + + virtual void RunFrom(intptr_t code_buffer) = 0; + + // Get return value according to C ABI. + virtual bool GetCReturnBool() const = 0; + virtual int32_t GetCReturnInt32() const = 0; + virtual int64_t GetCReturnInt64() const = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(CodeSimulator); +}; + +extern "C" CodeSimulator* CreateCodeSimulator(InstructionSet target_isa); + +} // namespace art + +#endif // ART_RUNTIME_SIMULATOR_CODE_SIMULATOR_H_ diff --git a/runtime/simulator/code_simulator_arm64.cc b/runtime/simulator/code_simulator_arm64.cc new file mode 100644 index 0000000000..39dfa6dafb --- /dev/null +++ b/runtime/simulator/code_simulator_arm64.cc @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "simulator/code_simulator_arm64.h" + +namespace art { +namespace arm64 { + +// VIXL has not been tested on 32bit architectures, so vixl::Simulator is not always +// available. To avoid linker error on these architectures, we check if we can simulate +// in the beginning of following methods, with compile time constant `kCanSimulate`. +// TODO: when vixl::Simulator is always available, remove the these checks. + +CodeSimulatorArm64* CodeSimulatorArm64::CreateCodeSimulatorArm64() { + if (kCanSimulate) { + return new CodeSimulatorArm64(); + } else { + return nullptr; + } +} + +CodeSimulatorArm64::CodeSimulatorArm64() + : CodeSimulator(), decoder_(nullptr), simulator_(nullptr) { + DCHECK(kCanSimulate); + decoder_ = new vixl::Decoder(); + simulator_ = new vixl::Simulator(decoder_); +} + +CodeSimulatorArm64::~CodeSimulatorArm64() { + DCHECK(kCanSimulate); + delete simulator_; + delete decoder_; +} + +void CodeSimulatorArm64::RunFrom(intptr_t code_buffer) { + DCHECK(kCanSimulate); + simulator_->RunFrom(reinterpret_cast<const vixl::Instruction*>(code_buffer)); +} + +bool CodeSimulatorArm64::GetCReturnBool() const { + DCHECK(kCanSimulate); + return simulator_->wreg(0); +} + +int32_t CodeSimulatorArm64::GetCReturnInt32() const { + DCHECK(kCanSimulate); + return simulator_->wreg(0); +} + +int64_t CodeSimulatorArm64::GetCReturnInt64() const { + DCHECK(kCanSimulate); + return simulator_->xreg(0); +} + +} // namespace arm64 +} // namespace art diff --git a/runtime/simulator/code_simulator_arm64.h b/runtime/simulator/code_simulator_arm64.h new file mode 100644 index 0000000000..10fceb98f7 --- /dev/null +++ b/runtime/simulator/code_simulator_arm64.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_SIMULATOR_CODE_SIMULATOR_ARM64_H_ +#define ART_RUNTIME_SIMULATOR_CODE_SIMULATOR_ARM64_H_ + +#include "memory" +#include "simulator/code_simulator.h" +// TODO: make vixl clean wrt -Wshadow. +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wshadow" +#include "vixl/a64/simulator-a64.h" +#pragma GCC diagnostic pop + +namespace art { +namespace arm64 { + +class CodeSimulatorArm64 : public CodeSimulator { + public: + static CodeSimulatorArm64* CreateCodeSimulatorArm64(); + virtual ~CodeSimulatorArm64(); + + void RunFrom(intptr_t code_buffer) OVERRIDE; + + bool GetCReturnBool() const OVERRIDE; + int32_t GetCReturnInt32() const OVERRIDE; + int64_t GetCReturnInt64() const OVERRIDE; + + private: + CodeSimulatorArm64(); + + vixl::Decoder* decoder_; + vixl::Simulator* simulator_; + + // TODO: Enable CodeSimulatorArm64 for more host ISAs once vixl::Simulator supports them. + static constexpr bool kCanSimulate = (kRuntimeISA == kX86_64); + + DISALLOW_COPY_AND_ASSIGN(CodeSimulatorArm64); +}; + +} // namespace arm64 +} // namespace art + +#endif // ART_RUNTIME_SIMULATOR_CODE_SIMULATOR_ARM64_H_ diff --git a/test/444-checker-nce/src/Main.java b/test/444-checker-nce/src/Main.java index 865355ce97..32122e4dcd 100644 --- a/test/444-checker-nce/src/Main.java +++ b/test/444-checker-nce/src/Main.java @@ -16,11 +16,11 @@ public class Main { - /// CHECK-START: Main Main.keepTest(Main) instruction_simplifier (before) + /// CHECK-START: Main Main.keepTest(Main) instruction_simplifier_after_types (before) /// CHECK: NullCheck /// CHECK: InvokeStaticOrDirect - /// CHECK-START: Main Main.keepTest(Main) instruction_simplifier (after) + /// CHECK-START: Main Main.keepTest(Main) instruction_simplifier_after_types (after) /// CHECK: NullCheck /// CHECK: InvokeStaticOrDirect public Main keepTest(Main m) { @@ -31,7 +31,7 @@ public class Main { /// CHECK: NullCheck /// CHECK: InvokeStaticOrDirect - /// CHECK-START: Main Main.thisTest() instruction_simplifier (after) + /// CHECK-START: Main Main.thisTest() instruction_simplifier_after_types (after) /// CHECK-NOT: NullCheck /// CHECK: InvokeStaticOrDirect public Main thisTest() { @@ -45,7 +45,7 @@ public class Main { /// CHECK: NullCheck /// CHECK: InvokeStaticOrDirect - /// CHECK-START: Main Main.newInstanceRemoveTest() instruction_simplifier (after) + /// CHECK-START: Main Main.newInstanceRemoveTest() instruction_simplifier_after_types (after) /// CHECK-NOT: NullCheck public Main newInstanceRemoveTest() { Main m = new Main(); @@ -57,7 +57,7 @@ public class Main { /// CHECK: NullCheck /// CHECK: ArrayGet - /// CHECK-START: Main Main.newArrayRemoveTest() instruction_simplifier (after) + /// CHECK-START: Main Main.newArrayRemoveTest() instruction_simplifier_after_types (after) /// CHECK: NewArray /// CHECK-NOT: NullCheck /// CHECK: ArrayGet @@ -66,11 +66,11 @@ public class Main { return ms[0]; } - /// CHECK-START: Main Main.ifRemoveTest(boolean) instruction_simplifier (before) + /// CHECK-START: Main Main.ifRemoveTest(boolean) instruction_simplifier_after_types (before) /// CHECK: NewInstance /// CHECK: NullCheck - /// CHECK-START: Main Main.ifRemoveTest(boolean) instruction_simplifier (after) + /// CHECK-START: Main Main.ifRemoveTest(boolean) instruction_simplifier_after_types (after) /// CHECK: NewInstance /// CHECK-NOT: NullCheck public Main ifRemoveTest(boolean flag) { @@ -83,11 +83,11 @@ public class Main { return m.g(); } - /// CHECK-START: Main Main.ifKeepTest(boolean) instruction_simplifier (before) + /// CHECK-START: Main Main.ifKeepTest(boolean) instruction_simplifier_after_types (before) /// CHECK: NewInstance /// CHECK: NullCheck - /// CHECK-START: Main Main.ifKeepTest(boolean) instruction_simplifier (after) + /// CHECK-START: Main Main.ifKeepTest(boolean) instruction_simplifier_after_types (after) /// CHECK: NewInstance /// CHECK: NullCheck public Main ifKeepTest(boolean flag) { @@ -98,10 +98,10 @@ public class Main { return m.g(); } - /// CHECK-START: Main Main.forRemoveTest(int) instruction_simplifier (before) + /// CHECK-START: Main Main.forRemoveTest(int) instruction_simplifier_after_types (before) /// CHECK: NullCheck - /// CHECK-START: Main Main.forRemoveTest(int) instruction_simplifier (after) + /// CHECK-START: Main Main.forRemoveTest(int) instruction_simplifier_after_types (after) /// CHECK-NOT: NullCheck public Main forRemoveTest(int count) { Main a = new Main(); @@ -114,10 +114,10 @@ public class Main { return m.g(); } - /// CHECK-START: Main Main.forKeepTest(int) instruction_simplifier (before) + /// CHECK-START: Main Main.forKeepTest(int) instruction_simplifier_after_types (before) /// CHECK: NullCheck - /// CHECK-START: Main Main.forKeepTest(int) instruction_simplifier (after) + /// CHECK-START: Main Main.forKeepTest(int) instruction_simplifier_after_types (after) /// CHECK: NullCheck public Main forKeepTest(int count) { Main a = new Main(); @@ -132,10 +132,10 @@ public class Main { return m.g(); } - /// CHECK-START: Main Main.phiFlowRemoveTest(int) instruction_simplifier (before) + /// CHECK-START: Main Main.phiFlowRemoveTest(int) instruction_simplifier_after_types (before) /// CHECK: NullCheck - /// CHECK-START: Main Main.phiFlowRemoveTest(int) instruction_simplifier (after) + /// CHECK-START: Main Main.phiFlowRemoveTest(int) instruction_simplifier_after_types (after) /// CHECK-NOT: NullCheck public Main phiFlowRemoveTest(int count) { Main a = new Main(); @@ -154,10 +154,10 @@ public class Main { return n.g(); } - /// CHECK-START: Main Main.phiFlowKeepTest(int) instruction_simplifier (before) + /// CHECK-START: Main Main.phiFlowKeepTest(int) instruction_simplifier_after_types (before) /// CHECK: NullCheck - /// CHECK-START: Main Main.phiFlowKeepTest(int) instruction_simplifier (after) + /// CHECK-START: Main Main.phiFlowKeepTest(int) instruction_simplifier_after_types (after) /// CHECK: NullCheck public Main phiFlowKeepTest(int count) { Main a = new Main(); @@ -181,7 +181,7 @@ public class Main { /// CHECK-START: Main Main.scopeRemoveTest(int, Main) ssa_builder (after) /// CHECK: NullCheck - /// CHECK-START: Main Main.scopeRemoveTest(int, Main) instruction_simplifier (after) + /// CHECK-START: Main Main.scopeRemoveTest(int, Main) instruction_simplifier_after_types (after) /// CHECK-NOT: NullCheck public Main scopeRemoveTest(int count, Main a) { Main m = null; @@ -196,10 +196,10 @@ public class Main { return m; } - /// CHECK-START: Main Main.scopeKeepTest(int, Main) instruction_simplifier (before) + /// CHECK-START: Main Main.scopeKeepTest(int, Main) instruction_simplifier_after_types (before) /// CHECK: NullCheck - /// CHECK-START: Main Main.scopeKeepTest(int, Main) instruction_simplifier (after) + /// CHECK-START: Main Main.scopeKeepTest(int, Main) instruction_simplifier_after_types (after) /// CHECK: NullCheck public Main scopeKeepTest(int count, Main a) { Main m = new Main(); @@ -214,10 +214,10 @@ public class Main { return m; } - /// CHECK-START: Main Main.scopeIfNotNullRemove(Main) instruction_simplifier (before) + /// CHECK-START: Main Main.scopeIfNotNullRemove(Main) instruction_simplifier_after_types (before) /// CHECK: NullCheck - /// CHECK-START: Main Main.scopeIfNotNullRemove(Main) instruction_simplifier (after) + /// CHECK-START: Main Main.scopeIfNotNullRemove(Main) instruction_simplifier_after_types (after) /// CHECK-NOT: NullCheck public Main scopeIfNotNullRemove(Main m) { if (m != null) { @@ -226,10 +226,10 @@ public class Main { return m; } - /// CHECK-START: Main Main.scopeIfKeep(Main) instruction_simplifier (before) + /// CHECK-START: Main Main.scopeIfKeep(Main) instruction_simplifier_after_types (before) /// CHECK: NullCheck - /// CHECK-START: Main Main.scopeIfKeep(Main) instruction_simplifier (after) + /// CHECK-START: Main Main.scopeIfKeep(Main) instruction_simplifier_after_types (after) /// CHECK: NullCheck public Main scopeIfKeep(Main m) { if (m == null) { @@ -258,11 +258,11 @@ public class Main { class ListElement { private ListElement next; - /// CHECK-START: boolean ListElement.isShorter(ListElement, ListElement) instruction_simplifier (before) + /// CHECK-START: boolean ListElement.isShorter(ListElement, ListElement) instruction_simplifier_after_types (before) /// CHECK: NullCheck /// CHECK: NullCheck - /// CHECK-START: boolean ListElement.isShorter(ListElement, ListElement) instruction_simplifier (after) + /// CHECK-START: boolean ListElement.isShorter(ListElement, ListElement) instruction_simplifier_after_types (after) /// CHECK-NOT: NullCheck static boolean isShorter(ListElement x, ListElement y) { ListElement xTail = x; diff --git a/test/450-checker-types/src/Main.java b/test/450-checker-types/src/Main.java index fd4dd5ecbf..f1f80caff0 100644 --- a/test/450-checker-types/src/Main.java +++ b/test/450-checker-types/src/Main.java @@ -72,49 +72,49 @@ final class FinalException extends Exception {} public class Main { - /// CHECK-START: void Main.testSimpleRemove() instruction_simplifier (before) + /// CHECK-START: void Main.testSimpleRemove() instruction_simplifier_after_types (before) /// CHECK: CheckCast - /// CHECK-START: void Main.testSimpleRemove() instruction_simplifier (after) + /// CHECK-START: void Main.testSimpleRemove() instruction_simplifier_after_types (after) /// CHECK-NOT: CheckCast public void testSimpleRemove() { Super s = new SubclassA(); ((SubclassA)s).$noinline$g(); } - /// CHECK-START: void Main.testSimpleKeep(Super) instruction_simplifier (before) + /// CHECK-START: void Main.testSimpleKeep(Super) instruction_simplifier_after_types (before) /// CHECK: CheckCast - /// CHECK-START: void Main.testSimpleKeep(Super) instruction_simplifier (after) + /// CHECK-START: void Main.testSimpleKeep(Super) instruction_simplifier_after_types (after) /// CHECK: CheckCast public void testSimpleKeep(Super s) { ((SubclassA)s).$noinline$f(); } - /// CHECK-START: java.lang.String Main.testClassRemove() instruction_simplifier (before) + /// CHECK-START: java.lang.String Main.testClassRemove() instruction_simplifier_after_types (before) /// CHECK: CheckCast - /// CHECK-START: java.lang.String Main.testClassRemove() instruction_simplifier (after) + /// CHECK-START: java.lang.String Main.testClassRemove() instruction_simplifier_after_types (after) /// CHECK-NOT: CheckCast public String testClassRemove() { Object s = SubclassA.class; return ((Class)s).getName(); } - /// CHECK-START: java.lang.String Main.testClassKeep() instruction_simplifier (before) + /// CHECK-START: java.lang.String Main.testClassKeep() instruction_simplifier_after_types (before) /// CHECK: CheckCast - /// CHECK-START: java.lang.String Main.testClassKeep() instruction_simplifier (after) + /// CHECK-START: java.lang.String Main.testClassKeep() instruction_simplifier_after_types (after) /// CHECK: CheckCast public String testClassKeep() { Object s = SubclassA.class; return ((SubclassA)s).$noinline$h(); } - /// CHECK-START: void Main.testIfRemove(int) instruction_simplifier (before) + /// CHECK-START: void Main.testIfRemove(int) instruction_simplifier_after_types (before) /// CHECK: CheckCast - /// CHECK-START: void Main.testIfRemove(int) instruction_simplifier (after) + /// CHECK-START: void Main.testIfRemove(int) instruction_simplifier_after_types (after) /// CHECK-NOT: CheckCast public void testIfRemove(int x) { Super s; @@ -126,10 +126,10 @@ public class Main { ((SubclassA)s).$noinline$g(); } - /// CHECK-START: void Main.testIfKeep(int) instruction_simplifier (before) + /// CHECK-START: void Main.testIfKeep(int) instruction_simplifier_after_types (before) /// CHECK: CheckCast - /// CHECK-START: void Main.testIfKeep(int) instruction_simplifier (after) + /// CHECK-START: void Main.testIfKeep(int) instruction_simplifier_after_types (after) /// CHECK: CheckCast public void testIfKeep(int x) { Super s; @@ -141,10 +141,10 @@ public class Main { ((SubclassA)s).$noinline$g(); } - /// CHECK-START: void Main.testForRemove(int) instruction_simplifier (before) + /// CHECK-START: void Main.testForRemove(int) instruction_simplifier_after_types (before) /// CHECK: CheckCast - /// CHECK-START: void Main.testForRemove(int) instruction_simplifier (after) + /// CHECK-START: void Main.testForRemove(int) instruction_simplifier_after_types (after) /// CHECK-NOT: CheckCast public void testForRemove(int x) { Super s = new SubclassA(); @@ -156,10 +156,10 @@ public class Main { ((SubclassA)s).$noinline$g(); } - /// CHECK-START: void Main.testForKeep(int) instruction_simplifier (before) + /// CHECK-START: void Main.testForKeep(int) instruction_simplifier_after_types (before) /// CHECK: CheckCast - /// CHECK-START: void Main.testForKeep(int) instruction_simplifier (after) + /// CHECK-START: void Main.testForKeep(int) instruction_simplifier_after_types (after) /// CHECK: CheckCast public void testForKeep(int x) { Super s = new SubclassA(); @@ -171,10 +171,10 @@ public class Main { ((SubclassC)s).$noinline$g(); } - /// CHECK-START: void Main.testPhiFromCall(int) instruction_simplifier (before) + /// CHECK-START: void Main.testPhiFromCall(int) instruction_simplifier_after_types (before) /// CHECK: CheckCast - /// CHECK-START: void Main.testPhiFromCall(int) instruction_simplifier (after) + /// CHECK-START: void Main.testPhiFromCall(int) instruction_simplifier_after_types (after) /// CHECK: CheckCast public void testPhiFromCall(int i) { Object x; @@ -186,12 +186,11 @@ public class Main { ((SubclassC)x).$noinline$g(); } - /// CHECK-START: void Main.testInstanceOf(java.lang.Object) instruction_simplifier (before) + /// CHECK-START: void Main.testInstanceOf(java.lang.Object) instruction_simplifier_after_types (before) /// CHECK: CheckCast /// CHECK: CheckCast - /// CHECK-NOT: CheckCast - /// CHECK-START: void Main.testInstanceOf(java.lang.Object) instruction_simplifier (after) + /// CHECK-START: void Main.testInstanceOf(java.lang.Object) instruction_simplifier_after_types (after) /// CHECK-NOT: CheckCast public void testInstanceOf(Object o) { if (o instanceof SubclassC) { @@ -202,101 +201,11 @@ public class Main { } } - public static boolean $inline$InstanceofSubclassB(Object o) { return o instanceof SubclassB; } - public static boolean $inline$InstanceofSubclassC(Object o) { return o instanceof SubclassC; } - - /// CHECK-START: void Main.testInstanceOf_NotInlined(java.lang.Object) ssa_builder (after) - /// CHECK-DAG: <<Cst0:i\d+>> IntConstant 0 - /// CHECK-DAG: <<Cst1:i\d+>> IntConstant 1 - /// CHECK-DAG: <<IOf1:z\d+>> InstanceOf - /// CHECK-DAG: NotEqual [<<IOf1>>,<<Cst1>>] - /// CHECK-DAG: <<IOf2:z\d+>> InstanceOf - /// CHECK-DAG: Equal [<<IOf2>>,<<Cst0>>] - - /// CHECK-START: void Main.testInstanceOf_NotInlined(java.lang.Object) instruction_simplifier (before) - /// CHECK: CheckCast - /// CHECK: CheckCast - /// CHECK-NOT: CheckCast - - /// CHECK-START: void Main.testInstanceOf_NotInlined(java.lang.Object) instruction_simplifier (after) - /// CHECK-NOT: CheckCast - public void testInstanceOf_NotInlined(Object o) { - if ((o instanceof SubclassC) == true) { - ((SubclassC)o).$noinline$g(); - } - if ((o instanceof SubclassB) != false) { - ((SubclassB)o).$noinline$g(); - } - } - - /// CHECK-START: void Main.testNotInstanceOf_NotInlined(java.lang.Object) ssa_builder (after) - /// CHECK-DAG: <<Cst0:i\d+>> IntConstant 0 - /// CHECK-DAG: <<Cst1:i\d+>> IntConstant 1 - /// CHECK-DAG: <<IOf1:z\d+>> InstanceOf - /// CHECK-DAG: Equal [<<IOf1>>,<<Cst1>>] - /// CHECK-DAG: <<IOf2:z\d+>> InstanceOf - /// CHECK-DAG: NotEqual [<<IOf2>>,<<Cst0>>] - - /// CHECK-START: void Main.testNotInstanceOf_NotInlined(java.lang.Object) instruction_simplifier (before) - /// CHECK: CheckCast - /// CHECK: CheckCast - /// CHECK-NOT: CheckCast - - /// CHECK-START: void Main.testNotInstanceOf_NotInlined(java.lang.Object) instruction_simplifier (after) - /// CHECK-NOT: CheckCast - public void testNotInstanceOf_NotInlined(Object o) { - if ((o instanceof SubclassC) != true) { - // Empty branch to flip the condition. - } else { - ((SubclassC)o).$noinline$g(); - } - if ((o instanceof SubclassB) == false) { - // Empty branch to flip the condition. - } else { - ((SubclassB)o).$noinline$g(); - } - } - - /// CHECK-START: void Main.testInstanceOf_Inlined(java.lang.Object) inliner (after) - /// CHECK-DAG: <<IOf:z\d+>> InstanceOf - /// CHECK-DAG: If [<<IOf>>] - - /// CHECK-START: void Main.testInstanceOf_Inlined(java.lang.Object) instruction_simplifier_after_bce (before) - /// CHECK: CheckCast - /// CHECK-NOT: CheckCast - - /// CHECK-START: void Main.testInstanceOf_Inlined(java.lang.Object) instruction_simplifier_after_bce (after) - /// CHECK-NOT: CheckCast - public void testInstanceOf_Inlined(Object o) { - if (!$inline$InstanceofSubclassC(o)) { - // Empty branch to flip the condition. - } else { - ((SubclassC)o).$noinline$g(); - } - } - - /// CHECK-START: void Main.testNotInstanceOf_Inlined(java.lang.Object) inliner (after) - /// CHECK-DAG: <<IOf:z\d+>> InstanceOf - /// CHECK-DAG: <<Not:z\d+>> BooleanNot [<<IOf>>] - /// CHECK-DAG: If [<<Not>>] - - /// CHECK-START: void Main.testNotInstanceOf_Inlined(java.lang.Object) instruction_simplifier_after_bce (before) - /// CHECK: CheckCast - /// CHECK-NOT: CheckCast - - /// CHECK-START: void Main.testNotInstanceOf_Inlined(java.lang.Object) instruction_simplifier_after_bce (after) - /// CHECK-NOT: CheckCast - public void testNotInstanceOf_Inlined(Object o) { - if ($inline$InstanceofSubclassC(o)) { - ((SubclassC)o).$noinline$g(); - } - } - - /// CHECK-START: void Main.testInstanceOfKeep(java.lang.Object) instruction_simplifier (before) + /// CHECK-START: void Main.testInstanceOfKeep(java.lang.Object) instruction_simplifier_after_types (before) /// CHECK: CheckCast /// CHECK: CheckCast - /// CHECK-START: void Main.testInstanceOfKeep(java.lang.Object) instruction_simplifier (after) + /// CHECK-START: void Main.testInstanceOfKeep(java.lang.Object) instruction_simplifier_after_types (after) /// CHECK: CheckCast /// CHECK: CheckCast public void testInstanceOfKeep(Object o) { @@ -308,11 +217,11 @@ public class Main { } } - /// CHECK-START: void Main.testInstanceOfNested(java.lang.Object) instruction_simplifier (before) + /// CHECK-START: void Main.testInstanceOfNested(java.lang.Object) instruction_simplifier_after_types (before) /// CHECK: CheckCast /// CHECK: CheckCast - /// CHECK-START: void Main.testInstanceOfNested(java.lang.Object) instruction_simplifier (after) + /// CHECK-START: void Main.testInstanceOfNested(java.lang.Object) instruction_simplifier_after_types (after) /// CHECK-NOT: CheckCast public void testInstanceOfNested(Object o) { if (o instanceof SubclassC) { @@ -324,10 +233,10 @@ public class Main { } } - /// CHECK-START: void Main.testInstanceOfWithPhi(int) instruction_simplifier (before) + /// CHECK-START: void Main.testInstanceOfWithPhi(int) instruction_simplifier_after_types (before) /// CHECK: CheckCast - /// CHECK-START: void Main.testInstanceOfWithPhi(int) instruction_simplifier (after) + /// CHECK-START: void Main.testInstanceOfWithPhi(int) instruction_simplifier_after_types (after) /// CHECK-NOT: CheckCast public void testInstanceOfWithPhi(int i) { Object o; @@ -342,10 +251,10 @@ public class Main { } } - /// CHECK-START: void Main.testInstanceOfInFor(int) instruction_simplifier (before) + /// CHECK-START: void Main.testInstanceOfInFor(int) instruction_simplifier_after_types (before) /// CHECK: CheckCast - /// CHECK-START: void Main.testInstanceOfInFor(int) instruction_simplifier (after) + /// CHECK-START: void Main.testInstanceOfInFor(int) instruction_simplifier_after_types (after) /// CHECK-NOT: CheckCast public void testInstanceOfInFor(int n) { Object o = new SubclassA(); @@ -359,10 +268,10 @@ public class Main { } } - /// CHECK-START: void Main.testInstanceOfSubclass() instruction_simplifier (before) + /// CHECK-START: void Main.testInstanceOfSubclass() instruction_simplifier_after_types (before) /// CHECK: CheckCast - /// CHECK-START: void Main.testInstanceOfSubclass() instruction_simplifier (after) + /// CHECK-START: void Main.testInstanceOfSubclass() instruction_simplifier_after_types (after) /// CHECK-NOT: CheckCast public void testInstanceOfSubclass() { Object o = new SubclassA(); @@ -371,10 +280,10 @@ public class Main { } } - /// CHECK-START: void Main.testInstanceOfWithPhiSubclass(int) instruction_simplifier (before) + /// CHECK-START: void Main.testInstanceOfWithPhiSubclass(int) instruction_simplifier_after_types (before) /// CHECK: CheckCast - /// CHECK-START: void Main.testInstanceOfWithPhiSubclass(int) instruction_simplifier (after) + /// CHECK-START: void Main.testInstanceOfWithPhiSubclass(int) instruction_simplifier_after_types (after) /// CHECK-NOT: CheckCast public void testInstanceOfWithPhiSubclass(int i) { Object o; @@ -389,10 +298,10 @@ public class Main { } } - /// CHECK-START: void Main.testInstanceOfWithPhiTop(int) instruction_simplifier (before) + /// CHECK-START: void Main.testInstanceOfWithPhiTop(int) instruction_simplifier_after_types (before) /// CHECK: CheckCast - /// CHECK-START: void Main.testInstanceOfWithPhiTop(int) instruction_simplifier (after) + /// CHECK-START: void Main.testInstanceOfWithPhiTop(int) instruction_simplifier_after_types (after) /// CHECK-NOT: CheckCast public void testInstanceOfWithPhiTop(int i) { Object o; @@ -407,10 +316,10 @@ public class Main { } } - /// CHECK-START: void Main.testInstanceOfSubclassInFor(int) instruction_simplifier (before) + /// CHECK-START: void Main.testInstanceOfSubclassInFor(int) instruction_simplifier_after_types (before) /// CHECK: CheckCast - /// CHECK-START: void Main.testInstanceOfSubclassInFor(int) instruction_simplifier (after) + /// CHECK-START: void Main.testInstanceOfSubclassInFor(int) instruction_simplifier_after_types (after) /// CHECK-NOT: CheckCast public void testInstanceOfSubclassInFor(int n) { Object o = new SubclassA(); @@ -424,10 +333,10 @@ public class Main { } } - /// CHECK-START: void Main.testInstanceOfTopInFor(int) instruction_simplifier (before) + /// CHECK-START: void Main.testInstanceOfTopInFor(int) instruction_simplifier_after_types (before) /// CHECK: CheckCast - /// CHECK-START: void Main.testInstanceOfTopInFor(int) instruction_simplifier (after) + /// CHECK-START: void Main.testInstanceOfTopInFor(int) instruction_simplifier_after_types (after) /// CHECK-NOT: CheckCast public void testInstanceOfTopInFor(int n) { Object o = new SubclassA(); @@ -452,10 +361,10 @@ public class Main { public SubclassA a = new SubclassA(); public static SubclassA b = new SubclassA(); - /// CHECK-START: void Main.testInstanceFieldGetSimpleRemove() instruction_simplifier (before) + /// CHECK-START: void Main.testInstanceFieldGetSimpleRemove() instruction_simplifier_after_types (before) /// CHECK: CheckCast - /// CHECK-START: void Main.testInstanceFieldGetSimpleRemove() instruction_simplifier (after) + /// CHECK-START: void Main.testInstanceFieldGetSimpleRemove() instruction_simplifier_after_types (after) /// CHECK-NOT: CheckCast public void testInstanceFieldGetSimpleRemove() { Main m = new Main(); @@ -463,10 +372,10 @@ public class Main { ((SubclassA)a).$noinline$g(); } - /// CHECK-START: void Main.testStaticFieldGetSimpleRemove() instruction_simplifier (before) + /// CHECK-START: void Main.testStaticFieldGetSimpleRemove() instruction_simplifier_after_types (before) /// CHECK: CheckCast - /// CHECK-START: void Main.testStaticFieldGetSimpleRemove() instruction_simplifier (after) + /// CHECK-START: void Main.testStaticFieldGetSimpleRemove() instruction_simplifier_after_types (after) /// CHECK-NOT: CheckCast public void testStaticFieldGetSimpleRemove() { Super b = Main.b; @@ -475,36 +384,36 @@ public class Main { public SubclassA $noinline$getSubclass() { throw new RuntimeException(); } - /// CHECK-START: void Main.testArraySimpleRemove() instruction_simplifier (before) + /// CHECK-START: void Main.testArraySimpleRemove() instruction_simplifier_after_types (before) /// CHECK: CheckCast - /// CHECK-START: void Main.testArraySimpleRemove() instruction_simplifier (after) + /// CHECK-START: void Main.testArraySimpleRemove() instruction_simplifier_after_types (after) /// CHECK-NOT: CheckCast public void testArraySimpleRemove() { Super[] b = new SubclassA[10]; SubclassA[] c = (SubclassA[])b; } - /// CHECK-START: void Main.testInvokeSimpleRemove() instruction_simplifier (before) + /// CHECK-START: void Main.testInvokeSimpleRemove() instruction_simplifier_after_types (before) /// CHECK: CheckCast - /// CHECK-START: void Main.testInvokeSimpleRemove() instruction_simplifier (after) + /// CHECK-START: void Main.testInvokeSimpleRemove() instruction_simplifier_after_types (after) /// CHECK-NOT: CheckCast public void testInvokeSimpleRemove() { Super b = $noinline$getSubclass(); ((SubclassA)b).$noinline$g(); } - /// CHECK-START: void Main.testArrayGetSimpleRemove() instruction_simplifier (before) + /// CHECK-START: void Main.testArrayGetSimpleRemove() instruction_simplifier_after_types (before) /// CHECK: CheckCast - /// CHECK-START: void Main.testArrayGetSimpleRemove() instruction_simplifier (after) + /// CHECK-START: void Main.testArrayGetSimpleRemove() instruction_simplifier_after_types (after) /// CHECK-NOT: CheckCast public void testArrayGetSimpleRemove() { Super[] a = new SubclassA[10]; ((SubclassA)a[0]).$noinline$g(); } - /// CHECK-START: int Main.testLoadExceptionInCatchNonExact(int, int) ssa_builder (after) + /// CHECK-START: int Main.testLoadExceptionInCatchNonExact(int, int) reference_type_propagation (after) /// CHECK: LoadException klass:java.lang.ArithmeticException can_be_null:false exact:false public int testLoadExceptionInCatchNonExact(int x, int y) { try { @@ -514,7 +423,7 @@ public class Main { } } - /// CHECK-START: int Main.testLoadExceptionInCatchExact(int) ssa_builder (after) + /// CHECK-START: int Main.testLoadExceptionInCatchExact(int) reference_type_propagation (after) /// CHECK: LoadException klass:FinalException can_be_null:false exact:true public int testLoadExceptionInCatchExact(int x) { try { @@ -528,7 +437,7 @@ public class Main { } } - /// CHECK-START: int Main.testLoadExceptionInCatchAll(int, int) ssa_builder (after) + /// CHECK-START: int Main.testLoadExceptionInCatchAll(int, int) reference_type_propagation (after) /// CHECK: LoadException klass:java.lang.Throwable can_be_null:false exact:false public int testLoadExceptionInCatchAll(int x, int y) { try { @@ -549,7 +458,7 @@ public class Main { return genericFinal.get(); } - /// CHECK-START: SubclassC Main.inlineGenerics() ssa_builder (after) + /// CHECK-START: SubclassC Main.inlineGenerics() reference_type_propagation (after) /// CHECK: <<Invoke:l\d+>> InvokeStaticOrDirect klass:SubclassC exact:false /// CHECK-NEXT: Return [<<Invoke>>] @@ -561,7 +470,7 @@ public class Main { return c; } - /// CHECK-START: Final Main.inlineGenericsFinal() ssa_builder (after) + /// CHECK-START: Final Main.inlineGenericsFinal() reference_type_propagation (after) /// CHECK: <<Invoke:l\d+>> InvokeStaticOrDirect klass:Final exact:true /// CHECK-NEXT: Return [<<Invoke>>] @@ -603,7 +512,7 @@ public class Main { return new SubclassA(); } - /// CHECK-START: void Main.updateNodesInTheSameBlockAsPhi(boolean) ssa_builder (after) + /// CHECK-START: void Main.updateNodesInTheSameBlockAsPhi(boolean) reference_type_propagation (after) /// CHECK: <<Phi:l\d+>> Phi klass:Super /// CHECK: NullCheck [<<Phi>>] klass:Super @@ -625,7 +534,7 @@ public class Main { /// CHECK: CheckCast [<<Param>>,<<Clazz>>] /// CHECK: BoundType [<<Param>>] can_be_null:true - /// CHECK-START: java.lang.String Main.checkcastPreserveNullCheck(java.lang.Object) instruction_simplifier (after) + /// CHECK-START: java.lang.String Main.checkcastPreserveNullCheck(java.lang.Object) instruction_simplifier_after_types (after) /// CHECK: <<This:l\d+>> ParameterValue /// CHECK: <<Param:l\d+>> ParameterValue /// CHECK: <<Clazz:l\d+>> LoadClass @@ -637,7 +546,7 @@ public class Main { } - /// CHECK-START: void Main.argumentCheck(Super, double, SubclassA, Final) ssa_builder (after) + /// CHECK-START: void Main.argumentCheck(Super, double, SubclassA, Final) reference_type_propagation (after) /// CHECK: ParameterValue klass:Main can_be_null:false exact:false /// CHECK: ParameterValue klass:Super can_be_null:true exact:false /// CHECK: ParameterValue @@ -653,7 +562,7 @@ public class Main { private int mainField = 0; - /// CHECK-START: SuperInterface Main.getWiderType(boolean, Interface, OtherInterface) ssa_builder (after) + /// CHECK-START: SuperInterface Main.getWiderType(boolean, Interface, OtherInterface) reference_type_propagation (after) /// CHECK: <<Phi:l\d+>> Phi klass:java.lang.Object /// CHECK: Return [<<Phi>>] private SuperInterface getWiderType(boolean cond, Interface a, OtherInterface b) { @@ -709,7 +618,7 @@ public class Main { getSuper(); } - /// CHECK-START: void Main.testLoopPhiWithNullFirstInput(boolean) ssa_builder (after) + /// CHECK-START: void Main.testLoopPhiWithNullFirstInput(boolean) reference_type_propagation (after) /// CHECK-DAG: <<Null:l\d+>> NullConstant /// CHECK-DAG: <<Main:l\d+>> NewInstance klass:Main exact:true /// CHECK-DAG: <<LoopPhi:l\d+>> Phi [<<Null>>,<<LoopPhi>>,<<Main>>] klass:Main exact:true @@ -722,7 +631,7 @@ public class Main { } } - /// CHECK-START: void Main.testLoopPhisWithNullAndCrossUses(boolean) ssa_builder (after) + /// CHECK-START: void Main.testLoopPhisWithNullAndCrossUses(boolean) reference_type_propagation (after) /// CHECK-DAG: <<Null:l\d+>> NullConstant /// CHECK-DAG: <<PhiA:l\d+>> Phi [<<Null>>,<<PhiB:l\d+>>,<<PhiA>>] klass:java.lang.Object exact:false /// CHECK-DAG: <<PhiB>> Phi [<<Null>>,<<PhiB>>,<<PhiA>>] klass:java.lang.Object exact:false @@ -738,7 +647,7 @@ public class Main { } } - /// CHECK-START: java.lang.Object[] Main.testInstructionsWithUntypedParent() ssa_builder (after) + /// CHECK-START: java.lang.Object[] Main.testInstructionsWithUntypedParent() reference_type_propagation (after) /// CHECK-DAG: <<Null:l\d+>> NullConstant /// CHECK-DAG: <<LoopPhi:l\d+>> Phi [<<Null>>,<<Phi:l\d+>>] klass:java.lang.Object[] exact:true /// CHECK-DAG: <<Array:l\d+>> NewArray klass:java.lang.Object[] exact:true diff --git a/test/477-checker-bound-type/src/Main.java b/test/477-checker-bound-type/src/Main.java index 0f65e44678..c873702408 100644 --- a/test/477-checker-bound-type/src/Main.java +++ b/test/477-checker-bound-type/src/Main.java @@ -17,7 +17,7 @@ public class Main { - /// CHECK-START: java.lang.Object Main.boundTypeForIf(java.lang.Object) ssa_builder (after) + /// CHECK-START: java.lang.Object Main.boundTypeForIf(java.lang.Object) reference_type_propagation (after) /// CHECK: BoundType public static Object boundTypeForIf(Object a) { if (a != null) { @@ -27,7 +27,7 @@ public class Main { } } - /// CHECK-START: java.lang.Object Main.boundTypeForInstanceOf(java.lang.Object) ssa_builder (after) + /// CHECK-START: java.lang.Object Main.boundTypeForInstanceOf(java.lang.Object) reference_type_propagation (after) /// CHECK: BoundType public static Object boundTypeForInstanceOf(Object a) { if (a instanceof Main) { @@ -37,7 +37,7 @@ public class Main { } } - /// CHECK-START: java.lang.Object Main.noBoundTypeForIf(java.lang.Object) ssa_builder (after) + /// CHECK-START: java.lang.Object Main.noBoundTypeForIf(java.lang.Object) reference_type_propagation (after) /// CHECK-NOT: BoundType public static Object noBoundTypeForIf(Object a) { if (a == null) { @@ -47,7 +47,7 @@ public class Main { } } - /// CHECK-START: java.lang.Object Main.noBoundTypeForInstanceOf(java.lang.Object) ssa_builder (after) + /// CHECK-START: java.lang.Object Main.noBoundTypeForInstanceOf(java.lang.Object) reference_type_propagation (after) /// CHECK-NOT: BoundType public static Object noBoundTypeForInstanceOf(Object a) { if (a instanceof Main) { diff --git a/test/530-checker-lse/src/Main.java b/test/530-checker-lse/src/Main.java index ced3e50d41..98251e4af3 100644 --- a/test/530-checker-lse/src/Main.java +++ b/test/530-checker-lse/src/Main.java @@ -453,14 +453,16 @@ public class Main { } /// CHECK-START: float Main.test19(float[], float[]) load_store_elimination (before) - /// CHECK: {{f\d+}} ArrayGet - /// CHECK: {{f\d+}} ArrayGet + /// CHECK: <<IntTypeValue:i\d+>> ArrayGet + /// CHECK: ArraySet + /// CHECK: <<FloatTypeValue:f\d+>> ArrayGet /// CHECK-START: float Main.test19(float[], float[]) load_store_elimination (after) - /// CHECK: {{f\d+}} ArrayGet - /// CHECK-NOT: {{f\d+}} ArrayGet + /// CHECK: <<IntTypeValue:i\d+>> ArrayGet + /// CHECK: ArraySet + /// CHECK: <<FloatTypeValue:f\d+>> ArrayGet - // I/F, J/D aliasing should not happen any more and LSE should eliminate the load. + // I/F, J/D aliasing should keep the load/store. static float test19(float[] fa1, float[] fa2) { fa1[0] = fa2[0]; return fa1[0]; diff --git a/test/540-checker-rtp-bug/src/Main.java b/test/540-checker-rtp-bug/src/Main.java index 9a9f0b6048..e9f16c04d9 100644 --- a/test/540-checker-rtp-bug/src/Main.java +++ b/test/540-checker-rtp-bug/src/Main.java @@ -21,14 +21,14 @@ final class Final { } public class Main { - /// CHECK-START: Final Main.testKeepCheckCast(java.lang.Object, boolean) ssa_builder (after) + /// CHECK-START: Final Main.testKeepCheckCast(java.lang.Object, boolean) reference_type_propagation (after) /// CHECK: <<Phi:l\d+>> Phi klass:java.lang.Object /// CHECK: <<Class:l\d+>> LoadClass /// CHECK: CheckCast [<<Phi>>,<<Class>>] /// CHECK: <<Ret:l\d+>> BoundType [<<Phi>>] klass:Final /// CHECK: Return [<<Ret>>] - /// CHECK-START: Final Main.testKeepCheckCast(java.lang.Object, boolean) instruction_simplifier (after) + /// CHECK-START: Final Main.testKeepCheckCast(java.lang.Object, boolean) instruction_simplifier_after_types (after) /// CHECK: <<Phi:l\d+>> Phi /// CHECK: <<Class:l\d+>> LoadClass /// CHECK: CheckCast [<<Phi>>,<<Class>>] @@ -43,7 +43,7 @@ public class Main { return (Final) x; } - /// CHECK-START: void Main.testKeepInstanceOf(java.lang.Object, boolean) ssa_builder (after) + /// CHECK-START: void Main.testKeepInstanceOf(java.lang.Object, boolean) reference_type_propagation (after) /// CHECK: <<Phi:l\d+>> Phi klass:java.lang.Object /// CHECK: <<Class:l\d+>> LoadClass /// CHECK: InstanceOf [<<Phi>>,<<Class>>] @@ -65,7 +65,7 @@ public class Main { } } - /// CHECK-START: java.lang.String Main.testNoInline(java.lang.Object, boolean) ssa_builder (after) + /// CHECK-START: java.lang.String Main.testNoInline(java.lang.Object, boolean) reference_type_propagation (after) /// CHECK: <<Phi:l\d+>> Phi klass:java.lang.Object /// CHECK: <<NC:l\d+>> NullCheck [<<Phi>>] /// CHECK: <<Ret:l\d+>> InvokeVirtual [<<NC>>] method_name:java.lang.Object.toString diff --git a/test/549-checker-types-merge/src/Main.java b/test/549-checker-types-merge/src/Main.java index 917073b1c9..dc27f10427 100644 --- a/test/549-checker-types-merge/src/Main.java +++ b/test/549-checker-types-merge/src/Main.java @@ -38,14 +38,14 @@ class ClassImplementsInterfaceA extends ClassSuper implements InterfaceA {} public class Main { - /// CHECK-START: java.lang.Object Main.testMergeNullContant(boolean) ssa_builder (after) + /// CHECK-START: java.lang.Object Main.testMergeNullContant(boolean) reference_type_propagation (after) /// CHECK: <<Phi:l\d+>> Phi klass:Main /// CHECK: Return [<<Phi>>] private Object testMergeNullContant(boolean cond) { return cond ? null : new Main(); } - /// CHECK-START: java.lang.Object Main.testMergeClasses(boolean, ClassExtendsA, ClassExtendsB) ssa_builder (after) + /// CHECK-START: java.lang.Object Main.testMergeClasses(boolean, ClassExtendsA, ClassExtendsB) reference_type_propagation (after) /// CHECK: <<Phi:l\d+>> Phi klass:ClassSuper /// CHECK: Return [<<Phi>>] private Object testMergeClasses(boolean cond, ClassExtendsA a, ClassExtendsB b) { @@ -53,7 +53,7 @@ public class Main { return cond ? a : b; } - /// CHECK-START: java.lang.Object Main.testMergeClasses(boolean, ClassExtendsA, ClassSuper) ssa_builder (after) + /// CHECK-START: java.lang.Object Main.testMergeClasses(boolean, ClassExtendsA, ClassSuper) reference_type_propagation (after) /// CHECK: <<Phi:l\d+>> Phi klass:ClassSuper /// CHECK: Return [<<Phi>>] private Object testMergeClasses(boolean cond, ClassExtendsA a, ClassSuper b) { @@ -61,7 +61,7 @@ public class Main { return cond ? a : b; } - /// CHECK-START: java.lang.Object Main.testMergeClasses(boolean, ClassSuper, ClassSuper) ssa_builder (after) + /// CHECK-START: java.lang.Object Main.testMergeClasses(boolean, ClassSuper, ClassSuper) reference_type_propagation (after) /// CHECK: <<Phi:l\d+>> Phi klass:ClassSuper /// CHECK: Return [<<Phi>>] private Object testMergeClasses(boolean cond, ClassSuper a, ClassSuper b) { @@ -69,7 +69,7 @@ public class Main { return cond ? a : b; } - /// CHECK-START: java.lang.Object Main.testMergeClasses(boolean, ClassOtherSuper, ClassSuper) ssa_builder (after) + /// CHECK-START: java.lang.Object Main.testMergeClasses(boolean, ClassOtherSuper, ClassSuper) reference_type_propagation (after) /// CHECK: <<Phi:l\d+>> Phi klass:java.lang.Object /// CHECK: Return [<<Phi>>] private Object testMergeClasses(boolean cond, ClassOtherSuper a, ClassSuper b) { @@ -77,7 +77,7 @@ public class Main { return cond ? a : b; } - /// CHECK-START: java.lang.Object Main.testMergeClassWithInterface(boolean, ClassImplementsInterfaceA, InterfaceSuper) ssa_builder (after) + /// CHECK-START: java.lang.Object Main.testMergeClassWithInterface(boolean, ClassImplementsInterfaceA, InterfaceSuper) reference_type_propagation (after) /// CHECK: <<Phi:l\d+>> Phi klass:InterfaceSuper /// CHECK: Return [<<Phi>>] private Object testMergeClassWithInterface(boolean cond, ClassImplementsInterfaceA a, InterfaceSuper b) { @@ -85,7 +85,7 @@ public class Main { return cond ? a : b; } - /// CHECK-START: java.lang.Object Main.testMergeClassWithInterface(boolean, ClassSuper, InterfaceSuper) ssa_builder (after) + /// CHECK-START: java.lang.Object Main.testMergeClassWithInterface(boolean, ClassSuper, InterfaceSuper) reference_type_propagation (after) /// CHECK: <<Phi:l\d+>> Phi klass:java.lang.Object /// CHECK: Return [<<Phi>>] private Object testMergeClassWithInterface(boolean cond, ClassSuper a, InterfaceSuper b) { @@ -93,7 +93,7 @@ public class Main { return cond ? a : b; } - /// CHECK-START: java.lang.Object Main.testMergeInterfaces(boolean, InterfaceExtendsA, InterfaceSuper) ssa_builder (after) + /// CHECK-START: java.lang.Object Main.testMergeInterfaces(boolean, InterfaceExtendsA, InterfaceSuper) reference_type_propagation (after) /// CHECK: <<Phi:l\d+>> Phi klass:InterfaceSuper /// CHECK: Return [<<Phi>>] private Object testMergeInterfaces(boolean cond, InterfaceExtendsA a, InterfaceSuper b) { @@ -101,7 +101,7 @@ public class Main { return cond ? a : b; } - /// CHECK-START: java.lang.Object Main.testMergeInterfaces(boolean, InterfaceSuper, InterfaceSuper) ssa_builder (after) + /// CHECK-START: java.lang.Object Main.testMergeInterfaces(boolean, InterfaceSuper, InterfaceSuper) reference_type_propagation (after) /// CHECK: <<Phi:l\d+>> Phi klass:InterfaceSuper /// CHECK: Return [<<Phi>>] private Object testMergeInterfaces(boolean cond, InterfaceSuper a, InterfaceSuper b) { @@ -109,7 +109,7 @@ public class Main { return cond ? a : b; } - /// CHECK-START: java.lang.Object Main.testMergeInterfaces(boolean, InterfaceExtendsA, InterfaceExtendsB) ssa_builder (after) + /// CHECK-START: java.lang.Object Main.testMergeInterfaces(boolean, InterfaceExtendsA, InterfaceExtendsB) reference_type_propagation (after) /// CHECK: <<Phi:l\d+>> Phi klass:java.lang.Object /// CHECK: Return [<<Phi>>] private Object testMergeInterfaces(boolean cond, InterfaceExtendsA a, InterfaceExtendsB b) { @@ -117,7 +117,7 @@ public class Main { return cond ? a : b; } - /// CHECK-START: java.lang.Object Main.testMergeInterfaces(boolean, InterfaceSuper, InterfaceOtherSuper) ssa_builder (after) + /// CHECK-START: java.lang.Object Main.testMergeInterfaces(boolean, InterfaceSuper, InterfaceOtherSuper) reference_type_propagation (after) /// CHECK: <<Phi:l\d+>> Phi klass:java.lang.Object /// CHECK: Return [<<Phi>>] private Object testMergeInterfaces(boolean cond, InterfaceSuper a, InterfaceOtherSuper b) { diff --git a/test/552-checker-primitive-typeprop/info.txt b/test/552-checker-primitive-typeprop/info.txt deleted file mode 100644 index 9d69056915..0000000000 --- a/test/552-checker-primitive-typeprop/info.txt +++ /dev/null @@ -1,2 +0,0 @@ -Test that phis with environment uses which can be properly typed are kept -in --debuggable mode.
\ No newline at end of file diff --git a/test/552-checker-primitive-typeprop/smali/ArrayGet.smali b/test/552-checker-primitive-typeprop/smali/ArrayGet.smali deleted file mode 100644 index 042fa0c80c..0000000000 --- a/test/552-checker-primitive-typeprop/smali/ArrayGet.smali +++ /dev/null @@ -1,245 +0,0 @@ -# Copyright (C) 2015 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. - -.class public LArrayGet; -.super Ljava/lang/Object; - - -# Test phi with fixed-type ArrayGet as an input and a matching second input. -# The phi should be typed accordingly. - -## CHECK-START: void ArrayGet.matchingFixedType(float[], float) ssa_builder (after) -## CHECK-NOT: Phi - -## CHECK-START-DEBUGGABLE: void ArrayGet.matchingFixedType(float[], float) ssa_builder (after) -## CHECK-DAG: <<Arg1:f\d+>> ParameterValue -## CHECK-DAG: <<Aget:f\d+>> ArrayGet -## CHECK-DAG: {{f\d+}} Phi [<<Aget>>,<<Arg1>>] reg:0 -.method public static matchingFixedType([FF)V - .registers 8 - - const v0, 0x0 - const v1, 0x1 - - aget v0, p0, v0 # read value - add-float v2, v0, v1 # float use fixes type - - float-to-int v2, p1 - if-eqz v2, :after - move v0, p1 - :after - # v0 = Phi [ArrayGet, Arg1] => float - - invoke-static {}, Ljava/lang/System;->nanoTime()J # create an env use - return-void -.end method - - -# Test phi with fixed-type ArrayGet as an input and a conflicting second input. -# The phi should be eliminated due to the conflict. - -## CHECK-START: void ArrayGet.conflictingFixedType(float[], int) ssa_builder (after) -## CHECK-NOT: Phi - -## CHECK-START-DEBUGGABLE: void ArrayGet.conflictingFixedType(float[], int) ssa_builder (after) -## CHECK-NOT: Phi -.method public static conflictingFixedType([FI)V - .registers 8 - - const v0, 0x0 - const v1, 0x1 - - aget v0, p0, v0 # read value - add-float v2, v0, v1 # float use fixes type - - if-eqz p1, :after - move v0, p1 - :after - # v0 = Phi [ArrayGet, Arg1] => conflict - - invoke-static {}, Ljava/lang/System;->nanoTime()J # create an env use - return-void -.end method - - -# Same test as the one above, only this time tests that type of ArrayGet is not -# changed. - -## CHECK-START: void ArrayGet.conflictingFixedType2(int[], float) ssa_builder (after) -## CHECK-NOT: Phi - -## CHECK-START-DEBUGGABLE: void ArrayGet.conflictingFixedType2(int[], float) ssa_builder (after) -## CHECK-NOT: Phi - -## CHECK-START-DEBUGGABLE: void ArrayGet.conflictingFixedType2(int[], float) ssa_builder (after) -## CHECK: {{i\d+}} ArrayGet -.method public static conflictingFixedType2([IF)V - .registers 8 - - const v0, 0x0 - const v1, 0x1 - - aget v0, p0, v0 # read value - add-int v2, v0, v1 # int use fixes type - - float-to-int v2, p1 - if-eqz v2, :after - move v0, p1 - :after - # v0 = Phi [ArrayGet, Arg1] => conflict - - invoke-static {}, Ljava/lang/System;->nanoTime()J # create an env use - return-void -.end method - - -# Test phi with free-type ArrayGet as an input and a matching second input. -# The phi should be typed accordingly. - -## CHECK-START: void ArrayGet.matchingFreeType(float[], float) ssa_builder (after) -## CHECK-NOT: Phi - -## CHECK-START-DEBUGGABLE: void ArrayGet.matchingFreeType(float[], float) ssa_builder (after) -## CHECK-DAG: <<Arg1:f\d+>> ParameterValue -## CHECK-DAG: <<Aget:f\d+>> ArrayGet -## CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<<Aget>>] -## CHECK-DAG: {{f\d+}} Phi [<<Aget>>,<<Arg1>>] reg:0 -.method public static matchingFreeType([FF)V - .registers 8 - - const v0, 0x0 - const v1, 0x1 - - aget v0, p0, v0 # read value, should be float but has no typed use - aput v0, p0, v1 # aput does not disambiguate the type - - float-to-int v2, p1 - if-eqz v2, :after - move v0, p1 - :after - # v0 = Phi [ArrayGet, Arg1] => float - - invoke-static {}, Ljava/lang/System;->nanoTime()J # create an env use - return-void -.end method - - -# Test phi with free-type ArrayGet as an input and a conflicting second input. -# The phi will be kept and typed according to the second input despite the -# conflict. - -## CHECK-START: void ArrayGet.conflictingFreeType(int[], float) ssa_builder (after) -## CHECK-NOT: Phi - -## CHECK-START-DEBUGGABLE: void ArrayGet.conflictingFreeType(int[], float) ssa_builder (after) -## CHECK-NOT: Phi - -.method public static conflictingFreeType([IF)V - .registers 8 - - const v0, 0x0 - const v1, 0x1 - - aget v0, p0, v0 # read value, should be int but has no typed use - aput v0, p0, v1 - - float-to-int v2, p1 - if-eqz v2, :after - move v0, p1 - :after - # v0 = Phi [ArrayGet, Arg1] => float - - invoke-static {}, Ljava/lang/System;->nanoTime()J # create an env use - return-void -.end method - - -# Test that real use of ArrayGet is propagated through phis. The following test -# case uses ArrayGet indirectly through two phis. It also creates an unused -# conflicting phi which should not be preserved. - -## CHECK-START: void ArrayGet.conflictingPhiUses(int[], float, boolean, boolean, boolean) ssa_builder (after) -## CHECK: InvokeStaticOrDirect env:[[{{i\d+}},{{i\d+}},_,{{i\d+}},{{.*}} - -.method public static conflictingPhiUses([IFZZZ)V - .registers 10 - - const v0, 0x0 - - # Create v1 = Phi [0x0, int ArrayGet] - move v1, v0 - if-eqz p2, :else1 - aget v1, p0, v0 - :else1 - - # Create v2 = Phi [v1, float] - move v2, v1 - if-eqz p3, :else2 - move v2, p1 - :else2 - - # Create v3 = Phi [v1, int] - move v3, v1 - if-eqz p4, :else3 - move v3, v0 - :else3 - - # Use v3 as int. - add-int/lit8 v4, v3, 0x2a - - # Create env uses. - invoke-static {}, Ljava/lang/System;->nanoTime()J - - return-void -.end method - -# Test that the right ArrayGet equivalent is always selected. The following test -# case uses ArrayGet as float through one phi and as an indeterminate type through -# another. The situation needs to be resolved so that only one instruction -# remains. - -## CHECK-START: void ArrayGet.typedVsUntypedPhiUse(float[], float, boolean, boolean) ssa_builder (after) -## CHECK: {{f\d+}} ArrayGet - -## CHECK-START: void ArrayGet.typedVsUntypedPhiUse(float[], float, boolean, boolean) ssa_builder (after) -## CHECK-NOT: {{i\d+}} ArrayGet - -.method public static typedVsUntypedPhiUse([FFZZ)V - .registers 10 - - const v0, 0x0 - - # v1 = float ArrayGet - aget v1, p0, v0 - - # Create v2 = Phi [v1, 0.0f] - move v2, v1 - if-eqz p2, :else1 - move v2, v0 - :else1 - - # Use v2 as float - cmpl-float v2, v2, p1 - - # Create v3 = Phi [v1, 0.0f] - move v3, v1 - if-eqz p3, :else2 - move v3, v0 - :else2 - - # Use v3 without a determinate type. - aput v3, p0, v0 - - return-void -.end method diff --git a/test/552-checker-primitive-typeprop/smali/SsaBuilder.smali b/test/552-checker-primitive-typeprop/smali/SsaBuilder.smali deleted file mode 100644 index 395feaaf61..0000000000 --- a/test/552-checker-primitive-typeprop/smali/SsaBuilder.smali +++ /dev/null @@ -1,52 +0,0 @@ -# Copyright (C) 2015 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. - -.class public LSsaBuilder; -.super Ljava/lang/Object; - -# Check that a dead phi with a live equivalent is replaced in an environment. The -# following test case throws an exception and uses v0 afterwards. However, v0 -# contains a phi that is interpreted as int for the environment, and as float for -# instruction use. SsaBuilder must substitute the int variant before removing it, -# otherwise running the code with an array short enough to throw will crash at -# runtime because v0 is undefined. - -## CHECK-START: int SsaBuilder.environmentPhi(boolean, int[]) ssa_builder (after) -## CHECK-DAG: <<Cst0:f\d+>> FloatConstant 0 -## CHECK-DAG: <<Cst2:f\d+>> FloatConstant 2 -## CHECK-DAG: <<Phi:f\d+>> Phi [<<Cst0>>,<<Cst2>>] -## CHECK-DAG: BoundsCheck env:[[<<Phi>>,{{i\d+}},{{z\d+}},{{l\d+}}]] - -.method public static environmentPhi(Z[I)I - .registers 4 - - const v0, 0x0 - if-eqz p0, :else - const v0, 0x40000000 - :else - # v0 = phi that can be both int and float - - :try_start - const v1, 0x3 - aput v1, p1, v1 - const v0, 0x1 # generate catch phi for v0 - const v1, 0x4 - aput v1, p1, v1 - :try_end - .catchall {:try_start .. :try_end} :use_as_float - - :use_as_float - float-to-int v0, v0 - return v0 -.end method
\ No newline at end of file diff --git a/test/552-checker-primitive-typeprop/smali/TypePropagation.smali b/test/552-checker-primitive-typeprop/smali/TypePropagation.smali deleted file mode 100644 index 58682a1923..0000000000 --- a/test/552-checker-primitive-typeprop/smali/TypePropagation.smali +++ /dev/null @@ -1,136 +0,0 @@ -# Copyright (C) 2015 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. - -.class public LTypePropagation; -.super Ljava/lang/Object; - -## CHECK-START-DEBUGGABLE: void TypePropagation.mergeDeadPhi(boolean, boolean, int, float, float) ssa_builder (after) -## CHECK-NOT: Phi -.method public static mergeDeadPhi(ZZIFF)V - .registers 8 - - if-eqz p0, :after1 - move p2, p3 - :after1 - # p2 = merge(int,float) = conflict - - if-eqz p1, :after2 - move p2, p4 - :after2 - # p2 = merge(conflict,float) = conflict - - invoke-static {}, Ljava/lang/System;->nanoTime()J # create an env use - return-void -.end method - -## CHECK-START-DEBUGGABLE: void TypePropagation.mergeSameType(boolean, int, int) ssa_builder (after) -## CHECK: {{i\d+}} Phi -## CHECK-NOT: Phi -.method public static mergeSameType(ZII)V - .registers 8 - if-eqz p0, :after - move p1, p2 - :after - # p1 = merge(int,int) = int - invoke-static {}, Ljava/lang/System;->nanoTime()J # create an env use - return-void -.end method - -## CHECK-START-DEBUGGABLE: void TypePropagation.mergeVoidInput(boolean, boolean, int, int) ssa_builder (after) -## CHECK: {{i\d+}} Phi -## CHECK: {{i\d+}} Phi -## CHECK-NOT: Phi -.method public static mergeVoidInput(ZZII)V - .registers 8 - :loop - # p2 = void (loop phi) => p2 = merge(int,int) = int - if-eqz p0, :after - move p2, p3 - :after - # p2 = merge(void,int) = int - if-eqz p1, :loop - invoke-static {}, Ljava/lang/System;->nanoTime()J # create an env use - return-void -.end method - -## CHECK-START-DEBUGGABLE: void TypePropagation.mergeDifferentSize(boolean, int, long) ssa_builder (after) -## CHECK-NOT: Phi -.method public static mergeDifferentSize(ZIJ)V - .registers 8 - if-eqz p0, :after - move-wide p1, p2 - :after - # p1 = merge(int,long) = conflict - invoke-static {}, Ljava/lang/System;->nanoTime()J # create an env use - return-void -.end method - -## CHECK-START-DEBUGGABLE: void TypePropagation.mergeRefFloat(boolean, float, java.lang.Object) ssa_builder (after) -## CHECK-NOT: Phi -.method public static mergeRefFloat(ZFLjava/lang/Object;)V - .registers 8 - if-eqz p0, :after - move-object p1, p2 - :after - # p1 = merge(float,reference) = conflict - invoke-static {}, Ljava/lang/System;->nanoTime()J # create an env use - return-void -.end method - -## CHECK-START-DEBUGGABLE: void TypePropagation.mergeIntFloat_Success(boolean, float) ssa_builder (after) -## CHECK: {{f\d+}} Phi -## CHECK-NOT: Phi -.method public static mergeIntFloat_Success(ZF)V - .registers 8 - if-eqz p0, :after - const/4 p1, 0x0 - :after - # p1 = merge(float,0x0) = float - invoke-static {}, Ljava/lang/System;->nanoTime()J # create an env use - return-void -.end method - -## CHECK-START-DEBUGGABLE: void TypePropagation.mergeIntFloat_Fail(boolean, int, float) ssa_builder (after) -## CHECK-NOT: Phi -.method public static mergeIntFloat_Fail(ZIF)V - .registers 8 - if-eqz p0, :after - move p1, p2 - :after - # p1 = merge(int,float) = conflict - invoke-static {}, Ljava/lang/System;->nanoTime()J # create an env use - return-void -.end method - -## CHECK-START-DEBUGGABLE: void TypePropagation.updateAllUsersOnConflict(boolean, boolean, int, float, int) ssa_builder (after) -## CHECK-NOT: Phi -.method public static updateAllUsersOnConflict(ZZIFI)V - .registers 8 - - :loop1 - # loop phis for all args - # p2 = merge(int,float) = float? => conflict - move p2, p3 - if-eqz p0, :loop1 - - :loop2 - # loop phis for all args - # requests float equivalent of p4 phi in loop1 => conflict - # propagates conflict to loop2's phis - move p2, p4 - if-eqz p1, :loop2 - - invoke-static {}, Ljava/lang/System;->nanoTime()J # create an env use - return-void -.end method diff --git a/test/552-checker-primitive-typeprop/src/Main.java b/test/552-checker-primitive-typeprop/src/Main.java deleted file mode 100644 index fe2343e48a..0000000000 --- a/test/552-checker-primitive-typeprop/src/Main.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2015 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. - */ - -import java.lang.reflect.Method; - -public class Main { - - // Workaround for b/18051191. - class InnerClass {} - - private static void assertEquals(int expected, int actual) { - if (expected != actual) { - throw new Error("Wrong result, expected=" + expected + ", actual=" + actual); - } - } - - public static void main(String[] args) throws Exception { - Class<?> c = Class.forName("SsaBuilder"); - Method m = c.getMethod("environmentPhi", new Class[] { boolean.class, int[].class }); - - int[] array = new int[3]; - int result; - - result = (Integer) m.invoke(null, new Object[] { true, array } ); - assertEquals(2, result); - - result = (Integer) m.invoke(null, new Object[] { false, array } ); - assertEquals(0, result); - } -} diff --git a/test/552-checker-primitive-typeprop/expected.txt b/test/558-switch/expected.txt index e69de29bb2..e69de29bb2 100644 --- a/test/552-checker-primitive-typeprop/expected.txt +++ b/test/558-switch/expected.txt diff --git a/test/558-switch/info.txt b/test/558-switch/info.txt new file mode 100644 index 0000000000..07283ffcd9 --- /dev/null +++ b/test/558-switch/info.txt @@ -0,0 +1,2 @@ +Regression test for optimizing that used to generate invalid +code for arm. diff --git a/test/558-switch/src/Main.java b/test/558-switch/src/Main.java new file mode 100644 index 0000000000..f44231e436 --- /dev/null +++ b/test/558-switch/src/Main.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2015 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 Main { + public static boolean testMethod(int statusCode) { + switch (statusCode) { + case 303: + case 301: + case 302: + case 307: + return true; + default: + return false; + } //end of switch + } + + public static void main(String[] args) { + if (!testMethod(301)) { + throw new Error("Unexpected result"); + } + } +} diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index 54ceb753fe..7589f8f7cc 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -511,43 +511,54 @@ endif TEST_ART_BROKEN_OPTIMIZING_DEBUGGABLE_RUN_TESTS := -# Tests that should fail in the read barrier configuration. -# 055: Exceeds run time limits due to read barrier instrumentation. -# 137: Read barrier forces interpreter. Cannot run this with the interpreter. -# 484: Baker's fast path based read barrier compiler instrumentation generates code containing -# more parallel moves (at least on x86), thus some Checker assertions may fail. -# 537: Expects an array copy to be intrinsified, but calling-on-slowpath intrinsics are not yet -# handled in the read barrier configuration. -# 554: Cannot run in interpreter mode and this rule covers both: the compiler and the interpreter. -TEST_ART_BROKEN_READ_BARRIER_RUN_TESTS := \ - 055-enum-performance \ - 137-cfi \ - 484-checker-register-hints \ - 537-checker-arraycopy \ + +# Tests that should fail in the read barrier configuration with the default (Quick) compiler. +# 137: Quick has no support for read barriers and punts to the +# interpreter, but CFI unwinding expects managed frames. +# 554: Quick does not support JIT profiling. +TEST_ART_BROKEN_DEFAULT_READ_BARRIER_RUN_TESTS := \ + 137-cfi \ 554-jit-profile-file +# Tests that should fail in the read barrier configuration with the Optimizing compiler. +# 484: Baker's fast path based read barrier compiler instrumentation generates code containing +# more parallel moves on x86, thus some Checker assertions may fail. +# 537: Expects an array copy to be intrinsified on x86-64, but calling-on-slowpath intrinsics are +# not yet handled in the read barrier configuration. +TEST_ART_BROKEN_OPTIMIZING_READ_BARRIER_RUN_TESTS := \ + 484-checker-register-hints \ + 537-checker-arraycopy + ifeq ($(ART_USE_READ_BARRIER),true) - ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES), \ - $(PREBUILD_TYPES),$(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES), \ - $(JNI_TYPES),$(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES), \ - $(TEST_ART_BROKEN_READ_BARRIER_RUN_TESTS),$(ALL_ADDRESS_SIZES)) + ifneq (,$(filter default,$(COMPILER_TYPES))) + ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES), \ + $(PREBUILD_TYPES),default,$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES), \ + $(JNI_TYPES),$(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES), \ + $(TEST_ART_BROKEN_DEFAULT_READ_BARRIER_RUN_TESTS),$(ALL_ADDRESS_SIZES)) + endif + + ifneq (,$(filter optimizing,$(COMPILER_TYPES))) + ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES), \ + $(PREBUILD_TYPES),optimizing,$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES), \ + $(JNI_TYPES),$(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES), \ + $(TEST_ART_BROKEN_OPTIMIZING_READ_BARRIER_RUN_TESTS),$(ALL_ADDRESS_SIZES)) + endif endif -TEST_ART_BROKEN_READ_BARRIER_RUN_TESTS := +TEST_ART_BROKEN_DEFAULT_READ_BARRIER_RUN_TESTS := +TEST_ART_BROKEN_OPTIMIZING_READ_BARRIER_RUN_TESTS := # Tests that should fail in the heap poisoning configuration with the default (Quick) compiler. -# 137: Quick punts to the interpreter, and this test cannot run this with the interpreter. +# 137: Quick has no support for read barriers and punts to the +# interpreter, but CFI unwinding expects managed frames. +# 554: Quick does not support JIT profiling. TEST_ART_BROKEN_DEFAULT_HEAP_POISONING_RUN_TESTS := \ - 137-cfi + 137-cfi \ + 554-jit-profile-file # Tests that should fail in the heap poisoning configuration with the Optimizing compiler. -# 055-enum-performance: Exceeds run time limits due to heap poisoning instrumentation. +# 055: Exceeds run time limits due to heap poisoning instrumentation (on ARM and ARM64 devices). TEST_ART_BROKEN_OPTIMIZING_HEAP_POISONING_RUN_TESTS := \ 055-enum-performance -# Tests that should fail in the heap poisoning configuration with the interpreter. -# 137: Cannot run this with the interpreter. -TEST_ART_BROKEN_INTERPRETER_HEAP_POISONING_RUN_TESTS := \ - 137-cfi \ - 554-jit-profile-file ifeq ($(ART_HEAP_POISONING),true) ifneq (,$(filter default,$(COMPILER_TYPES))) @@ -563,18 +574,10 @@ ifeq ($(ART_HEAP_POISONING),true) $(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES), \ $(TEST_ART_BROKEN_OPTIMIZING_HEAP_POISONING_RUN_TESTS),$(ALL_ADDRESS_SIZES)) endif - - ifneq (,$(filter interpreter,$(COMPILER_TYPES))) - ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES), \ - $(PREBUILD_TYPES),interpreter,$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \ - $(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES), \ - $(TEST_ART_BROKEN_INTERPRETER_HEAP_POISONING_RUN_TESTS),$(ALL_ADDRESS_SIZES)) - endif endif -TEST_ART_BROKEN_INTERPRETER_HEAP_POISONING_RUN_TESTS := -TEST_ART_BROKEN_OPTIMIZING_HEAP_POISONING_RUN_TESTS := TEST_ART_BROKEN_DEFAULT_HEAP_POISONING_RUN_TESTS := +TEST_ART_BROKEN_OPTIMIZING_HEAP_POISONING_RUN_TESTS := # Clear variables ahead of appending to them when defining tests. $(foreach target, $(TARGET_TYPES), $(eval ART_RUN_TEST_$(call name-to-var,$(target))_RULES :=)) diff --git a/test/run-test b/test/run-test index 6e13b8a976..60e008c8bb 100755 --- a/test/run-test +++ b/test/run-test @@ -41,7 +41,7 @@ else fi checker="${progdir}/../tools/checker/checker.py" export JAVA="java" -export JAVAC="javac -g" +export JAVAC="javac -g -source 1.7 -target 1.7 -Xlint:-options" export RUN="${progdir}/etc/run-test-jar" export DEX_LOCATION=/data/run-test/${test_dir} export NEED_DEX="true" diff --git a/tools/libcore_failures.txt b/tools/libcore_failures.txt index a5476f7c41..a2d2f239d6 100644 --- a/tools/libcore_failures.txt +++ b/tools/libcore_failures.txt @@ -170,5 +170,12 @@ result: EXEC_FAILED, names: ["org.apache.harmony.tests.java.util.WeakHashMapTest#test_keySet"], bug: 25437292 +}, +{ + description: "JSR166TestCase.waitForThreadToEnterWaitState seems to time out; needs investigation.", + result: EXEC_FAILED, + names: ["jsr166.LinkedTransferQueueTest#testTransfer2", + "jsr166.LinkedTransferQueueTest#testWaitingConsumer"], + bug: 25883050 } ] |