diff options
Diffstat (limited to 'compiler')
47 files changed, 523 insertions, 1388 deletions
diff --git a/compiler/Android.bp b/compiler/Android.bp index 2e60e7d658..453965947d 100644 --- a/compiler/Android.bp +++ b/compiler/Android.bp @@ -184,6 +184,7 @@ art_cc_defaults { }, generated_sources: ["art_compiler_operator_srcs"], shared_libs: [ + "libdexfile", "libbase", "libcutils", // for atrace. "liblzma", diff --git a/compiler/dex/dex_to_dex_compiler.h b/compiler/dex/dex_to_dex_compiler.h index abd048167c..2105a9ded4 100644 --- a/compiler/dex/dex_to_dex_compiler.h +++ b/compiler/dex/dex_to_dex_compiler.h @@ -23,8 +23,8 @@ #include "base/bit_vector.h" #include "dex/dex_file.h" +#include "dex/invoke_type.h" #include "handle.h" -#include "invoke_type.h" #include "method_reference.h" #include "quicken_info.h" diff --git a/compiler/dex/inline_method_analyser.cc b/compiler/dex/inline_method_analyser.cc index ce67b85b99..dc044c1210 100644 --- a/compiler/dex/inline_method_analyser.cc +++ b/compiler/dex/inline_method_analyser.cc @@ -142,7 +142,7 @@ ArtMethod* GetTargetConstructor(ArtMethod* method, const Instruction* invoke_dir REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK_EQ(invoke_direct->Opcode(), Instruction::INVOKE_DIRECT); if (kIsDebugBuild) { - CodeItemDataAccessor accessor(method); + CodeItemDataAccessor accessor(method->DexInstructionData()); DCHECK_EQ(invoke_direct->VRegC_35c(), accessor.RegistersSize() - accessor.InsSize()); } @@ -324,9 +324,9 @@ bool DoAnalyseConstructor(const CodeItemDataAccessor* code_item, return false; } if (target_method->GetDeclaringClass()->IsObjectClass()) { - DCHECK_EQ(CodeItemDataAccessor(target_method).begin()->Opcode(), Instruction::RETURN_VOID); + DCHECK_EQ(target_method->DexInstructionData().begin()->Opcode(), Instruction::RETURN_VOID); } else { - CodeItemDataAccessor target_code_item(target_method); + CodeItemDataAccessor target_code_item(target_method->DexInstructionData()); if (!target_code_item.HasCodeItem()) { return false; // Native constructor? } @@ -430,7 +430,7 @@ static_assert(InlineMethodAnalyser::IGetVariant(Instruction::IGET_SHORT) == InlineMethodAnalyser::IPutVariant(Instruction::IPUT_SHORT), "iget/iput_short variant"); bool InlineMethodAnalyser::AnalyseMethodCode(ArtMethod* method, InlineMethod* result) { - CodeItemDataAccessor code_item(method); + CodeItemDataAccessor code_item(method->DexInstructionData()); if (!code_item.HasCodeItem()) { // Native or abstract. return false; diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 70cbb01569..c617f54f55 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -384,6 +384,12 @@ static optimizer::DexToDexCompiler::CompilationLevel GetDexToDexCompilationLevel Thread* self, const CompilerDriver& driver, Handle<mirror::ClassLoader> class_loader, const DexFile& dex_file, const DexFile::ClassDef& class_def) REQUIRES_SHARED(Locks::mutator_lock_) { + // When the dex file is uncompressed in the APK, we do not generate a copy in the .vdex + // file. As a result, dex2oat will map the dex file read-only, and we only need to check + // that to know if we can do quickening. + if (dex_file.GetContainer() != nullptr && dex_file.GetContainer()->IsReadOnly()) { + return optimizer::DexToDexCompiler::CompilationLevel::kDontDexToDexCompile; + } auto* const runtime = Runtime::Current(); DCHECK(driver.GetCompilerOptions().IsQuickeningCompilationEnabled()); const char* descriptor = dex_file.GetClassDescriptor(class_def); @@ -674,8 +680,7 @@ void CompilerDriver::Resolve(jobject class_loader, // TODO: Collect the relevant string indices in parallel, then allocate them sequentially in a // stable order. -static void ResolveConstStrings(ClassLinker* class_linker, - Handle<mirror::DexCache> dex_cache, +static void ResolveConstStrings(Handle<mirror::DexCache> dex_cache, const DexFile& dex_file, const DexFile::CodeItem* code_item) REQUIRES_SHARED(Locks::mutator_lock_) { @@ -684,6 +689,7 @@ static void ResolveConstStrings(ClassLinker* class_linker, return; } + ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); for (const DexInstructionPcPair& inst : CodeItemInstructionAccessor(dex_file, code_item)) { switch (inst->Opcode()) { case Instruction::CONST_STRING: @@ -731,105 +737,22 @@ static void ResolveConstStrings(CompilerDriver* driver, dex_file->StringByTypeIdx(class_def.class_idx_)); if (!compilation_enabled) { // Compilation is skipped, do not resolve const-string in code of this class. - // FIXME: Make sure that inlining honors this. b/26687569 + // TODO: Make sure that inlining honors this. continue; } // Direct and virtual methods. + int64_t previous_method_idx = -1; while (it.HasNextMethod()) { - ResolveConstStrings(class_linker, dex_cache, *dex_file, it.GetMethodCodeItem()); - it.Next(); - } - DCHECK(!it.HasNext()); - } - } -} - -// Initialize type check bit strings for check-cast and instance-of in the code. Done to have -// deterministic allocation behavior. Right now this is single-threaded for simplicity. -// TODO: Collect the relevant type indices in parallel, then process them sequentially in a -// stable order. - -static void InitializeTypeCheckBitstrings(CompilerDriver* driver, - ClassLinker* class_linker, - Handle<mirror::DexCache> dex_cache, - const DexFile& dex_file, - const DexFile::CodeItem* code_item) - REQUIRES_SHARED(Locks::mutator_lock_) { - if (code_item == nullptr) { - // Abstract or native method. - return; - } - - for (const DexInstructionPcPair& inst : CodeItemInstructionAccessor(dex_file, code_item)) { - switch (inst->Opcode()) { - case Instruction::CHECK_CAST: - case Instruction::INSTANCE_OF: { - dex::TypeIndex type_index( - (inst->Opcode() == Instruction::CHECK_CAST) ? inst->VRegB_21c() : inst->VRegC_22c()); - const char* descriptor = dex_file.StringByTypeIdx(type_index); - // We currently do not use the bitstring type check for array or final (including - // primitive) classes. We may reconsider this in future if it's deemed to be beneficial. - // And we cannot use it for classes outside the boot image as we do not know the runtime - // value of their bitstring when compiling (it may not even get assigned at runtime). - if (descriptor[0] == 'L' && driver->IsImageClass(descriptor)) { - ObjPtr<mirror::Class> klass = - class_linker->LookupResolvedType(type_index, - dex_cache.Get(), - /* class_loader */ nullptr); - CHECK(klass != nullptr) << descriptor << " should have been previously resolved."; - // Now assign the bitstring if the class is not final. Keep this in sync with sharpening. - if (!klass->IsFinal()) { - MutexLock subtype_check_lock(Thread::Current(), *Locks::subtype_check_lock_); - SubtypeCheck<ObjPtr<mirror::Class>>::EnsureAssigned(klass); - } + uint32_t method_idx = it.GetMemberIndex(); + if (method_idx == previous_method_idx) { + // smali can create dex files with two encoded_methods sharing the same method_idx + // http://code.google.com/p/smali/issues/detail?id=119 + it.Next(); + continue; } - break; - } - - default: - break; - } - } -} - -static void InitializeTypeCheckBitstrings(CompilerDriver* driver, - const std::vector<const DexFile*>& dex_files, - TimingLogger* timings) { - ScopedObjectAccess soa(Thread::Current()); - StackHandleScope<1> hs(soa.Self()); - ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); - MutableHandle<mirror::DexCache> dex_cache(hs.NewHandle<mirror::DexCache>(nullptr)); - - for (const DexFile* dex_file : dex_files) { - dex_cache.Assign(class_linker->FindDexCache(soa.Self(), *dex_file)); - TimingLogger::ScopedTiming t("Initialize type check bitstrings", timings); - - size_t class_def_count = dex_file->NumClassDefs(); - for (size_t class_def_index = 0; class_def_index < class_def_count; ++class_def_index) { - const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index); - - const uint8_t* class_data = dex_file->GetClassData(class_def); - if (class_data == nullptr) { - // empty class, probably a marker interface - continue; - } - - ClassDataItemIterator it(*dex_file, class_data); - it.SkipAllFields(); - - bool compilation_enabled = driver->IsClassToCompile( - dex_file->StringByTypeIdx(class_def.class_idx_)); - if (!compilation_enabled) { - // Compilation is skipped, do not look for type checks in code of this class. - // FIXME: Make sure that inlining honors this. b/26687569 - continue; - } - - // Direct and virtual methods. - while (it.HasNextMethod()) { - InitializeTypeCheckBitstrings( - driver, class_linker, dex_cache, *dex_file, it.GetMethodCodeItem()); + previous_method_idx = method_idx; + ResolveConstStrings(dex_cache, *dex_file, it.GetMethodCodeItem()); it.Next(); } DCHECK(!it.HasNext()); @@ -931,13 +854,6 @@ void CompilerDriver::PreCompile(jobject class_loader, UpdateImageClasses(timings); VLOG(compiler) << "UpdateImageClasses: " << GetMemoryUsageString(false); - - if (GetCompilerOptions().IsForceDeterminism() && GetCompilerOptions().IsBootImage()) { - // Initialize type check bit string used by check-cast and instanceof. - // Do this now to have a deterministic image. - // Note: This is done after UpdateImageClasses() at it relies on the image classes to be final. - InitializeTypeCheckBitstrings(this, dex_files, timings); - } } bool CompilerDriver::IsImageClass(const char* descriptor) const { @@ -1016,7 +932,7 @@ class ResolveCatchBlockExceptionsClassVisitor : public ClassVisitor { if (method->GetCodeItem() == nullptr) { return; // native or abstract method } - CodeItemDataAccessor accessor(method); + CodeItemDataAccessor accessor(method->DexInstructionData()); if (accessor.TriesSize() == 0) { return; // nothing to process } diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index 4b5916d572..b51e0debbb 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -77,9 +77,6 @@ class VdexFile; class VerificationResults; class VerifiedMethod; -// Compile-time flag to enable/disable bitstring type checks. -static constexpr bool kUseBitstringTypeCheck = true; - enum EntryPointCallingConvention { // ABI of invocations to a method's interpreter entry point. kInterpreterAbi, @@ -110,13 +107,13 @@ class CompilerDriver { ~CompilerDriver(); - // Set dex files that will be stored in the oat file after being compiled. + // Set dex files associated with the oat file being compiled. void SetDexFilesForOatFile(const std::vector<const DexFile*>& dex_files); // Set dex files classpath. void SetClasspathDexFiles(const std::vector<const DexFile*>& dex_files); - // Get dex file that will be stored in the oat file after being compiled. + // Get dex files associated with the the oat file being compiled. ArrayRef<const DexFile* const> GetDexFilesForOatFile() const { return ArrayRef<const DexFile* const>(dex_files_for_oat_file_); } @@ -533,7 +530,7 @@ class CompilerDriver { bool support_boot_image_fixup_; - // List of dex files that will be stored in the oat file. + // List of dex files associates with the oat file. std::vector<const DexFile*> dex_files_for_oat_file_; CompiledMethodStorage compiled_method_storage_; diff --git a/compiler/driver/compiler_options.cc b/compiler/driver/compiler_options.cc index 1780b1d7ed..2d82d79c4a 100644 --- a/compiler/driver/compiler_options.cc +++ b/compiler/driver/compiler_options.cc @@ -60,6 +60,7 @@ CompilerOptions::CompilerOptions() dump_cfg_append_(false), force_determinism_(false), deduplicate_code_(true), + count_hotness_in_compiled_code_(false), register_allocation_strategy_(RegisterAllocator::kRegisterAllocatorDefault), passes_to_run_(nullptr) { } diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h index 3f660293d2..18b0913430 100644 --- a/compiler/driver/compiler_options.h +++ b/compiler/driver/compiler_options.h @@ -274,6 +274,10 @@ class CompilerOptions FINAL { return dump_stats_; } + bool CountHotnessInCompiledCode() const { + return count_hotness_in_compiled_code_; + } + private: bool ParseDumpInitFailures(const std::string& option, std::string* error_msg); void ParseDumpCfgPasses(const StringPiece& option, UsageFn Usage); @@ -336,6 +340,10 @@ class CompilerOptions FINAL { // Whether code should be deduplicated. bool deduplicate_code_; + // Whether compiled code should increment the hotness count of ArtMethod. Note that the increments + // won't be atomic for performance reasons, so we accept races, just like in interpreter. + bool count_hotness_in_compiled_code_; + RegisterAllocator::Strategy register_allocation_strategy_; // If not null, specifies optimization passes which will be run instead of defaults. diff --git a/compiler/driver/compiler_options_map-inl.h b/compiler/driver/compiler_options_map-inl.h index f97ab08600..3b18db09fc 100644 --- a/compiler/driver/compiler_options_map-inl.h +++ b/compiler/driver/compiler_options_map-inl.h @@ -77,6 +77,9 @@ inline bool ReadCompilerOptions(Base& map, CompilerOptions* options, std::string } map.AssignIfExists(Base::VerboseMethods, &options->verbose_methods_); options->deduplicate_code_ = map.GetOrDefault(Base::DeduplicateCode); + if (map.Exists(Base::CountHotnessInCompiledCode)) { + options->count_hotness_in_compiled_code_ = true; + } if (map.Exists(Base::DumpTimings)) { options->dump_timings_ = true; @@ -137,6 +140,9 @@ inline void AddCompilerOptionsArgumentParserOptions(Builder& b) { .WithValueMap({{"false", false}, {"true", true}}) .IntoKey(Map::DeduplicateCode) + .Define({"--count-hotness-in-compiled-code"}) + .IntoKey(Map::CountHotnessInCompiledCode) + .Define({"--dump-timings"}) .IntoKey(Map::DumpTimings) diff --git a/compiler/driver/compiler_options_map.def b/compiler/driver/compiler_options_map.def index 2c56fd7974..acddae7299 100644 --- a/compiler/driver/compiler_options_map.def +++ b/compiler/driver/compiler_options_map.def @@ -58,6 +58,7 @@ COMPILER_OPTIONS_KEY (Unit, DumpCFGAppend) COMPILER_OPTIONS_KEY (std::string, RegisterAllocationStrategy) COMPILER_OPTIONS_KEY (ParseStringList<','>, VerboseMethods) COMPILER_OPTIONS_KEY (bool, DeduplicateCode, true) +COMPILER_OPTIONS_KEY (Unit, CountHotnessInCompiledCode) COMPILER_OPTIONS_KEY (Unit, DumpTimings) COMPILER_OPTIONS_KEY (Unit, DumpStats) diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index 2dafbf7f6d..3c5a37f958 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -438,8 +438,6 @@ class CodeGenerator : public DeletableArenaObject<kArenaAllocCodeGenerator> { case TypeCheckKind::kArrayCheck: case TypeCheckKind::kUnresolvedCheck: return false; - case TypeCheckKind::kBitstringCheck: - return true; } LOG(FATAL) << "Unreachable"; UNREACHABLE(); diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index b47a5cf3c4..1380596ab2 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -1488,6 +1488,14 @@ void CodeGeneratorARM64::GenerateFrameEntry() { MacroAssembler* masm = GetVIXLAssembler(); __ Bind(&frame_entry_label_); + if (GetCompilerOptions().CountHotnessInCompiledCode()) { + UseScratchRegisterScope temps(masm); + Register temp = temps.AcquireX(); + __ Ldrh(temp, MemOperand(kArtMethodRegister, ArtMethod::HotnessCountOffset().Int32Value())); + __ Add(temp, temp, 1); + __ Strh(temp, MemOperand(kArtMethodRegister, ArtMethod::HotnessCountOffset().Int32Value())); + } + bool do_overflow_check = FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kArm64) || !IsLeafMethod(); if (do_overflow_check) { @@ -2112,26 +2120,6 @@ void InstructionCodeGeneratorARM64::GenerateClassInitializationCheck(SlowPathCod __ Bind(slow_path->GetExitLabel()); } -void InstructionCodeGeneratorARM64::GenerateBitstringTypeCheckCompare( - HTypeCheckInstruction* check, vixl::aarch64::Register temp) { - uint32_t path_to_root = check->GetBitstringPathToRoot(); - uint32_t mask = check->GetBitstringMask(); - DCHECK(IsPowerOfTwo(mask + 1)); - size_t mask_bits = WhichPowerOf2(mask + 1); - - if (mask_bits == 16u) { - // Load only the bitstring part of the status word. - __ Ldrh(temp, HeapOperand(temp, mirror::Class::StatusOffset())); - } else { - // /* uint32_t */ temp = temp->status_ - __ Ldr(temp, HeapOperand(temp, mirror::Class::StatusOffset())); - // Extract the bitstring bits. - __ Ubfx(temp, temp, 0, mask_bits); - } - // Compare the bitstring bits to `path_to_root`. - __ Cmp(temp, path_to_root); -} - void CodeGeneratorARM64::GenerateMemoryBarrier(MemBarrierKind kind) { BarrierType type = BarrierAll; @@ -3521,6 +3509,15 @@ void InstructionCodeGeneratorARM64::HandleGoto(HInstruction* got, HBasicBlock* s HLoopInformation* info = block->GetLoopInformation(); if (info != nullptr && info->IsBackEdge(*block) && info->HasSuspendCheck()) { + if (codegen_->GetCompilerOptions().CountHotnessInCompiledCode()) { + UseScratchRegisterScope temps(GetVIXLAssembler()); + Register temp1 = temps.AcquireX(); + Register temp2 = temps.AcquireX(); + __ Ldr(temp1, MemOperand(sp, 0)); + __ Ldrh(temp2, MemOperand(temp1, ArtMethod::HotnessCountOffset().Int32Value())); + __ Add(temp2, temp2, 1); + __ Strh(temp2, MemOperand(temp1, ArtMethod::HotnessCountOffset().Int32Value())); + } GenerateSuspendCheck(info->GetSuspendCheck(), successor); return; } @@ -3860,8 +3857,6 @@ void LocationsBuilderARM64::VisitInstanceOf(HInstanceOf* instruction) { case TypeCheckKind::kInterfaceCheck: call_kind = LocationSummary::kCallOnSlowPath; break; - case TypeCheckKind::kBitstringCheck: - break; } LocationSummary* locations = @@ -3870,13 +3865,7 @@ void LocationsBuilderARM64::VisitInstanceOf(HInstanceOf* instruction) { locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers. } locations->SetInAt(0, Location::RequiresRegister()); - if (type_check_kind == TypeCheckKind::kBitstringCheck) { - locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); - locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); - locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); - } else { - locations->SetInAt(1, Location::RequiresRegister()); - } + locations->SetInAt(1, Location::RequiresRegister()); // The "out" register is used as a temporary, so it overlaps with the inputs. // Note that TypeCheckSlowPathARM64 uses this register too. locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); @@ -3889,9 +3878,7 @@ void InstructionCodeGeneratorARM64::VisitInstanceOf(HInstanceOf* instruction) { LocationSummary* locations = instruction->GetLocations(); Location obj_loc = locations->InAt(0); Register obj = InputRegisterAt(instruction, 0); - Register cls = (type_check_kind == TypeCheckKind::kBitstringCheck) - ? Register() - : InputRegisterAt(instruction, 1); + Register cls = InputRegisterAt(instruction, 1); Location out_loc = locations->Out(); Register out = OutputRegister(instruction); const size_t num_temps = NumberOfInstanceOfTemps(type_check_kind); @@ -4077,23 +4064,6 @@ void InstructionCodeGeneratorARM64::VisitInstanceOf(HInstanceOf* instruction) { } break; } - - case TypeCheckKind::kBitstringCheck: { - // /* HeapReference<Class> */ temp = obj->klass_ - GenerateReferenceLoadTwoRegisters(instruction, - out_loc, - obj_loc, - class_offset, - maybe_temp_loc, - kWithoutReadBarrier); - - GenerateBitstringTypeCheckCompare(instruction, out); - __ Cset(out, eq); - if (zero.IsLinked()) { - __ B(&done); - } - break; - } } if (zero.IsLinked()) { @@ -4116,13 +4086,7 @@ void LocationsBuilderARM64::VisitCheckCast(HCheckCast* instruction) { LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind); locations->SetInAt(0, Location::RequiresRegister()); - if (type_check_kind == TypeCheckKind::kBitstringCheck) { - locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); - locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); - locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); - } else { - locations->SetInAt(1, Location::RequiresRegister()); - } + locations->SetInAt(1, Location::RequiresRegister()); // Add temps for read barriers and other uses. One is used by TypeCheckSlowPathARM64. locations->AddRegisterTemps(NumberOfCheckCastTemps(type_check_kind)); } @@ -4132,9 +4096,7 @@ void InstructionCodeGeneratorARM64::VisitCheckCast(HCheckCast* instruction) { LocationSummary* locations = instruction->GetLocations(); Location obj_loc = locations->InAt(0); Register obj = InputRegisterAt(instruction, 0); - Register cls = (type_check_kind == TypeCheckKind::kBitstringCheck) - ? Register() - : InputRegisterAt(instruction, 1); + Register cls = InputRegisterAt(instruction, 1); const size_t num_temps = NumberOfCheckCastTemps(type_check_kind); DCHECK_GE(num_temps, 1u); DCHECK_LE(num_temps, 3u); @@ -4315,20 +4277,6 @@ void InstructionCodeGeneratorARM64::VisitCheckCast(HCheckCast* instruction) { __ B(ne, &start_loop); break; } - - case TypeCheckKind::kBitstringCheck: { - // /* HeapReference<Class> */ temp = obj->klass_ - GenerateReferenceLoadTwoRegisters(instruction, - temp_loc, - obj_loc, - class_offset, - maybe_temp2_loc, - kWithoutReadBarrier); - - GenerateBitstringTypeCheckCompare(instruction, temp); - __ B(ne, type_check_slow_path->GetEntryLabel()); - break; - } } __ Bind(&done); diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h index cc369de983..f92c94fda7 100644 --- a/compiler/optimizing/code_generator_arm64.h +++ b/compiler/optimizing/code_generator_arm64.h @@ -264,8 +264,6 @@ class InstructionCodeGeneratorARM64 : public InstructionCodeGenerator { private: void GenerateClassInitializationCheck(SlowPathCodeARM64* slow_path, vixl::aarch64::Register class_reg); - void GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, - vixl::aarch64::Register temp); void GenerateSuspendCheck(HSuspendCheck* instruction, HBasicBlock* successor); void HandleBinaryOp(HBinaryOperation* instr); diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc index 504c6479cc..18e7d1cc46 100644 --- a/compiler/optimizing/code_generator_arm_vixl.cc +++ b/compiler/optimizing/code_generator_arm_vixl.cc @@ -2485,17 +2485,21 @@ void CodeGeneratorARMVIXL::GenerateFrameEntry() { DCHECK(GetCompilerOptions().GetImplicitStackOverflowChecks()); __ Bind(&frame_entry_label_); + if (GetCompilerOptions().CountHotnessInCompiledCode()) { + UseScratchRegisterScope temps(GetVIXLAssembler()); + vixl32::Register temp = temps.Acquire(); + __ Ldrh(temp, MemOperand(kMethodRegister, ArtMethod::HotnessCountOffset().Int32Value())); + __ Add(temp, temp, 1); + __ Strh(temp, MemOperand(kMethodRegister, ArtMethod::HotnessCountOffset().Int32Value())); + } + if (HasEmptyFrame()) { return; } if (!skip_overflow_check) { - // Using r4 instead of IP saves 2 bytes. Start by asserting that r4 is available here. - for (vixl32::Register reg : kParameterCoreRegistersVIXL) { - DCHECK(!reg.Is(r4)); - } - DCHECK(!kCoreCalleeSaves.Includes(r4)); - vixl32::Register temp = r4; + UseScratchRegisterScope temps(GetVIXLAssembler()); + vixl32::Register temp = temps.Acquire(); __ Sub(temp, sp, Operand::From(GetStackOverflowReservedBytes(InstructionSet::kArm))); // The load must immediately precede RecordPcInfo. ExactAssemblyScope aas(GetVIXLAssembler(), @@ -2790,6 +2794,16 @@ void InstructionCodeGeneratorARMVIXL::HandleGoto(HInstruction* got, HBasicBlock* HLoopInformation* info = block->GetLoopInformation(); if (info != nullptr && info->IsBackEdge(*block) && info->HasSuspendCheck()) { + if (codegen_->GetCompilerOptions().CountHotnessInCompiledCode()) { + UseScratchRegisterScope temps(GetVIXLAssembler()); + vixl32::Register temp = temps.Acquire(); + __ Push(vixl32::Register(kMethodRegister)); + GetAssembler()->LoadFromOffset(kLoadWord, kMethodRegister, sp, kArmWordSize); + __ Ldrh(temp, MemOperand(kMethodRegister, ArtMethod::HotnessCountOffset().Int32Value())); + __ Add(temp, temp, 1); + __ Strh(temp, MemOperand(kMethodRegister, ArtMethod::HotnessCountOffset().Int32Value())); + __ Pop(vixl32::Register(kMethodRegister)); + } GenerateSuspendCheck(info->GetSuspendCheck(), successor); return; } @@ -7195,67 +7209,6 @@ void InstructionCodeGeneratorARMVIXL::GenerateClassInitializationCheck( __ Bind(slow_path->GetExitLabel()); } -void InstructionCodeGeneratorARMVIXL::GenerateBitstringTypeCheckCompare( - HTypeCheckInstruction* check, - vixl32::Register temp, - vixl32::FlagsUpdate flags_update) { - uint32_t path_to_root = check->GetBitstringPathToRoot(); - uint32_t mask = check->GetBitstringMask(); - DCHECK(IsPowerOfTwo(mask + 1)); - size_t mask_bits = WhichPowerOf2(mask + 1); - - // Note that HInstanceOf shall check for zero value in `temp` but HCheckCast needs - // the Z flag for BNE. This is indicated by the `flags_update` parameter. - if (mask_bits == 16u) { - // Load only the bitstring part of the status word. - __ Ldrh(temp, MemOperand(temp, mirror::Class::StatusOffset().Int32Value())); - // Check if the bitstring bits are equal to `path_to_root`. - if (flags_update == SetFlags) { - __ Cmp(temp, path_to_root); - } else { - __ Sub(temp, temp, path_to_root); - } - } else { - // /* uint32_t */ temp = temp->status_ - __ Ldr(temp, MemOperand(temp, mirror::Class::StatusOffset().Int32Value())); - if (GetAssembler()->ShifterOperandCanHold(SUB, path_to_root)) { - // Compare the bitstring bits using SUB. - __ Sub(temp, temp, path_to_root); - // Shift out bits that do not contribute to the comparison. - __ Lsl(flags_update, temp, temp, dchecked_integral_cast<uint32_t>(32u - mask_bits)); - } else if (IsUint<16>(path_to_root)) { - if (temp.IsLow()) { - // Note: Optimized for size but contains one more dependent instruction than necessary. - // MOVW+SUB(register) would be 8 bytes unless we find a low-reg temporary but the - // macro assembler would use the high reg IP for the constant by default. - // Compare the bitstring bits using SUB. - __ Sub(temp, temp, path_to_root & 0x00ffu); // 16-bit SUB (immediate) T2 - __ Sub(temp, temp, path_to_root & 0xff00u); // 32-bit SUB (immediate) T3 - // Shift out bits that do not contribute to the comparison. - __ Lsl(flags_update, temp, temp, dchecked_integral_cast<uint32_t>(32u - mask_bits)); - } else { - // Extract the bitstring bits. - __ Ubfx(temp, temp, 0, mask_bits); - // Check if the bitstring bits are equal to `path_to_root`. - if (flags_update == SetFlags) { - __ Cmp(temp, path_to_root); - } else { - __ Sub(temp, temp, path_to_root); - } - } - } else { - // Shift out bits that do not contribute to the comparison. - __ Lsl(temp, temp, dchecked_integral_cast<uint32_t>(32u - mask_bits)); - // Check if the shifted bitstring bits are equal to `path_to_root << (32u - mask_bits)`. - if (flags_update == SetFlags) { - __ Cmp(temp, path_to_root << (32u - mask_bits)); - } else { - __ Sub(temp, temp, path_to_root << (32u - mask_bits)); - } - } - } -} - HLoadString::LoadKind CodeGeneratorARMVIXL::GetSupportedLoadStringKind( HLoadString::LoadKind desired_string_load_kind) { switch (desired_string_load_kind) { @@ -7447,8 +7400,6 @@ void LocationsBuilderARMVIXL::VisitInstanceOf(HInstanceOf* instruction) { case TypeCheckKind::kInterfaceCheck: call_kind = LocationSummary::kCallOnSlowPath; break; - case TypeCheckKind::kBitstringCheck: - break; } LocationSummary* locations = @@ -7457,13 +7408,7 @@ void LocationsBuilderARMVIXL::VisitInstanceOf(HInstanceOf* instruction) { locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers. } locations->SetInAt(0, Location::RequiresRegister()); - if (type_check_kind == TypeCheckKind::kBitstringCheck) { - locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); - locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); - locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); - } else { - locations->SetInAt(1, Location::RequiresRegister()); - } + locations->SetInAt(1, Location::RequiresRegister()); // The "out" register is used as a temporary, so it overlaps with the inputs. // Note that TypeCheckSlowPathARM uses this register too. locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); @@ -7478,9 +7423,7 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) LocationSummary* locations = instruction->GetLocations(); Location obj_loc = locations->InAt(0); vixl32::Register obj = InputRegisterAt(instruction, 0); - vixl32::Register cls = (type_check_kind == TypeCheckKind::kBitstringCheck) - ? vixl32::Register() - : InputRegisterAt(instruction, 1); + vixl32::Register cls = InputRegisterAt(instruction, 1); Location out_loc = locations->Out(); vixl32::Register out = OutputRegister(instruction); const size_t num_temps = NumberOfInstanceOfTemps(type_check_kind); @@ -7720,26 +7663,6 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) __ B(slow_path->GetEntryLabel()); break; } - - case TypeCheckKind::kBitstringCheck: { - // /* HeapReference<Class> */ temp = obj->klass_ - GenerateReferenceLoadTwoRegisters(instruction, - out_loc, - obj_loc, - class_offset, - maybe_temp_loc, - kWithoutReadBarrier); - - GenerateBitstringTypeCheckCompare(instruction, out, DontCare); - // If `out` is a low reg and we would have another low reg temp, we could - // optimize this as RSBS+ADC, see GenerateConditionWithZero(). - // - // Also, in some cases when `out` is a low reg and we're loading a constant to IP - // it would make sense to use CMP+MOV+IT+MOV instead of SUB+CLZ+LSR as the code size - // would be the same and we would have fewer direct data dependencies. - codegen_->GenerateConditionWithZero(kCondEQ, out, out); // CLZ+LSR - break; - } } if (done.IsReferenced()) { @@ -7757,13 +7680,7 @@ void LocationsBuilderARMVIXL::VisitCheckCast(HCheckCast* instruction) { LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind); locations->SetInAt(0, Location::RequiresRegister()); - if (type_check_kind == TypeCheckKind::kBitstringCheck) { - locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); - locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); - locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); - } else { - locations->SetInAt(1, Location::RequiresRegister()); - } + locations->SetInAt(1, Location::RequiresRegister()); locations->AddRegisterTemps(NumberOfCheckCastTemps(type_check_kind)); } @@ -7772,9 +7689,7 @@ void InstructionCodeGeneratorARMVIXL::VisitCheckCast(HCheckCast* instruction) { LocationSummary* locations = instruction->GetLocations(); Location obj_loc = locations->InAt(0); vixl32::Register obj = InputRegisterAt(instruction, 0); - vixl32::Register cls = (type_check_kind == TypeCheckKind::kBitstringCheck) - ? vixl32::Register() - : InputRegisterAt(instruction, 1); + vixl32::Register cls = InputRegisterAt(instruction, 1); Location temp_loc = locations->GetTemp(0); vixl32::Register temp = RegisterFrom(temp_loc); const size_t num_temps = NumberOfCheckCastTemps(type_check_kind); @@ -7959,20 +7874,6 @@ void InstructionCodeGeneratorARMVIXL::VisitCheckCast(HCheckCast* instruction) { __ B(ne, &start_loop, /* far_target */ false); break; } - - case TypeCheckKind::kBitstringCheck: { - // /* HeapReference<Class> */ temp = obj->klass_ - GenerateReferenceLoadTwoRegisters(instruction, - temp_loc, - obj_loc, - class_offset, - maybe_temp2_loc, - kWithoutReadBarrier); - - GenerateBitstringTypeCheckCompare(instruction, temp, SetFlags); - __ B(ne, type_check_slow_path->GetEntryLabel()); - break; - } } if (done.IsReferenced()) { __ Bind(&done); diff --git a/compiler/optimizing/code_generator_arm_vixl.h b/compiler/optimizing/code_generator_arm_vixl.h index bd815f45b3..38570bb0fe 100644 --- a/compiler/optimizing/code_generator_arm_vixl.h +++ b/compiler/optimizing/code_generator_arm_vixl.h @@ -322,9 +322,6 @@ class InstructionCodeGeneratorARMVIXL : public InstructionCodeGenerator { void GenerateSuspendCheck(HSuspendCheck* instruction, HBasicBlock* successor); void GenerateClassInitializationCheck(LoadClassSlowPathARMVIXL* slow_path, vixl32::Register class_reg); - void GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, - vixl::aarch32::Register temp, - vixl::aarch32::FlagsUpdate flags_update); void GenerateAndConst(vixl::aarch32::Register out, vixl::aarch32::Register first, uint32_t value); void GenerateOrrConst(vixl::aarch32::Register out, vixl::aarch32::Register first, uint32_t value); void GenerateEorConst(vixl::aarch32::Register out, vixl::aarch32::Register first, uint32_t value); diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc index 2ed0ab79a1..51fb4dacfd 100644 --- a/compiler/optimizing/code_generator_mips.cc +++ b/compiler/optimizing/code_generator_mips.cc @@ -1276,6 +1276,10 @@ static dwarf::Reg DWARFReg(Register reg) { void CodeGeneratorMIPS::GenerateFrameEntry() { __ Bind(&frame_entry_label_); + if (GetCompilerOptions().CountHotnessInCompiledCode()) { + LOG(WARNING) << "Unimplemented hotness update in mips backend"; + } + bool do_overflow_check = FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kMips) || !IsLeafMethod(); @@ -1929,34 +1933,6 @@ void InstructionCodeGeneratorMIPS::GenerateClassInitializationCheck(SlowPathCode __ Bind(slow_path->GetExitLabel()); } -void InstructionCodeGeneratorMIPS::GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, - Register temp) { - uint32_t path_to_root = check->GetBitstringPathToRoot(); - uint32_t mask = check->GetBitstringMask(); - DCHECK(IsPowerOfTwo(mask + 1)); - size_t mask_bits = WhichPowerOf2(mask + 1); - - if (mask_bits == 16u) { - // Load only the bitstring part of the status word. - __ LoadFromOffset( - kLoadUnsignedHalfword, temp, temp, mirror::Class::StatusOffset().Int32Value()); - // Compare the bitstring bits using XOR. - __ Xori(temp, temp, dchecked_integral_cast<uint16_t>(path_to_root)); - } else { - // /* uint32_t */ temp = temp->status_ - __ LoadFromOffset(kLoadWord, temp, temp, mirror::Class::StatusOffset().Int32Value()); - // Compare the bitstring bits using XOR. - if (IsUint<16>(path_to_root)) { - __ Xori(temp, temp, dchecked_integral_cast<uint16_t>(path_to_root)); - } else { - __ LoadConst32(TMP, path_to_root); - __ Xor(temp, temp, TMP); - } - // Shift out bits that do not contribute to the comparison. - __ Sll(temp, temp, 32 - mask_bits); - } -} - void InstructionCodeGeneratorMIPS::GenerateMemoryBarrier(MemBarrierKind kind ATTRIBUTE_UNUSED) { __ Sync(0); // Only stype 0 is supported. } @@ -3317,20 +3293,12 @@ void LocationsBuilderMIPS::VisitCheckCast(HCheckCast* instruction) { case TypeCheckKind::kInterfaceCheck: call_kind = LocationSummary::kCallOnSlowPath; break; - case TypeCheckKind::kBitstringCheck: - break; } LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind); locations->SetInAt(0, Location::RequiresRegister()); - if (type_check_kind == TypeCheckKind::kBitstringCheck) { - locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); - locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); - locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); - } else { - locations->SetInAt(1, Location::RequiresRegister()); - } + locations->SetInAt(1, Location::RequiresRegister()); locations->AddRegisterTemps(NumberOfCheckCastTemps(type_check_kind)); } @@ -3339,7 +3307,7 @@ void InstructionCodeGeneratorMIPS::VisitCheckCast(HCheckCast* instruction) { LocationSummary* locations = instruction->GetLocations(); Location obj_loc = locations->InAt(0); Register obj = obj_loc.AsRegister<Register>(); - Location cls = locations->InAt(1); + Register cls = locations->InAt(1).AsRegister<Register>(); Location temp_loc = locations->GetTemp(0); Register temp = temp_loc.AsRegister<Register>(); const size_t num_temps = NumberOfCheckCastTemps(type_check_kind); @@ -3389,7 +3357,7 @@ void InstructionCodeGeneratorMIPS::VisitCheckCast(HCheckCast* instruction) { kWithoutReadBarrier); // Jump to slow path for throwing the exception or doing a // more involved array check. - __ Bne(temp, cls.AsRegister<Register>(), slow_path->GetEntryLabel()); + __ Bne(temp, cls, slow_path->GetEntryLabel()); break; } @@ -3415,7 +3383,7 @@ void InstructionCodeGeneratorMIPS::VisitCheckCast(HCheckCast* instruction) { // exception. __ Beqz(temp, slow_path->GetEntryLabel()); // Otherwise, compare the classes. - __ Bne(temp, cls.AsRegister<Register>(), &loop); + __ Bne(temp, cls, &loop); break; } @@ -3430,7 +3398,7 @@ void InstructionCodeGeneratorMIPS::VisitCheckCast(HCheckCast* instruction) { // Walk over the class hierarchy to find a match. MipsLabel loop; __ Bind(&loop); - __ Beq(temp, cls.AsRegister<Register>(), &done); + __ Beq(temp, cls, &done); // /* HeapReference<Class> */ temp = temp->super_class_ GenerateReferenceLoadOneRegister(instruction, temp_loc, @@ -3453,7 +3421,7 @@ void InstructionCodeGeneratorMIPS::VisitCheckCast(HCheckCast* instruction) { maybe_temp2_loc, kWithoutReadBarrier); // Do an exact check. - __ Beq(temp, cls.AsRegister<Register>(), &done); + __ Beq(temp, cls, &done); // Otherwise, we need to check that the object's class is a non-primitive array. // /* HeapReference<Class> */ temp = temp->component_type_ GenerateReferenceLoadOneRegister(instruction, @@ -3512,21 +3480,7 @@ void InstructionCodeGeneratorMIPS::VisitCheckCast(HCheckCast* instruction) { // Go to next interface. __ Addiu(TMP, TMP, -2); // Compare the classes and continue the loop if they do not match. - __ Bne(AT, cls.AsRegister<Register>(), &loop); - break; - } - - case TypeCheckKind::kBitstringCheck: { - // /* HeapReference<Class> */ temp = obj->klass_ - GenerateReferenceLoadTwoRegisters(instruction, - temp_loc, - obj_loc, - class_offset, - maybe_temp2_loc, - kWithoutReadBarrier); - - GenerateBitstringTypeCheckCompare(instruction, temp); - __ Bnez(temp, slow_path->GetEntryLabel()); + __ Bne(AT, cls, &loop); break; } } @@ -7257,8 +7211,6 @@ void LocationsBuilderMIPS::VisitInstanceOf(HInstanceOf* instruction) { case TypeCheckKind::kInterfaceCheck: call_kind = LocationSummary::kCallOnSlowPath; break; - case TypeCheckKind::kBitstringCheck: - break; } LocationSummary* locations = @@ -7267,13 +7219,7 @@ void LocationsBuilderMIPS::VisitInstanceOf(HInstanceOf* instruction) { locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers. } locations->SetInAt(0, Location::RequiresRegister()); - if (type_check_kind == TypeCheckKind::kBitstringCheck) { - locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); - locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); - locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); - } else { - locations->SetInAt(1, Location::RequiresRegister()); - } + locations->SetInAt(1, Location::RequiresRegister()); // The output does overlap inputs. // Note that TypeCheckSlowPathMIPS uses this register too. locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); @@ -7285,7 +7231,7 @@ void InstructionCodeGeneratorMIPS::VisitInstanceOf(HInstanceOf* instruction) { LocationSummary* locations = instruction->GetLocations(); Location obj_loc = locations->InAt(0); Register obj = obj_loc.AsRegister<Register>(); - Location cls = locations->InAt(1); + Register cls = locations->InAt(1).AsRegister<Register>(); Location out_loc = locations->Out(); Register out = out_loc.AsRegister<Register>(); const size_t num_temps = NumberOfInstanceOfTemps(type_check_kind); @@ -7315,7 +7261,7 @@ void InstructionCodeGeneratorMIPS::VisitInstanceOf(HInstanceOf* instruction) { maybe_temp_loc, kCompilerReadBarrierOption); // Classes must be equal for the instanceof to succeed. - __ Xor(out, out, cls.AsRegister<Register>()); + __ Xor(out, out, cls); __ Sltiu(out, out, 1); break; } @@ -7340,7 +7286,7 @@ void InstructionCodeGeneratorMIPS::VisitInstanceOf(HInstanceOf* instruction) { kCompilerReadBarrierOption); // If `out` is null, we use it for the result, and jump to `done`. __ Beqz(out, &done); - __ Bne(out, cls.AsRegister<Register>(), &loop); + __ Bne(out, cls, &loop); __ LoadConst32(out, 1); break; } @@ -7356,7 +7302,7 @@ void InstructionCodeGeneratorMIPS::VisitInstanceOf(HInstanceOf* instruction) { // Walk over the class hierarchy to find a match. MipsLabel loop, success; __ Bind(&loop); - __ Beq(out, cls.AsRegister<Register>(), &success); + __ Beq(out, cls, &success); // /* HeapReference<Class> */ out = out->super_class_ GenerateReferenceLoadOneRegister(instruction, out_loc, @@ -7381,7 +7327,7 @@ void InstructionCodeGeneratorMIPS::VisitInstanceOf(HInstanceOf* instruction) { kCompilerReadBarrierOption); // Do an exact check. MipsLabel success; - __ Beq(out, cls.AsRegister<Register>(), &success); + __ Beq(out, cls, &success); // Otherwise, we need to check that the object's class is a non-primitive array. // /* HeapReference<Class> */ out = out->component_type_ GenerateReferenceLoadOneRegister(instruction, @@ -7413,7 +7359,7 @@ void InstructionCodeGeneratorMIPS::VisitInstanceOf(HInstanceOf* instruction) { slow_path = new (codegen_->GetScopedAllocator()) TypeCheckSlowPathMIPS( instruction, /* is_fatal */ false); codegen_->AddSlowPath(slow_path); - __ Bne(out, cls.AsRegister<Register>(), slow_path->GetEntryLabel()); + __ Bne(out, cls, slow_path->GetEntryLabel()); __ LoadConst32(out, 1); break; } @@ -7445,20 +7391,6 @@ void InstructionCodeGeneratorMIPS::VisitInstanceOf(HInstanceOf* instruction) { __ B(slow_path->GetEntryLabel()); break; } - - case TypeCheckKind::kBitstringCheck: { - // /* HeapReference<Class> */ temp = obj->klass_ - GenerateReferenceLoadTwoRegisters(instruction, - out_loc, - obj_loc, - class_offset, - maybe_temp_loc, - kWithoutReadBarrier); - - GenerateBitstringTypeCheckCompare(instruction, out); - __ Sltiu(out, out, 1); - break; - } } __ Bind(&done); diff --git a/compiler/optimizing/code_generator_mips.h b/compiler/optimizing/code_generator_mips.h index ffeb3b00a7..32b3e4221f 100644 --- a/compiler/optimizing/code_generator_mips.h +++ b/compiler/optimizing/code_generator_mips.h @@ -237,7 +237,6 @@ class InstructionCodeGeneratorMIPS : public InstructionCodeGenerator { private: void GenerateClassInitializationCheck(SlowPathCodeMIPS* slow_path, Register class_reg); void GenerateSuspendCheck(HSuspendCheck* check, HBasicBlock* successor); - void GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, Register temp); void HandleBinaryOp(HBinaryOperation* operation); void HandleCondition(HCondition* instruction); void HandleShift(HBinaryOperation* operation); diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc index 3ae8a30754..480b9178d2 100644 --- a/compiler/optimizing/code_generator_mips64.cc +++ b/compiler/optimizing/code_generator_mips64.cc @@ -1079,6 +1079,10 @@ static dwarf::Reg DWARFReg(FpuRegister reg) { void CodeGeneratorMIPS64::GenerateFrameEntry() { __ Bind(&frame_entry_label_); + if (GetCompilerOptions().CountHotnessInCompiledCode()) { + LOG(WARNING) << "Unimplemented hotness update in mips64 backend"; + } + bool do_overflow_check = FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kMips64) || !IsLeafMethod(); @@ -1775,34 +1779,6 @@ void InstructionCodeGeneratorMIPS64::GenerateClassInitializationCheck(SlowPathCo __ Bind(slow_path->GetExitLabel()); } -void InstructionCodeGeneratorMIPS64::GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, - GpuRegister temp) { - uint32_t path_to_root = check->GetBitstringPathToRoot(); - uint32_t mask = check->GetBitstringMask(); - DCHECK(IsPowerOfTwo(mask + 1)); - size_t mask_bits = WhichPowerOf2(mask + 1); - - if (mask_bits == 16u) { - // Load only the bitstring part of the status word. - __ LoadFromOffset( - kLoadUnsignedHalfword, temp, temp, mirror::Class::StatusOffset().Int32Value()); - // Compare the bitstring bits using XOR. - __ Xori(temp, temp, dchecked_integral_cast<uint16_t>(path_to_root)); - } else { - // /* uint32_t */ temp = temp->status_ - __ LoadFromOffset(kLoadWord, temp, temp, mirror::Class::StatusOffset().Int32Value()); - // Compare the bitstring bits using XOR. - if (IsUint<16>(path_to_root)) { - __ Xori(temp, temp, dchecked_integral_cast<uint16_t>(path_to_root)); - } else { - __ LoadConst32(TMP, path_to_root); - __ Xor(temp, temp, TMP); - } - // Shift out bits that do not contribute to the comparison. - __ Sll(temp, temp, 32 - mask_bits); - } -} - void InstructionCodeGeneratorMIPS64::GenerateMemoryBarrier(MemBarrierKind kind ATTRIBUTE_UNUSED) { __ Sync(0); // only stype 0 is supported } @@ -2872,20 +2848,12 @@ void LocationsBuilderMIPS64::VisitCheckCast(HCheckCast* instruction) { case TypeCheckKind::kInterfaceCheck: call_kind = LocationSummary::kCallOnSlowPath; break; - case TypeCheckKind::kBitstringCheck: - break; } LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind); locations->SetInAt(0, Location::RequiresRegister()); - if (type_check_kind == TypeCheckKind::kBitstringCheck) { - locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); - locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); - locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); - } else { - locations->SetInAt(1, Location::RequiresRegister()); - } + locations->SetInAt(1, Location::RequiresRegister()); locations->AddRegisterTemps(NumberOfCheckCastTemps(type_check_kind)); } @@ -2894,7 +2862,7 @@ void InstructionCodeGeneratorMIPS64::VisitCheckCast(HCheckCast* instruction) { LocationSummary* locations = instruction->GetLocations(); Location obj_loc = locations->InAt(0); GpuRegister obj = obj_loc.AsRegister<GpuRegister>(); - Location cls = locations->InAt(1); + GpuRegister cls = locations->InAt(1).AsRegister<GpuRegister>(); Location temp_loc = locations->GetTemp(0); GpuRegister temp = temp_loc.AsRegister<GpuRegister>(); const size_t num_temps = NumberOfCheckCastTemps(type_check_kind); @@ -2944,7 +2912,7 @@ void InstructionCodeGeneratorMIPS64::VisitCheckCast(HCheckCast* instruction) { kWithoutReadBarrier); // Jump to slow path for throwing the exception or doing a // more involved array check. - __ Bnec(temp, cls.AsRegister<GpuRegister>(), slow_path->GetEntryLabel()); + __ Bnec(temp, cls, slow_path->GetEntryLabel()); break; } @@ -2970,7 +2938,7 @@ void InstructionCodeGeneratorMIPS64::VisitCheckCast(HCheckCast* instruction) { // exception. __ Beqzc(temp, slow_path->GetEntryLabel()); // Otherwise, compare the classes. - __ Bnec(temp, cls.AsRegister<GpuRegister>(), &loop); + __ Bnec(temp, cls, &loop); break; } @@ -2985,7 +2953,7 @@ void InstructionCodeGeneratorMIPS64::VisitCheckCast(HCheckCast* instruction) { // Walk over the class hierarchy to find a match. Mips64Label loop; __ Bind(&loop); - __ Beqc(temp, cls.AsRegister<GpuRegister>(), &done); + __ Beqc(temp, cls, &done); // /* HeapReference<Class> */ temp = temp->super_class_ GenerateReferenceLoadOneRegister(instruction, temp_loc, @@ -3008,7 +2976,7 @@ void InstructionCodeGeneratorMIPS64::VisitCheckCast(HCheckCast* instruction) { maybe_temp2_loc, kWithoutReadBarrier); // Do an exact check. - __ Beqc(temp, cls.AsRegister<GpuRegister>(), &done); + __ Beqc(temp, cls, &done); // Otherwise, we need to check that the object's class is a non-primitive array. // /* HeapReference<Class> */ temp = temp->component_type_ GenerateReferenceLoadOneRegister(instruction, @@ -3067,21 +3035,7 @@ void InstructionCodeGeneratorMIPS64::VisitCheckCast(HCheckCast* instruction) { __ Daddiu(temp, temp, 2 * kHeapReferenceSize); __ Addiu(TMP, TMP, -2); // Compare the classes and continue the loop if they do not match. - __ Bnec(AT, cls.AsRegister<GpuRegister>(), &loop); - break; - } - - case TypeCheckKind::kBitstringCheck: { - // /* HeapReference<Class> */ temp = obj->klass_ - GenerateReferenceLoadTwoRegisters(instruction, - temp_loc, - obj_loc, - class_offset, - maybe_temp2_loc, - kWithoutReadBarrier); - - GenerateBitstringTypeCheckCompare(instruction, temp); - __ Bnezc(temp, slow_path->GetEntryLabel()); + __ Bnec(AT, cls, &loop); break; } } @@ -5574,8 +5528,6 @@ void LocationsBuilderMIPS64::VisitInstanceOf(HInstanceOf* instruction) { case TypeCheckKind::kInterfaceCheck: call_kind = LocationSummary::kCallOnSlowPath; break; - case TypeCheckKind::kBitstringCheck: - break; } LocationSummary* locations = @@ -5584,13 +5536,7 @@ void LocationsBuilderMIPS64::VisitInstanceOf(HInstanceOf* instruction) { locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers. } locations->SetInAt(0, Location::RequiresRegister()); - if (type_check_kind == TypeCheckKind::kBitstringCheck) { - locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); - locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); - locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); - } else { - locations->SetInAt(1, Location::RequiresRegister()); - } + locations->SetInAt(1, Location::RequiresRegister()); // The output does overlap inputs. // Note that TypeCheckSlowPathMIPS64 uses this register too. locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); @@ -5602,7 +5548,7 @@ void InstructionCodeGeneratorMIPS64::VisitInstanceOf(HInstanceOf* instruction) { LocationSummary* locations = instruction->GetLocations(); Location obj_loc = locations->InAt(0); GpuRegister obj = obj_loc.AsRegister<GpuRegister>(); - Location cls = locations->InAt(1); + GpuRegister cls = locations->InAt(1).AsRegister<GpuRegister>(); Location out_loc = locations->Out(); GpuRegister out = out_loc.AsRegister<GpuRegister>(); const size_t num_temps = NumberOfInstanceOfTemps(type_check_kind); @@ -5632,7 +5578,7 @@ void InstructionCodeGeneratorMIPS64::VisitInstanceOf(HInstanceOf* instruction) { maybe_temp_loc, kCompilerReadBarrierOption); // Classes must be equal for the instanceof to succeed. - __ Xor(out, out, cls.AsRegister<GpuRegister>()); + __ Xor(out, out, cls); __ Sltiu(out, out, 1); break; } @@ -5657,7 +5603,7 @@ void InstructionCodeGeneratorMIPS64::VisitInstanceOf(HInstanceOf* instruction) { kCompilerReadBarrierOption); // If `out` is null, we use it for the result, and jump to `done`. __ Beqzc(out, &done); - __ Bnec(out, cls.AsRegister<GpuRegister>(), &loop); + __ Bnec(out, cls, &loop); __ LoadConst32(out, 1); break; } @@ -5673,7 +5619,7 @@ void InstructionCodeGeneratorMIPS64::VisitInstanceOf(HInstanceOf* instruction) { // Walk over the class hierarchy to find a match. Mips64Label loop, success; __ Bind(&loop); - __ Beqc(out, cls.AsRegister<GpuRegister>(), &success); + __ Beqc(out, cls, &success); // /* HeapReference<Class> */ out = out->super_class_ GenerateReferenceLoadOneRegister(instruction, out_loc, @@ -5698,7 +5644,7 @@ void InstructionCodeGeneratorMIPS64::VisitInstanceOf(HInstanceOf* instruction) { kCompilerReadBarrierOption); // Do an exact check. Mips64Label success; - __ Beqc(out, cls.AsRegister<GpuRegister>(), &success); + __ Beqc(out, cls, &success); // Otherwise, we need to check that the object's class is a non-primitive array. // /* HeapReference<Class> */ out = out->component_type_ GenerateReferenceLoadOneRegister(instruction, @@ -5730,7 +5676,7 @@ void InstructionCodeGeneratorMIPS64::VisitInstanceOf(HInstanceOf* instruction) { slow_path = new (codegen_->GetScopedAllocator()) TypeCheckSlowPathMIPS64( instruction, /* is_fatal */ false); codegen_->AddSlowPath(slow_path); - __ Bnec(out, cls.AsRegister<GpuRegister>(), slow_path->GetEntryLabel()); + __ Bnec(out, cls, slow_path->GetEntryLabel()); __ LoadConst32(out, 1); break; } @@ -5762,20 +5708,6 @@ void InstructionCodeGeneratorMIPS64::VisitInstanceOf(HInstanceOf* instruction) { __ Bc(slow_path->GetEntryLabel()); break; } - - case TypeCheckKind::kBitstringCheck: { - // /* HeapReference<Class> */ temp = obj->klass_ - GenerateReferenceLoadTwoRegisters(instruction, - out_loc, - obj_loc, - class_offset, - maybe_temp_loc, - kWithoutReadBarrier); - - GenerateBitstringTypeCheckCompare(instruction, out); - __ Sltiu(out, out, 1); - break; - } } __ Bind(&done); diff --git a/compiler/optimizing/code_generator_mips64.h b/compiler/optimizing/code_generator_mips64.h index 87d5a9c15a..d479410f07 100644 --- a/compiler/optimizing/code_generator_mips64.h +++ b/compiler/optimizing/code_generator_mips64.h @@ -233,7 +233,6 @@ class InstructionCodeGeneratorMIPS64 : public InstructionCodeGenerator { private: void GenerateClassInitializationCheck(SlowPathCodeMIPS64* slow_path, GpuRegister class_reg); - void GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, GpuRegister temp); void GenerateSuspendCheck(HSuspendCheck* check, HBasicBlock* successor); void HandleBinaryOp(HBinaryOperation* operation); void HandleCondition(HCondition* instruction); diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index e85f9001b1..c52c7ff7f1 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -1061,6 +1061,11 @@ void CodeGeneratorX86::GenerateFrameEntry() { IsLeafMethod() && !FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kX86); DCHECK(GetCompilerOptions().GetImplicitStackOverflowChecks()); + if (GetCompilerOptions().CountHotnessInCompiledCode()) { + __ addw(Address(kMethodRegisterArgument, ArtMethod::HotnessCountOffset().Int32Value()), + Immediate(1)); + } + if (!skip_overflow_check) { size_t reserved_bytes = GetStackOverflowReservedBytes(InstructionSet::kX86); __ testl(EAX, Address(ESP, -static_cast<int32_t>(reserved_bytes))); @@ -1357,6 +1362,12 @@ void InstructionCodeGeneratorX86::HandleGoto(HInstruction* got, HBasicBlock* suc HLoopInformation* info = block->GetLoopInformation(); if (info != nullptr && info->IsBackEdge(*block) && info->HasSuspendCheck()) { + if (codegen_->GetCompilerOptions().CountHotnessInCompiledCode()) { + __ pushl(EAX); + __ movl(EAX, Address(ESP, kX86WordSize)); + __ addw(Address(EAX, ArtMethod::HotnessCountOffset().Int32Value()), Immediate(1)); + __ popl(EAX); + } GenerateSuspendCheck(info->GetSuspendCheck(), successor); return; } @@ -6234,27 +6245,6 @@ void InstructionCodeGeneratorX86::GenerateClassInitializationCheck( // No need for memory fence, thanks to the X86 memory model. } -void InstructionCodeGeneratorX86::GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, - Register temp) { - uint32_t path_to_root = check->GetBitstringPathToRoot(); - uint32_t mask = check->GetBitstringMask(); - DCHECK(IsPowerOfTwo(mask + 1)); - size_t mask_bits = WhichPowerOf2(mask + 1); - - if ((false) && mask_bits == 16u) { - // FIXME: cmpw() erroneously emits the constant as 32 bits instead of 16 bits. b/71853552 - // Compare the bitstring in memory. - __ cmpw(Address(temp, mirror::Class::StatusOffset()), Immediate(path_to_root)); - } else { - // /* uint32_t */ temp = temp->status_ - __ movl(temp, Address(temp, mirror::Class::StatusOffset())); - // Compare the bitstring bits using SUB. - __ subl(temp, Immediate(path_to_root)); - // Shift out bits that do not contribute to the comparison. - __ shll(temp, Immediate(32u - mask_bits)); - } -} - HLoadString::LoadKind CodeGeneratorX86::GetSupportedLoadStringKind( HLoadString::LoadKind desired_string_load_kind) { switch (desired_string_load_kind) { @@ -6447,8 +6437,6 @@ void LocationsBuilderX86::VisitInstanceOf(HInstanceOf* instruction) { case TypeCheckKind::kInterfaceCheck: call_kind = LocationSummary::kCallOnSlowPath; break; - case TypeCheckKind::kBitstringCheck: - break; } LocationSummary* locations = @@ -6457,13 +6445,7 @@ void LocationsBuilderX86::VisitInstanceOf(HInstanceOf* instruction) { locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers. } locations->SetInAt(0, Location::RequiresRegister()); - if (type_check_kind == TypeCheckKind::kBitstringCheck) { - locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); - locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); - locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); - } else { - locations->SetInAt(1, Location::Any()); - } + locations->SetInAt(1, Location::Any()); // Note that TypeCheckSlowPathX86 uses this "out" register too. locations->SetOut(Location::RequiresRegister()); // When read barriers are enabled, we need a temporary register for some cases. @@ -6684,21 +6666,6 @@ void InstructionCodeGeneratorX86::VisitInstanceOf(HInstanceOf* instruction) { } break; } - - case TypeCheckKind::kBitstringCheck: { - // /* HeapReference<Class> */ temp = obj->klass_ - GenerateReferenceLoadTwoRegisters(instruction, - out_loc, - obj_loc, - class_offset, - kWithoutReadBarrier); - - GenerateBitstringTypeCheckCompare(instruction, out); - __ j(kNotEqual, &zero); - __ movl(out, Immediate(1)); - __ jmp(&done); - break; - } } if (zero.IsLinked()) { @@ -6725,10 +6692,6 @@ void LocationsBuilderX86::VisitCheckCast(HCheckCast* instruction) { // Require a register for the interface check since there is a loop that compares the class to // a memory address. locations->SetInAt(1, Location::RequiresRegister()); - } else if (type_check_kind == TypeCheckKind::kBitstringCheck) { - locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); - locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); - locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); } else { locations->SetInAt(1, Location::Any()); } @@ -6948,19 +6911,6 @@ void InstructionCodeGeneratorX86::VisitCheckCast(HCheckCast* instruction) { __ MaybeUnpoisonHeapReference(cls.AsRegister<Register>()); break; } - - case TypeCheckKind::kBitstringCheck: { - // /* HeapReference<Class> */ temp = obj->klass_ - GenerateReferenceLoadTwoRegisters(instruction, - temp_loc, - obj_loc, - class_offset, - kWithoutReadBarrier); - - GenerateBitstringTypeCheckCompare(instruction, temp); - __ j(kNotEqual, type_check_slow_path->GetEntryLabel()); - break; - } } __ Bind(&done); diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h index 2d14d4cdc2..0082853184 100644 --- a/compiler/optimizing/code_generator_x86.h +++ b/compiler/optimizing/code_generator_x86.h @@ -211,7 +211,6 @@ class InstructionCodeGeneratorX86 : public InstructionCodeGenerator { // the suspend call. void GenerateSuspendCheck(HSuspendCheck* check, HBasicBlock* successor); void GenerateClassInitializationCheck(SlowPathCode* slow_path, Register class_reg); - void GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, Register temp); void HandleBitwiseOperation(HBinaryOperation* instruction); void GenerateDivRemIntegral(HBinaryOperation* instruction); void DivRemOneOrMinusOne(HBinaryOperation* instruction); diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index 9f8b1bb038..ee5918de71 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -1268,6 +1268,12 @@ void CodeGeneratorX86_64::GenerateFrameEntry() { && !FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kX86_64); DCHECK(GetCompilerOptions().GetImplicitStackOverflowChecks()); + if (GetCompilerOptions().CountHotnessInCompiledCode()) { + __ addw(Address(CpuRegister(kMethodRegisterArgument), + ArtMethod::HotnessCountOffset().Int32Value()), + Immediate(1)); + } + if (!skip_overflow_check) { size_t reserved_bytes = GetStackOverflowReservedBytes(InstructionSet::kX86_64); __ testq(CpuRegister(RAX), Address(CpuRegister(RSP), -static_cast<int32_t>(reserved_bytes))); @@ -1459,6 +1465,11 @@ void InstructionCodeGeneratorX86_64::HandleGoto(HInstruction* got, HBasicBlock* HLoopInformation* info = block->GetLoopInformation(); if (info != nullptr && info->IsBackEdge(*block) && info->HasSuspendCheck()) { + if (codegen_->GetCompilerOptions().CountHotnessInCompiledCode()) { + __ movq(CpuRegister(TMP), Address(CpuRegister(RSP), 0)); + __ addw(Address(CpuRegister(TMP), ArtMethod::HotnessCountOffset().Int32Value()), + Immediate(1)); + } GenerateSuspendCheck(info->GetSuspendCheck(), successor); return; } @@ -5440,27 +5451,6 @@ void InstructionCodeGeneratorX86_64::GenerateClassInitializationCheck( // No need for memory fence, thanks to the x86-64 memory model. } -void InstructionCodeGeneratorX86_64::GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, - CpuRegister temp) { - uint32_t path_to_root = check->GetBitstringPathToRoot(); - uint32_t mask = check->GetBitstringMask(); - DCHECK(IsPowerOfTwo(mask + 1)); - size_t mask_bits = WhichPowerOf2(mask + 1); - - if ((false) && mask_bits == 16u) { - // FIXME: cmpw() erroneously emits the constant as 32 bits instead of 16 bits. b/71853552 - // Compare the bitstring in memory. - __ cmpw(Address(temp, mirror::Class::StatusOffset()), Immediate(path_to_root)); - } else { - // /* uint32_t */ temp = temp->status_ - __ movl(temp, Address(temp, mirror::Class::StatusOffset())); - // Compare the bitstring bits using SUB. - __ subl(temp, Immediate(path_to_root)); - // Shift out bits that do not contribute to the comparison. - __ shll(temp, Immediate(32u - mask_bits)); - } -} - HLoadClass::LoadKind CodeGeneratorX86_64::GetSupportedLoadClassKind( HLoadClass::LoadKind desired_class_load_kind) { switch (desired_class_load_kind) { @@ -5833,8 +5823,6 @@ void LocationsBuilderX86_64::VisitInstanceOf(HInstanceOf* instruction) { case TypeCheckKind::kInterfaceCheck: call_kind = LocationSummary::kCallOnSlowPath; break; - case TypeCheckKind::kBitstringCheck: - break; } LocationSummary* locations = @@ -5843,13 +5831,7 @@ void LocationsBuilderX86_64::VisitInstanceOf(HInstanceOf* instruction) { locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers. } locations->SetInAt(0, Location::RequiresRegister()); - if (type_check_kind == TypeCheckKind::kBitstringCheck) { - locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); - locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); - locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); - } else { - locations->SetInAt(1, Location::Any()); - } + locations->SetInAt(1, Location::Any()); // Note that TypeCheckSlowPathX86_64 uses this "out" register too. locations->SetOut(Location::RequiresRegister()); // When read barriers are enabled, we need a temporary register for @@ -6078,27 +6060,6 @@ void InstructionCodeGeneratorX86_64::VisitInstanceOf(HInstanceOf* instruction) { } break; } - - case TypeCheckKind::kBitstringCheck: { - // /* HeapReference<Class> */ temp = obj->klass_ - GenerateReferenceLoadTwoRegisters(instruction, - out_loc, - obj_loc, - class_offset, - kWithoutReadBarrier); - - GenerateBitstringTypeCheckCompare(instruction, out); - if (zero.IsLinked()) { - __ j(kNotEqual, &zero); - __ movl(out, Immediate(1)); - __ jmp(&done); - } else { - __ setcc(kEqual, out); - // setcc only sets the low byte. - __ andl(out, Immediate(1)); - } - break; - } } if (zero.IsLinked()) { @@ -6125,10 +6086,6 @@ void LocationsBuilderX86_64::VisitCheckCast(HCheckCast* instruction) { // Require a register for the interface check since there is a loop that compares the class to // a memory address. locations->SetInAt(1, Location::RequiresRegister()); - } else if (type_check_kind == TypeCheckKind::kBitstringCheck) { - locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); - locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); - locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); } else { locations->SetInAt(1, Location::Any()); } @@ -6315,7 +6272,7 @@ void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) { break; } - case TypeCheckKind::kInterfaceCheck: { + case TypeCheckKind::kInterfaceCheck: // Fast path for the interface check. Try to avoid read barriers to improve the fast path. // We can not get false positives by doing this. // /* HeapReference<Class> */ temp = obj->klass_ @@ -6351,20 +6308,6 @@ void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) { // If `cls` was poisoned above, unpoison it. __ MaybeUnpoisonHeapReference(cls.AsRegister<CpuRegister>()); break; - } - - case TypeCheckKind::kBitstringCheck: { - // /* HeapReference<Class> */ temp = obj->klass_ - GenerateReferenceLoadTwoRegisters(instruction, - temp_loc, - obj_loc, - class_offset, - kWithoutReadBarrier); - - GenerateBitstringTypeCheckCompare(instruction, temp); - __ j(kNotEqual, type_check_slow_path->GetEntryLabel()); - break; - } } if (done.IsLinked()) { diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h index 97f8ec7ae0..e86123ef01 100644 --- a/compiler/optimizing/code_generator_x86_64.h +++ b/compiler/optimizing/code_generator_x86_64.h @@ -208,7 +208,6 @@ class InstructionCodeGeneratorX86_64 : public InstructionCodeGenerator { // the suspend call. void GenerateSuspendCheck(HSuspendCheck* instruction, HBasicBlock* successor); void GenerateClassInitializationCheck(SlowPathCode* slow_path, CpuRegister class_reg); - void GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, CpuRegister temp); void HandleBitwiseOperation(HBinaryOperation* operation); void GenerateRemFP(HRem* rem); void DivRemOneOrMinusOne(HBinaryOperation* instruction); diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc index fbcbe3608e..c88baa8610 100644 --- a/compiler/optimizing/graph_checker.cc +++ b/compiler/optimizing/graph_checker.cc @@ -25,11 +25,6 @@ #include "base/bit_vector-inl.h" #include "base/scoped_arena_allocator.h" #include "base/scoped_arena_containers.h" -#include "handle.h" -#include "mirror/class.h" -#include "obj_ptr-inl.h" -#include "scoped_thread_state_change-inl.h" -#include "subtype_check.h" namespace art { @@ -553,83 +548,28 @@ void GraphChecker::VisitReturnVoid(HReturnVoid* ret) { } } -void GraphChecker::CheckTypeCheckBitstringInput(HTypeCheckInstruction* check, - size_t input_pos, - bool check_value, - uint32_t expected_value, - const char* name) { - if (!check->InputAt(input_pos)->IsIntConstant()) { - AddError(StringPrintf("%s:%d (bitstring) expects a HIntConstant input %zu (%s), not %s:%d.", - check->DebugName(), - check->GetId(), - input_pos, - name, - check->InputAt(2)->DebugName(), - check->InputAt(2)->GetId())); - } else if (check_value) { - uint32_t actual_value = - static_cast<uint32_t>(check->InputAt(input_pos)->AsIntConstant()->GetValue()); - if (actual_value != expected_value) { - AddError(StringPrintf("%s:%d (bitstring) has %s 0x%x, not 0x%x as expected.", - check->DebugName(), - check->GetId(), - name, - actual_value, - expected_value)); - } - } -} - -void GraphChecker::HandleTypeCheckInstruction(HTypeCheckInstruction* check) { +void GraphChecker::VisitCheckCast(HCheckCast* check) { VisitInstruction(check); HInstruction* input = check->InputAt(1); - if (check->GetTypeCheckKind() == TypeCheckKind::kBitstringCheck) { - if (!input->IsNullConstant()) { - AddError(StringPrintf("%s:%d (bitstring) expects a HNullConstant as second input, not %s:%d.", - check->DebugName(), - check->GetId(), - input->DebugName(), - input->GetId())); - } - bool check_values = false; - BitString::StorageType expected_path_to_root = 0u; - BitString::StorageType expected_mask = 0u; - { - ScopedObjectAccess soa(Thread::Current()); - ObjPtr<mirror::Class> klass = check->GetClass().Get(); - MutexLock subtype_check_lock(Thread::Current(), *Locks::subtype_check_lock_); - SubtypeCheckInfo::State state = SubtypeCheck<ObjPtr<mirror::Class>>::GetState(klass); - if (state == SubtypeCheckInfo::kAssigned) { - expected_path_to_root = - SubtypeCheck<ObjPtr<mirror::Class>>::GetEncodedPathToRootForTarget(klass); - expected_mask = SubtypeCheck<ObjPtr<mirror::Class>>::GetEncodedPathToRootMask(klass); - check_values = true; - } else { - AddError(StringPrintf("%s:%d (bitstring) references a class with unassigned bitstring.", - check->DebugName(), - check->GetId())); - } - } - CheckTypeCheckBitstringInput( - check, /* input_pos */ 2, check_values, expected_path_to_root, "path_to_root"); - CheckTypeCheckBitstringInput(check, /* input_pos */ 3, check_values, expected_mask, "mask"); - } else { - if (!input->IsLoadClass()) { - AddError(StringPrintf("%s:%d (classic) expects a HLoadClass as second input, not %s:%d.", - check->DebugName(), - check->GetId(), - input->DebugName(), - input->GetId())); - } + if (!input->IsLoadClass()) { + AddError(StringPrintf("%s:%d expects a HLoadClass as second input, not %s:%d.", + check->DebugName(), + check->GetId(), + input->DebugName(), + input->GetId())); } } -void GraphChecker::VisitCheckCast(HCheckCast* check) { - HandleTypeCheckInstruction(check); -} - void GraphChecker::VisitInstanceOf(HInstanceOf* instruction) { - HandleTypeCheckInstruction(instruction); + VisitInstruction(instruction); + HInstruction* input = instruction->InputAt(1); + if (!input->IsLoadClass()) { + AddError(StringPrintf("%s:%d expects a HLoadClass as second input, not %s:%d.", + instruction->DebugName(), + instruction->GetId(), + input->DebugName(), + input->GetId())); + } } void GraphChecker::HandleLoop(HBasicBlock* loop_header) { diff --git a/compiler/optimizing/graph_checker.h b/compiler/optimizing/graph_checker.h index dbedc40518..0f0b49d240 100644 --- a/compiler/optimizing/graph_checker.h +++ b/compiler/optimizing/graph_checker.h @@ -71,12 +71,6 @@ class GraphChecker : public HGraphDelegateVisitor { void VisitTryBoundary(HTryBoundary* try_boundary) OVERRIDE; void VisitTypeConversion(HTypeConversion* instruction) OVERRIDE; - void CheckTypeCheckBitstringInput(HTypeCheckInstruction* check, - size_t input_pos, - bool check_value, - uint32_t expected_value, - const char* name); - void HandleTypeCheckInstruction(HTypeCheckInstruction* instruction); void HandleLoop(HBasicBlock* loop_header); void HandleBooleanInput(HInstruction* instruction, size_t input_index); diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc index 55191214a6..12c69889ab 100644 --- a/compiler/optimizing/graph_visualizer.cc +++ b/compiler/optimizing/graph_visualizer.cc @@ -389,23 +389,16 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor { StartAttributeStream("load_kind") << load_string->GetLoadKind(); } - void HandleTypeCheckInstruction(HTypeCheckInstruction* check) { - StartAttributeStream("check_kind") << check->GetTypeCheckKind(); - StartAttributeStream("must_do_null_check") << std::boolalpha - << check->MustDoNullCheck() << std::noboolalpha; - if (check->GetTypeCheckKind() == TypeCheckKind::kBitstringCheck) { - StartAttributeStream("path_to_root") << std::hex - << "0x" << check->GetBitstringPathToRoot() << std::dec; - StartAttributeStream("mask") << std::hex << "0x" << check->GetBitstringMask() << std::dec; - } - } - void VisitCheckCast(HCheckCast* check_cast) OVERRIDE { - HandleTypeCheckInstruction(check_cast); + StartAttributeStream("check_kind") << check_cast->GetTypeCheckKind(); + StartAttributeStream("must_do_null_check") << std::boolalpha + << check_cast->MustDoNullCheck() << std::noboolalpha; } void VisitInstanceOf(HInstanceOf* instance_of) OVERRIDE { - HandleTypeCheckInstruction(instance_of); + StartAttributeStream("check_kind") << instance_of->GetTypeCheckKind(); + StartAttributeStream("must_do_null_check") << std::boolalpha + << instance_of->MustDoNullCheck() << std::noboolalpha; } void VisitArrayLength(HArrayLength* array_length) OVERRIDE { @@ -655,32 +648,20 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor { << std::boolalpha << loop_info->IsIrreducible() << std::noboolalpha; } - // For the builder and the inliner, we want to add extra information on HInstructions - // that have reference types, and also HInstanceOf/HCheckcast. if ((IsPass(HGraphBuilder::kBuilderPassName) || IsPass(HInliner::kInlinerPassName)) - && (instruction->GetType() == DataType::Type::kReference || - instruction->IsInstanceOf() || - instruction->IsCheckCast())) { - ReferenceTypeInfo info = (instruction->GetType() == DataType::Type::kReference) - ? instruction->IsLoadClass() - ? instruction->AsLoadClass()->GetLoadedClassRTI() - : instruction->GetReferenceTypeInfo() - : instruction->IsInstanceOf() - ? instruction->AsInstanceOf()->GetTargetClassRTI() - : instruction->AsCheckCast()->GetTargetClassRTI(); + && (instruction->GetType() == DataType::Type::kReference)) { + ReferenceTypeInfo info = instruction->IsLoadClass() + ? instruction->AsLoadClass()->GetLoadedClassRTI() + : instruction->GetReferenceTypeInfo(); ScopedObjectAccess soa(Thread::Current()); if (info.IsValid()) { StartAttributeStream("klass") << mirror::Class::PrettyDescriptor(info.GetTypeHandle().Get()); - if (instruction->GetType() == DataType::Type::kReference) { - StartAttributeStream("can_be_null") - << std::boolalpha << instruction->CanBeNull() << std::noboolalpha; - } + StartAttributeStream("can_be_null") + << std::boolalpha << instruction->CanBeNull() << std::noboolalpha; StartAttributeStream("exact") << std::boolalpha << info.IsExact() << std::noboolalpha; - } else if (instruction->IsLoadClass() || - instruction->IsInstanceOf() || - instruction->IsCheckCast()) { + } else if (instruction->IsLoadClass()) { StartAttributeStream("klass") << "unresolved"; } else { // The NullConstant may be added to the graph during other passes that happen between diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index 452be6feae..035e5ce3e1 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -392,8 +392,9 @@ ArtMethod* HInliner::TryCHADevirtualization(ArtMethod* resolved_method) { return single_impl; } -static bool AlwaysThrows(ArtMethod* method) { - CodeItemDataAccessor accessor(method); +static bool AlwaysThrows(ArtMethod* method) + REQUIRES_SHARED(Locks::mutator_lock_) { + CodeItemDataAccessor accessor(method->DexInstructionData()); // Skip native methods, methods with try blocks, and methods that are too large. if (!accessor.HasCodeItem() || accessor.TriesSize() != 0 || @@ -1418,7 +1419,7 @@ bool HInliner::TryBuildAndInline(HInvoke* invoke_instruction, bool same_dex_file = IsSameDexFile(*outer_compilation_unit_.GetDexFile(), *method->GetDexFile()); - CodeItemDataAccessor accessor(method); + CodeItemDataAccessor accessor(method->DexInstructionData()); if (!accessor.HasCodeItem()) { LOG_FAIL_NO_STAT() @@ -1697,7 +1698,7 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction, const DexFile::CodeItem* code_item = resolved_method->GetCodeItem(); const DexFile& callee_dex_file = *resolved_method->GetDexFile(); uint32_t method_index = resolved_method->GetDexMethodIndex(); - CodeItemDebugInfoAccessor code_item_accessor(resolved_method); + CodeItemDebugInfoAccessor code_item_accessor(resolved_method->DexInstructionDebugInfo()); ClassLinker* class_linker = caller_compilation_unit_.GetClassLinker(); Handle<mirror::DexCache> dex_cache = NewHandleIfDifferent(resolved_method->GetDexCache(), caller_compilation_unit_.GetDexCache(), diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h index e81d97b0a8..02465d37ba 100644 --- a/compiler/optimizing/inliner.h +++ b/compiler/optimizing/inliner.h @@ -18,7 +18,7 @@ #define ART_COMPILER_OPTIMIZING_INLINER_H_ #include "dex/dex_file_types.h" -#include "invoke_type.h" +#include "dex/invoke_type.h" #include "jit/profile_compilation_info.h" #include "optimization.h" diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc index 0205c6a4d3..64a1eccf60 100644 --- a/compiler/optimizing/instruction_builder.cc +++ b/compiler/optimizing/instruction_builder.cc @@ -1811,6 +1811,29 @@ void HInstructionBuilder::BuildFillWideArrayData(HInstruction* object, } } +static TypeCheckKind ComputeTypeCheckKind(Handle<mirror::Class> cls) + REQUIRES_SHARED(Locks::mutator_lock_) { + if (cls == nullptr) { + return TypeCheckKind::kUnresolvedCheck; + } else if (cls->IsInterface()) { + return TypeCheckKind::kInterfaceCheck; + } else if (cls->IsArrayClass()) { + if (cls->GetComponentType()->IsObjectClass()) { + return TypeCheckKind::kArrayObjectCheck; + } else if (cls->CannotBeAssignedFromOtherTypes()) { + return TypeCheckKind::kExactCheck; + } else { + return TypeCheckKind::kArrayCheck; + } + } else if (cls->IsFinal()) { + return TypeCheckKind::kExactCheck; + } else if (cls->IsAbstract()) { + return TypeCheckKind::kAbstractClassCheck; + } else { + return TypeCheckKind::kClassHierarchyCheck; + } +} + void HInstructionBuilder::BuildLoadString(dex::StringIndex string_index, uint32_t dex_pc) { HLoadString* load_string = new (allocator_) HLoadString(graph_->GetCurrentMethod(), string_index, *dex_file_, dex_pc); @@ -1825,8 +1848,22 @@ void HInstructionBuilder::BuildLoadString(dex::StringIndex string_index, uint32_ HLoadClass* HInstructionBuilder::BuildLoadClass(dex::TypeIndex type_index, uint32_t dex_pc) { ScopedObjectAccess soa(Thread::Current()); const DexFile& dex_file = *dex_compilation_unit_->GetDexFile(); - Handle<mirror::Class> klass = ResolveClass(soa, type_index); - bool needs_access_check = LoadClassNeedsAccessCheck(klass); + Handle<mirror::ClassLoader> class_loader = dex_compilation_unit_->GetClassLoader(); + Handle<mirror::Class> klass = handles_->NewHandle(compiler_driver_->ResolveClass( + soa, dex_compilation_unit_->GetDexCache(), class_loader, type_index, dex_compilation_unit_)); + + bool needs_access_check = true; + if (klass != nullptr) { + if (klass->IsPublic()) { + needs_access_check = false; + } else { + ObjPtr<mirror::Class> compiling_class = GetCompilingClass(); + if (compiling_class != nullptr && compiling_class->CanAccess(klass.Get())) { + needs_access_check = false; + } + } + } + return BuildLoadClass(type_index, dex_file, klass, dex_pc, needs_access_check); } @@ -1871,83 +1908,25 @@ HLoadClass* HInstructionBuilder::BuildLoadClass(dex::TypeIndex type_index, return load_class; } -Handle<mirror::Class> HInstructionBuilder::ResolveClass(ScopedObjectAccess& soa, - dex::TypeIndex type_index) { - Handle<mirror::ClassLoader> class_loader = dex_compilation_unit_->GetClassLoader(); - ObjPtr<mirror::Class> klass = compiler_driver_->ResolveClass( - soa, dex_compilation_unit_->GetDexCache(), class_loader, type_index, dex_compilation_unit_); - // TODO: Avoid creating excessive handles if the method references the same class repeatedly. - // (Use a map on the local_allocator_.) - return handles_->NewHandle(klass); -} - -bool HInstructionBuilder::LoadClassNeedsAccessCheck(Handle<mirror::Class> klass) { - if (klass == nullptr) { - return true; - } else if (klass->IsPublic()) { - return false; - } else { - ObjPtr<mirror::Class> compiling_class = GetCompilingClass(); - return compiling_class == nullptr || !compiling_class->CanAccess(klass.Get()); - } -} - void HInstructionBuilder::BuildTypeCheck(const Instruction& instruction, uint8_t destination, uint8_t reference, dex::TypeIndex type_index, uint32_t dex_pc) { HInstruction* object = LoadLocal(reference, DataType::Type::kReference); + HLoadClass* cls = BuildLoadClass(type_index, dex_pc); ScopedObjectAccess soa(Thread::Current()); - const DexFile& dex_file = *dex_compilation_unit_->GetDexFile(); - Handle<mirror::Class> klass = ResolveClass(soa, type_index); - bool needs_access_check = LoadClassNeedsAccessCheck(klass); - TypeCheckKind check_kind = HSharpening::ComputeTypeCheckKind( - klass.Get(), code_generator_, compiler_driver_, needs_access_check); - - HInstruction* class_or_null = nullptr; - HIntConstant* bitstring_path_to_root = nullptr; - HIntConstant* bitstring_mask = nullptr; - if (check_kind == TypeCheckKind::kBitstringCheck) { - // TODO: Allow using the bitstring check also if we need an access check. - DCHECK(!needs_access_check); - class_or_null = graph_->GetNullConstant(dex_pc); - MutexLock subtype_check_lock(Thread::Current(), *Locks::subtype_check_lock_); - uint32_t path_to_root = - SubtypeCheck<ObjPtr<mirror::Class>>::GetEncodedPathToRootForTarget(klass.Get()); - uint32_t mask = SubtypeCheck<ObjPtr<mirror::Class>>::GetEncodedPathToRootMask(klass.Get()); - bitstring_path_to_root = graph_->GetIntConstant(static_cast<int32_t>(path_to_root), dex_pc); - bitstring_mask = graph_->GetIntConstant(static_cast<int32_t>(mask), dex_pc); - } else { - class_or_null = BuildLoadClass(type_index, dex_file, klass, dex_pc, needs_access_check); - } - DCHECK(class_or_null != nullptr); - + TypeCheckKind check_kind = ComputeTypeCheckKind(cls->GetClass()); if (instruction.Opcode() == Instruction::INSTANCE_OF) { - AppendInstruction(new (allocator_) HInstanceOf(object, - class_or_null, - check_kind, - klass, - dex_pc, - allocator_, - bitstring_path_to_root, - bitstring_mask)); + AppendInstruction(new (allocator_) HInstanceOf(object, cls, check_kind, dex_pc)); UpdateLocal(destination, current_block_->GetLastInstruction()); } else { DCHECK_EQ(instruction.Opcode(), Instruction::CHECK_CAST); // We emit a CheckCast followed by a BoundType. CheckCast is a statement // which may throw. If it succeeds BoundType sets the new type of `object` // for all subsequent uses. - AppendInstruction( - new (allocator_) HCheckCast(object, - class_or_null, - check_kind, - klass, - dex_pc, - allocator_, - bitstring_path_to_root, - bitstring_mask)); + AppendInstruction(new (allocator_) HCheckCast(object, cls, check_kind, dex_pc)); AppendInstruction(new (allocator_) HBoundType(object, dex_pc)); UpdateLocal(reference, current_block_->GetLastInstruction()); } diff --git a/compiler/optimizing/instruction_builder.h b/compiler/optimizing/instruction_builder.h index f78829232d..4428c53277 100644 --- a/compiler/optimizing/instruction_builder.h +++ b/compiler/optimizing/instruction_builder.h @@ -39,7 +39,6 @@ class DexCompilationUnit; class HBasicBlockBuilder; class Instruction; class OptimizingCompilerStats; -class ScopedObjectAccess; class SsaBuilder; class VariableSizedHandleScope; @@ -233,12 +232,6 @@ class HInstructionBuilder : public ValueObject { bool needs_access_check) REQUIRES_SHARED(Locks::mutator_lock_); - Handle<mirror::Class> ResolveClass(ScopedObjectAccess& soa, dex::TypeIndex type_index) - REQUIRES_SHARED(Locks::mutator_lock_); - - bool LoadClassNeedsAccessCheck(Handle<mirror::Class> klass) - REQUIRES_SHARED(Locks::mutator_lock_); - // Returns the outer-most compiling method's class. ObjPtr<mirror::Class> GetOutermostCompilingClass() const; diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index 2538fa38f1..a42a85dc1d 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -576,9 +576,7 @@ bool InstructionSimplifierVisitor::CanEnsureNotNullAt(HInstruction* input, HInst // Returns whether doing a type test between the class of `object` against `klass` has // a statically known outcome. The result of the test is stored in `outcome`. -static bool TypeCheckHasKnownOutcome(ReferenceTypeInfo class_rti, - HInstruction* object, - /*out*/bool* outcome) { +static bool TypeCheckHasKnownOutcome(HLoadClass* klass, HInstruction* object, bool* outcome) { DCHECK(!object->IsNullConstant()) << "Null constants should be special cased"; ReferenceTypeInfo obj_rti = object->GetReferenceTypeInfo(); ScopedObjectAccess soa(Thread::Current()); @@ -588,6 +586,7 @@ static bool TypeCheckHasKnownOutcome(ReferenceTypeInfo class_rti, return false; } + ReferenceTypeInfo class_rti = klass->GetLoadedClassRTI(); if (!class_rti.IsValid()) { // Happens when the loaded class is unresolved. return false; @@ -612,8 +611,8 @@ static bool TypeCheckHasKnownOutcome(ReferenceTypeInfo class_rti, void InstructionSimplifierVisitor::VisitCheckCast(HCheckCast* check_cast) { HInstruction* object = check_cast->InputAt(0); - if (check_cast->GetTypeCheckKind() != TypeCheckKind::kBitstringCheck && - check_cast->GetTargetClass()->NeedsAccessCheck()) { + HLoadClass* load_class = check_cast->InputAt(1)->AsLoadClass(); + if (load_class->NeedsAccessCheck()) { // If we need to perform an access check we cannot remove the instruction. return; } @@ -631,18 +630,15 @@ void InstructionSimplifierVisitor::VisitCheckCast(HCheckCast* check_cast) { // Note: The `outcome` is initialized to please valgrind - the compiler can reorder // the return value check with the `outcome` check, b/27651442 . bool outcome = false; - if (TypeCheckHasKnownOutcome(check_cast->GetTargetClassRTI(), object, &outcome)) { + if (TypeCheckHasKnownOutcome(load_class, object, &outcome)) { if (outcome) { check_cast->GetBlock()->RemoveInstruction(check_cast); MaybeRecordStat(stats_, MethodCompilationStat::kRemovedCheckedCast); - if (check_cast->GetTypeCheckKind() != TypeCheckKind::kBitstringCheck) { - HLoadClass* load_class = check_cast->GetTargetClass(); - if (!load_class->HasUses()) { - // We cannot rely on DCE to remove the class because the `HLoadClass` thinks it can throw. - // However, here we know that it cannot because the checkcast was successfull, hence - // the class was already loaded. - load_class->GetBlock()->RemoveInstruction(load_class); - } + if (!load_class->HasUses()) { + // We cannot rely on DCE to remove the class because the `HLoadClass` thinks it can throw. + // However, here we know that it cannot because the checkcast was successfull, hence + // the class was already loaded. + load_class->GetBlock()->RemoveInstruction(load_class); } } else { // Don't do anything for exceptional cases for now. Ideally we should remove @@ -653,8 +649,8 @@ void InstructionSimplifierVisitor::VisitCheckCast(HCheckCast* check_cast) { void InstructionSimplifierVisitor::VisitInstanceOf(HInstanceOf* instruction) { HInstruction* object = instruction->InputAt(0); - if (instruction->GetTypeCheckKind() != TypeCheckKind::kBitstringCheck && - instruction->GetTargetClass()->NeedsAccessCheck()) { + HLoadClass* load_class = instruction->InputAt(1)->AsLoadClass(); + if (load_class->NeedsAccessCheck()) { // If we need to perform an access check we cannot remove the instruction. return; } @@ -677,7 +673,7 @@ void InstructionSimplifierVisitor::VisitInstanceOf(HInstanceOf* instruction) { // Note: The `outcome` is initialized to please valgrind - the compiler can reorder // the return value check with the `outcome` check, b/27651442 . bool outcome = false; - if (TypeCheckHasKnownOutcome(instruction->GetTargetClassRTI(), object, &outcome)) { + if (TypeCheckHasKnownOutcome(load_class, object, &outcome)) { MaybeRecordStat(stats_, MethodCompilationStat::kRemovedInstanceOf); if (outcome && can_be_null) { // Type test will succeed, we just need a null test. @@ -690,14 +686,11 @@ void InstructionSimplifierVisitor::VisitInstanceOf(HInstanceOf* instruction) { } RecordSimplification(); instruction->GetBlock()->RemoveInstruction(instruction); - if (outcome && instruction->GetTypeCheckKind() != TypeCheckKind::kBitstringCheck) { - HLoadClass* load_class = instruction->GetTargetClass(); - if (!load_class->HasUses()) { - // We cannot rely on DCE to remove the class because the `HLoadClass` thinks it can throw. - // However, here we know that it cannot because the instanceof check was successfull, hence - // the class was already loaded. - load_class->GetBlock()->RemoveInstruction(load_class); - } + if (outcome && !load_class->HasUses()) { + // We cannot rely on DCE to remove the class because the `HLoadClass` thinks it can throw. + // However, here we know that it cannot because the instanceof check was successfull, hence + // the class was already loaded. + load_class->GetBlock()->RemoveInstruction(load_class); } } } diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc index 6928b70df7..acb830e524 100644 --- a/compiler/optimizing/intrinsics.cc +++ b/compiler/optimizing/intrinsics.cc @@ -19,9 +19,9 @@ #include "art_field-inl.h" #include "art_method-inl.h" #include "class_linker.h" +#include "dex/invoke_type.h" #include "driver/compiler_driver.h" #include "driver/compiler_options.h" -#include "invoke_type.h" #include "mirror/dex_cache-inl.h" #include "nodes.h" #include "scoped_thread_state_change-inl.h" diff --git a/compiler/optimizing/load_store_elimination.cc b/compiler/optimizing/load_store_elimination.cc index aae94b227c..88326d321b 100644 --- a/compiler/optimizing/load_store_elimination.cc +++ b/compiler/optimizing/load_store_elimination.cc @@ -25,51 +25,6 @@ #include <iostream> -/** - * The general algorithm of load-store elimination (LSE). - * Load-store analysis in the previous pass collects a list of heap locations - * and does alias analysis of those heap locations. - * LSE keeps track of a list of heap values corresponding to the heap - * locations. It visits basic blocks in reverse post order and for - * each basic block, visits instructions sequentially, and processes - * instructions as follows: - * - If the instruction is a load, and the heap location for that load has a - * valid heap value, the load can be eliminated. In order to maintain the - * validity of all heap locations during the optimization phase, the real - * elimination is delayed till the end of LSE. - * - If the instruction is a store, it updates the heap value for the heap - * location of the store with the store instruction. The real heap value - * can be fetched from the store instruction. Heap values are invalidated - * for heap locations that may alias with the store instruction's heap - * location. The store instruction can be eliminated unless the value stored - * is later needed e.g. by a load from the same/aliased heap location or - * the heap location persists at method return/deoptimization. - * The store instruction is also needed if it's not used to track the heap - * value anymore, e.g. when it fails to merge with the heap values from other - * predecessors. - * - A store that stores the same value as the heap value is eliminated. - * - The list of heap values are merged at basic block entry from the basic - * block's predecessors. The algorithm is single-pass, so loop side-effects is - * used as best effort to decide if a heap location is stored inside the loop. - * - A special type of objects called singletons are instantiated in the method - * and have a single name, i.e. no aliases. Singletons have exclusive heap - * locations since they have no aliases. Singletons are helpful in narrowing - * down the life span of a heap location such that they do not always - * need to participate in merging heap values. Allocation of a singleton - * can be eliminated if that singleton is not used and does not persist - * at method return/deoptimization. - * - For newly instantiated instances, their heap values are initialized to - * language defined default values. - * - Some instructions such as invokes are treated as loading and invalidating - * all the heap values, depending on the instruction's side effects. - * - Finalizable objects are considered as persisting at method - * return/deoptimization. - * - Currently this LSE algorithm doesn't handle SIMD graph, e.g. with VecLoad - * and VecStore instructions. - * - Currently this LSE algorithm doesn't handle graph with try-catch, due to - * the special block merging structure. - */ - namespace art { // An unknown heap value. Loads with such a value in the heap location cannot be eliminated. @@ -104,7 +59,8 @@ class LSEVisitor : public HGraphDelegateVisitor { removed_loads_(allocator_.Adapter(kArenaAllocLSE)), substitute_instructions_for_loads_(allocator_.Adapter(kArenaAllocLSE)), possibly_removed_stores_(allocator_.Adapter(kArenaAllocLSE)), - singleton_new_instances_(allocator_.Adapter(kArenaAllocLSE)) { + singleton_new_instances_(allocator_.Adapter(kArenaAllocLSE)), + singleton_new_arrays_(allocator_.Adapter(kArenaAllocLSE)) { } void VisitBasicBlock(HBasicBlock* block) OVERRIDE { @@ -132,26 +88,19 @@ class LSEVisitor : public HGraphDelegateVisitor { return type_conversion; } - // Find an instruction's substitute if it's a removed load. + // Find an instruction's substitute if it should be removed. // Return the same instruction if it should not be removed. HInstruction* FindSubstitute(HInstruction* instruction) { - if (!IsLoad(instruction)) { - return instruction; - } size_t size = removed_loads_.size(); for (size_t i = 0; i < size; i++) { if (removed_loads_[i] == instruction) { - HInstruction* substitute = substitute_instructions_for_loads_[i]; - // The substitute list is a flat hierarchy. - DCHECK_EQ(FindSubstitute(substitute), substitute); - return substitute; + return substitute_instructions_for_loads_[i]; } } return instruction; } void AddRemovedLoad(HInstruction* load, HInstruction* heap_value) { - DCHECK(IsLoad(load)); DCHECK_EQ(FindSubstitute(heap_value), heap_value) << "Unexpected heap_value that has a substitute " << heap_value->DebugName(); removed_loads_.push_back(load); @@ -258,59 +207,28 @@ class LSEVisitor : public HGraphDelegateVisitor { new_instance->GetBlock()->RemoveInstruction(new_instance); } } - } - - private: - static bool IsLoad(HInstruction* instruction) { - if (instruction == kUnknownHeapValue || instruction == kDefaultHeapValue) { - return false; - } - // Unresolved load is not treated as a load. - return instruction->IsInstanceFieldGet() || - instruction->IsStaticFieldGet() || - instruction->IsArrayGet(); - } - - static bool IsStore(HInstruction* instruction) { - if (instruction == kUnknownHeapValue || instruction == kDefaultHeapValue) { - return false; - } - // Unresolved store is not treated as a store. - return instruction->IsInstanceFieldSet() || - instruction->IsArraySet() || - instruction->IsStaticFieldSet(); - } - - // Returns the real heap value by finding its substitute or by "peeling" - // a store instruction. - HInstruction* GetRealHeapValue(HInstruction* heap_value) { - if (IsLoad(heap_value)) { - return FindSubstitute(heap_value); - } - if (!IsStore(heap_value)) { - return heap_value; - } + for (HInstruction* new_array : singleton_new_arrays_) { + size_t removed = HConstructorFence::RemoveConstructorFences(new_array); + MaybeRecordStat(stats_, + MethodCompilationStat::kConstructorFenceRemovedLSE, + removed); - // We keep track of store instructions as the heap values which might be - // eliminated if the stores are later found not necessary. The real stored - // value needs to be fetched from the store instruction. - if (heap_value->IsInstanceFieldSet()) { - heap_value = heap_value->AsInstanceFieldSet()->GetValue(); - } else if (heap_value->IsStaticFieldSet()) { - heap_value = heap_value->AsStaticFieldSet()->GetValue(); - } else { - DCHECK(heap_value->IsArraySet()); - heap_value = heap_value->AsArraySet()->GetValue(); + if (!new_array->HasNonEnvironmentUses()) { + new_array->RemoveEnvironmentUsers(); + new_array->GetBlock()->RemoveInstruction(new_array); + } } - // heap_value may already be a removed load. - return FindSubstitute(heap_value); } - // If heap_value is a store, need to keep the store. - // This is necessary if a heap value is killed or replaced by another value, - // so that the store is no longer used to track heap value. + private: + // If heap_values[index] is an instance field store, need to keep the store. + // This is necessary if a heap value is killed due to merging, or loop side + // effects (which is essentially merging also), since a load later from the + // location won't be eliminated. void KeepIfIsStore(HInstruction* heap_value) { - if (!IsStore(heap_value)) { + if (heap_value == kDefaultHeapValue || + heap_value == kUnknownHeapValue || + !(heap_value->IsInstanceFieldSet() || heap_value->IsArraySet())) { return; } auto idx = std::find(possibly_removed_stores_.begin(), @@ -321,41 +239,26 @@ class LSEVisitor : public HGraphDelegateVisitor { } } - // If a heap location X may alias with heap location at `loc_index` - // and heap_values of that heap location X holds a store, keep that store. - // It's needed for a dependent load that's not eliminated since any store - // that may put value into the load's heap location needs to be kept. - void KeepStoresIfAliasedToLocation(ScopedArenaVector<HInstruction*>& heap_values, - size_t loc_index) { - for (size_t i = 0; i < heap_values.size(); i++) { - if ((i == loc_index) || heap_location_collector_.MayAlias(i, loc_index)) { - KeepIfIsStore(heap_values[i]); - } - } - } - void HandleLoopSideEffects(HBasicBlock* block) { DCHECK(block->IsLoopHeader()); int block_id = block->GetBlockId(); ScopedArenaVector<HInstruction*>& heap_values = heap_values_for_[block_id]; - HBasicBlock* pre_header = block->GetLoopInformation()->GetPreHeader(); - ScopedArenaVector<HInstruction*>& pre_header_heap_values = - heap_values_for_[pre_header->GetBlockId()]; - // Don't eliminate loads in irreducible loops. - // Also keep the stores before the loop. + // Don't eliminate loads in irreducible loops. This is safe for singletons, because + // they are always used by the non-eliminated loop-phi. if (block->GetLoopInformation()->IsIrreducible()) { if (kIsDebugBuild) { for (size_t i = 0; i < heap_values.size(); i++) { DCHECK_EQ(heap_values[i], kUnknownHeapValue); } } - for (size_t i = 0; i < heap_values.size(); i++) { - KeepIfIsStore(pre_header_heap_values[i]); - } return; } + HBasicBlock* pre_header = block->GetLoopInformation()->GetPreHeader(); + ScopedArenaVector<HInstruction*>& pre_header_heap_values = + heap_values_for_[pre_header->GetBlockId()]; + // Inherit the values from pre-header. for (size_t i = 0; i < heap_values.size(); i++) { heap_values[i] = pre_header_heap_values[i]; @@ -367,17 +270,18 @@ class LSEVisitor : public HGraphDelegateVisitor { for (size_t i = 0; i < heap_values.size(); i++) { HeapLocation* location = heap_location_collector_.GetHeapLocation(i); ReferenceInfo* ref_info = location->GetReferenceInfo(); - if (ref_info->IsSingleton() && !location->IsValueKilledByLoopSideEffects()) { - // A singleton's field that's not stored into inside a loop is + if (ref_info->IsSingletonAndRemovable() && + !location->IsValueKilledByLoopSideEffects()) { + // A removable singleton's field that's not stored into inside a loop is // invariant throughout the loop. Nothing to do. } else { - // heap value is killed by loop side effects. + // heap value is killed by loop side effects (stored into directly, or + // due to aliasing). Or the heap value may be needed after method return + // or deoptimization. KeepIfIsStore(pre_header_heap_values[i]); heap_values[i] = kUnknownHeapValue; } } - } else { - // The loop doesn't kill any value. } } @@ -396,73 +300,45 @@ class LSEVisitor : public HGraphDelegateVisitor { ScopedArenaVector<HInstruction*>& heap_values = heap_values_for_[block->GetBlockId()]; for (size_t i = 0; i < heap_values.size(); i++) { HInstruction* merged_value = nullptr; - // If we can merge the store itself from the predecessors, we keep - // the store as the heap value as long as possible. In case we cannot - // merge the store, we try to merge the values of the stores. - HInstruction* merged_store_value = nullptr; // Whether merged_value is a result that's merged from all predecessors. bool from_all_predecessors = true; ReferenceInfo* ref_info = heap_location_collector_.GetHeapLocation(i)->GetReferenceInfo(); - HInstruction* ref = ref_info->GetReference(); HInstruction* singleton_ref = nullptr; if (ref_info->IsSingleton()) { - // We do more analysis based on singleton's liveness when merging - // heap values for such cases. - singleton_ref = ref; + // We do more analysis of liveness when merging heap values for such + // cases since stores into such references may potentially be eliminated. + singleton_ref = ref_info->GetReference(); } for (HBasicBlock* predecessor : predecessors) { HInstruction* pred_value = heap_values_for_[predecessor->GetBlockId()][i]; - if (!IsStore(pred_value)) { - pred_value = FindSubstitute(pred_value); - } - DCHECK(pred_value != nullptr); - HInstruction* pred_store_value = GetRealHeapValue(pred_value); if ((singleton_ref != nullptr) && !singleton_ref->GetBlock()->Dominates(predecessor)) { - // singleton_ref is not live in this predecessor. No need to merge - // since singleton_ref is not live at the beginning of this block. + // singleton_ref is not live in this predecessor. Skip this predecessor since + // it does not really have the location. DCHECK_EQ(pred_value, kUnknownHeapValue); from_all_predecessors = false; - break; + continue; } if (merged_value == nullptr) { // First seen heap value. - DCHECK(pred_value != nullptr); merged_value = pred_value; } else if (pred_value != merged_value) { // There are conflicting values. merged_value = kUnknownHeapValue; - // We may still be able to merge store values. - } - - // Conflicting stores may be storing the same value. We do another merge - // of real stored values. - if (merged_store_value == nullptr) { - // First seen store value. - DCHECK(pred_store_value != nullptr); - merged_store_value = pred_store_value; - } else if (pred_store_value != merged_store_value) { - // There are conflicting store values. - merged_store_value = kUnknownHeapValue; - // There must be conflicting stores also. - DCHECK_EQ(merged_value, kUnknownHeapValue); - // No need to merge anymore. break; } } - if (merged_value == nullptr) { - DCHECK(!from_all_predecessors); - DCHECK(singleton_ref != nullptr); - } - if (from_all_predecessors) { - if (ref_info->IsSingletonAndRemovable() && - block->IsSingleReturnOrReturnVoidAllowingPhis()) { - // Values in the singleton are not needed anymore. - } else if (!IsStore(merged_value)) { - // We don't track merged value as a store anymore. We have to - // hold the stores in predecessors live here. + if (ref_info->IsSingleton()) { + if (ref_info->IsSingletonAndNonRemovable() || + (merged_value == kUnknownHeapValue && + !block->IsSingleReturnOrReturnVoidAllowingPhis())) { + // The heap value may be needed after method return or deoptimization, + // or there are conflicting heap values from different predecessors and + // this block is not a single return, + // keep the last store in each predecessor since future loads may not + // be eliminated. for (HBasicBlock* predecessor : predecessors) { ScopedArenaVector<HInstruction*>& pred_values = heap_values_for_[predecessor->GetBlockId()]; @@ -470,33 +346,18 @@ class LSEVisitor : public HGraphDelegateVisitor { } } } else { - DCHECK(singleton_ref != nullptr); - // singleton_ref is non-existing at the beginning of the block. There is - // no need to keep the stores. + // Currenctly we don't eliminate stores to non-singletons. } - if (!from_all_predecessors) { + if ((merged_value == nullptr) || !from_all_predecessors) { DCHECK(singleton_ref != nullptr); DCHECK((singleton_ref->GetBlock() == block) || - !singleton_ref->GetBlock()->Dominates(block)) - << "method: " << GetGraph()->GetMethodName(); + !singleton_ref->GetBlock()->Dominates(block)); // singleton_ref is not defined before block or defined only in some of its // predecessors, so block doesn't really have the location at its entry. heap_values[i] = kUnknownHeapValue; - } else if (predecessors.size() == 1) { - // Inherit heap value from the single predecessor. - DCHECK_EQ(heap_values_for_[predecessors[0]->GetBlockId()][i], merged_value); - heap_values[i] = merged_value; } else { - DCHECK(merged_value == kUnknownHeapValue || - merged_value == kDefaultHeapValue || - merged_value->GetBlock()->Dominates(block)); - if (merged_value != kUnknownHeapValue) { - heap_values[i] = merged_value; - } else { - // Stores in different predecessors may be storing the same value. - heap_values[i] = merged_store_value; - } + heap_values[i] = merged_value; } } } @@ -562,12 +423,23 @@ class LSEVisitor : public HGraphDelegateVisitor { heap_values[idx] = constant; return; } - heap_value = GetRealHeapValue(heap_value); + if (heap_value != kUnknownHeapValue) { + if (heap_value->IsInstanceFieldSet() || heap_value->IsArraySet()) { + HInstruction* store = heap_value; + // This load must be from a singleton since it's from the same + // field/element that a "removed" store puts the value. That store + // must be to a singleton's field/element. + DCHECK(ref_info->IsSingleton()); + // Get the real heap value of the store. + heap_value = heap_value->IsInstanceFieldSet() ? store->InputAt(1) : store->InputAt(2); + // heap_value may already have a substitute. + heap_value = FindSubstitute(heap_value); + } + } if (heap_value == kUnknownHeapValue) { // Load isn't eliminated. Put the load as the value into the HeapLocation. // This acts like GVN but with better aliasing analysis. heap_values[idx] = instruction; - KeepStoresIfAliasedToLocation(heap_values, idx); } else { if (DataType::Kind(heap_value->GetType()) != DataType::Kind(instruction->GetType())) { // The only situation where the same heap location has different type is when @@ -580,10 +452,6 @@ class LSEVisitor : public HGraphDelegateVisitor { DCHECK(heap_value->IsArrayGet()) << heap_value->DebugName(); DCHECK(instruction->IsArrayGet()) << instruction->DebugName(); } - // Load isn't eliminated. Put the load as the value into the HeapLocation. - // This acts like GVN but with better aliasing analysis. - heap_values[idx] = instruction; - KeepStoresIfAliasedToLocation(heap_values, idx); return; } AddRemovedLoad(instruction, heap_value); @@ -592,21 +460,12 @@ class LSEVisitor : public HGraphDelegateVisitor { } bool Equal(HInstruction* heap_value, HInstruction* value) { - DCHECK(!IsStore(value)) << value->DebugName(); - if (heap_value == kUnknownHeapValue) { - // Don't compare kUnknownHeapValue with other values. - return false; - } if (heap_value == value) { return true; } if (heap_value == kDefaultHeapValue && GetDefaultValue(value->GetType()) == value) { return true; } - HInstruction* real_heap_value = GetRealHeapValue(heap_value); - if (real_heap_value != heap_value) { - return Equal(real_heap_value, value); - } return false; } @@ -617,7 +476,6 @@ class LSEVisitor : public HGraphDelegateVisitor { size_t vector_length, int16_t declaring_class_def_index, HInstruction* value) { - DCHECK(!IsStore(value)) << value->DebugName(); // value may already have a substitute. value = FindSubstitute(value); HInstruction* original_ref = heap_location_collector_.HuntForOriginalReference(ref); @@ -628,47 +486,59 @@ class LSEVisitor : public HGraphDelegateVisitor { ScopedArenaVector<HInstruction*>& heap_values = heap_values_for_[instruction->GetBlock()->GetBlockId()]; HInstruction* heap_value = heap_values[idx]; + bool same_value = false; bool possibly_redundant = false; - if (Equal(heap_value, value)) { // Store into the heap location with the same value. - // This store can be eliminated right away. - instruction->GetBlock()->RemoveInstruction(instruction); - return; - } else { + same_value = true; + } else if (index != nullptr && + heap_location_collector_.GetHeapLocation(idx)->HasAliasedLocations()) { + // For array element, don't eliminate stores if the location can be aliased + // (due to either ref or index aliasing). + } else if (ref_info->IsSingleton()) { + // Store into a field/element of a singleton. The value cannot be killed due to + // aliasing/invocation. It can be redundant since future loads can + // directly get the value set by this instruction. The value can still be killed due to + // merging or loop side effects. Stores whose values are killed due to merging/loop side + // effects later will be removed from possibly_removed_stores_ when that is detected. + // Stores whose values may be needed after method return or deoptimization + // are also removed from possibly_removed_stores_ when that is detected. + possibly_redundant = true; HLoopInformation* loop_info = instruction->GetBlock()->GetLoopInformation(); - if (loop_info == nullptr) { - // Store is not in a loop. We try to precisely track the heap value by - // the store. - possibly_redundant = true; - } else if (!loop_info->IsIrreducible()) { - // instruction is a store in the loop so the loop must do write. + if (loop_info != nullptr) { + // instruction is a store in the loop so the loop must does write. DCHECK(side_effects_.GetLoopEffects(loop_info->GetHeader()).DoesAnyWrite()); - if (ref_info->IsSingleton() && !loop_info->IsDefinedOutOfTheLoop(original_ref)) { - // original_ref is created inside the loop. Value stored to it isn't needed at - // the loop header. This is true for outer loops also. - possibly_redundant = true; - } else { + + if (loop_info->IsDefinedOutOfTheLoop(original_ref)) { + DCHECK(original_ref->GetBlock()->Dominates(loop_info->GetPreHeader())); // Keep the store since its value may be needed at the loop header. + possibly_redundant = false; + } else { + // The singleton is created inside the loop. Value stored to it isn't needed at + // the loop header. This is true for outer loops also. } - } else { - // Keep the store inside irreducible loops. } } - if (possibly_redundant) { + if (same_value || possibly_redundant) { possibly_removed_stores_.push_back(instruction); } - // Put the store as the heap value. If the value is loaded or needed after - // return/deoptimization later, this store isn't really redundant. - heap_values[idx] = instruction; - + if (!same_value) { + if (possibly_redundant) { + DCHECK(instruction->IsInstanceFieldSet() || instruction->IsArraySet()); + // Put the store as the heap value. If the value is loaded from heap + // by a load later, this store isn't really redundant. + heap_values[idx] = instruction; + } else { + heap_values[idx] = value; + } + } // This store may kill values in other heap locations due to aliasing. for (size_t i = 0; i < heap_values.size(); i++) { if (i == idx) { continue; } - if (Equal(heap_values[i], value)) { + if (heap_values[i] == value) { // Same value should be kept even if aliasing happens. continue; } @@ -677,9 +547,7 @@ class LSEVisitor : public HGraphDelegateVisitor { continue; } if (heap_location_collector_.MayAlias(i, idx)) { - // Kill heap locations that may alias and as a result if the heap value - // is a store, the store needs to be kept. - KeepIfIsStore(heap_values[i]); + // Kill heap locations that may alias. heap_values[i] = kUnknownHeapValue; } } @@ -765,35 +633,24 @@ class LSEVisitor : public HGraphDelegateVisitor { const ScopedArenaVector<HInstruction*>& heap_values = heap_values_for_[instruction->GetBlock()->GetBlockId()]; for (HInstruction* heap_value : heap_values) { + // Filter out fake instructions before checking instruction kind below. + if (heap_value == kUnknownHeapValue || heap_value == kDefaultHeapValue) { + continue; + } // A store is kept as the heap value for possibly removed stores. - // That value stored is generally observeable after deoptimization, except - // for singletons that don't escape after deoptimization. - if (IsStore(heap_value)) { - if (heap_value->IsStaticFieldSet()) { - KeepIfIsStore(heap_value); - continue; - } + if (heap_value->IsInstanceFieldSet() || heap_value->IsArraySet()) { + // Check whether the reference for a store is used by an environment local of + // HDeoptimize. HInstruction* reference = heap_value->InputAt(0); - if (heap_location_collector_.FindReferenceInfoOf(reference)->IsSingleton()) { - if (reference->IsNewInstance() && reference->AsNewInstance()->IsFinalizable()) { - // Finalizable objects alway escape. + DCHECK(heap_location_collector_.FindReferenceInfoOf(reference)->IsSingleton()); + for (const HUseListNode<HEnvironment*>& use : reference->GetEnvUses()) { + HEnvironment* user = use.GetUser(); + if (user->GetHolder() == instruction) { + // The singleton for the store is visible at this deoptimization + // point. Need to keep the store so that the heap value is + // seen by the interpreter. KeepIfIsStore(heap_value); - continue; } - // Check whether the reference for a store is used by an environment local of - // HDeoptimize. If not, the singleton is not observed after - // deoptimizion. - for (const HUseListNode<HEnvironment*>& use : reference->GetEnvUses()) { - HEnvironment* user = use.GetUser(); - if (user->GetHolder() == instruction) { - // The singleton for the store is visible at this deoptimization - // point. Need to keep the store so that the heap value is - // seen by the interpreter. - KeepIfIsStore(heap_value); - } - } - } else { - KeepIfIsStore(heap_value); } } } @@ -901,7 +758,7 @@ class LSEVisitor : public HGraphDelegateVisitor { return; } if (ref_info->IsSingletonAndRemovable()) { - singleton_new_instances_.push_back(new_array); + singleton_new_arrays_.push_back(new_array); } ScopedArenaVector<HInstruction*>& heap_values = heap_values_for_[new_array->GetBlock()->GetBlockId()]; @@ -934,6 +791,7 @@ class LSEVisitor : public HGraphDelegateVisitor { ScopedArenaVector<HInstruction*> possibly_removed_stores_; ScopedArenaVector<HInstruction*> singleton_new_instances_; + ScopedArenaVector<HInstruction*> singleton_new_arrays_; DISALLOW_COPY_AND_ASSIGN(LSEVisitor); }; diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index 5587f87dae..91e475d737 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -3105,8 +3105,6 @@ std::ostream& operator<<(std::ostream& os, TypeCheckKind rhs) { return os << "array_object_check"; case TypeCheckKind::kArrayCheck: return os << "array_check"; - case TypeCheckKind::kBitstringCheck: - return os << "bitstring_check"; default: LOG(FATAL) << "Unknown TypeCheckKind: " << static_cast<int>(rhs); UNREACHABLE(); diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index b0657d6f1c..43ca2cf874 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -32,11 +32,11 @@ #include "deoptimization_kind.h" #include "dex/dex_file.h" #include "dex/dex_file_types.h" +#include "dex/invoke_type.h" #include "entrypoints/quick/quick_entrypoints_enum.h" #include "handle.h" #include "handle_scope.h" #include "intrinsics_enum.h" -#include "invoke_type.h" #include "locations.h" #include "method_reference.h" #include "mirror/class.h" @@ -5951,7 +5951,8 @@ class HLoadClass FINAL : public HInstruction { special_input_(HUserRecord<HInstruction*>(current_method)), type_index_(type_index), dex_file_(dex_file), - klass_(klass) { + klass_(klass), + loaded_class_rti_(ReferenceTypeInfo::CreateInvalid()) { // Referrers class should not need access check. We never inline unverified // methods so we can't possibly end up in this situation. DCHECK(!is_referrers_class || !needs_access_check); @@ -5961,7 +5962,6 @@ class HLoadClass FINAL : public HInstruction { SetPackedFlag<kFlagNeedsAccessCheck>(needs_access_check); SetPackedFlag<kFlagIsInBootImage>(false); SetPackedFlag<kFlagGenerateClInitCheck>(false); - SetPackedFlag<kFlagValidLoadedClassRTI>(false); } bool IsClonable() const OVERRIDE { return true; } @@ -6010,18 +6010,13 @@ class HLoadClass FINAL : public HInstruction { } ReferenceTypeInfo GetLoadedClassRTI() { - if (GetPackedFlag<kFlagValidLoadedClassRTI>()) { - // Note: The is_exact flag from the return value should not be used. - return ReferenceTypeInfo::CreateUnchecked(klass_, /* is_exact */ true); - } else { - return ReferenceTypeInfo::CreateInvalid(); - } + return loaded_class_rti_; } - // Loaded class RTI is marked as valid by RTP if the klass_ is admissible. - void SetValidLoadedClassRTI() REQUIRES_SHARED(Locks::mutator_lock_) { - DCHECK(klass_ != nullptr); - SetPackedFlag<kFlagValidLoadedClassRTI>(true); + void SetLoadedClassRTI(ReferenceTypeInfo rti) { + // Make sure we only set exact types (the loaded class should never be merged). + DCHECK(rti.IsExact()); + loaded_class_rti_ = rti; } dex::TypeIndex GetTypeIndex() const { return type_index_; } @@ -6074,8 +6069,7 @@ class HLoadClass FINAL : public HInstruction { static constexpr size_t kFieldLoadKind = kFlagGenerateClInitCheck + 1; static constexpr size_t kFieldLoadKindSize = MinimumBitsToStore(static_cast<size_t>(LoadKind::kLast)); - static constexpr size_t kFlagValidLoadedClassRTI = kFieldLoadKind + kFieldLoadKindSize; - static constexpr size_t kNumberOfLoadClassPackedBits = kFlagValidLoadedClassRTI + 1; + static constexpr size_t kNumberOfLoadClassPackedBits = kFieldLoadKind + kFieldLoadKindSize; static_assert(kNumberOfLoadClassPackedBits < kMaxNumberOfPackedBits, "Too many packed fields."); using LoadKindField = BitField<LoadKind, kFieldLoadKind, kFieldLoadKindSize>; @@ -6103,6 +6097,8 @@ class HLoadClass FINAL : public HInstruction { const DexFile& dex_file_; Handle<mirror::Class> klass_; + + ReferenceTypeInfo loaded_class_rti_; }; std::ostream& operator<<(std::ostream& os, HLoadClass::LoadKind rhs); @@ -6630,143 +6626,49 @@ enum class TypeCheckKind { kInterfaceCheck, // No optimization yet when checking against an interface. kArrayObjectCheck, // Can just check if the array is not primitive. kArrayCheck, // No optimization yet when checking against a generic array. - kBitstringCheck, // Compare the type check bitstring. kLast = kArrayCheck }; std::ostream& operator<<(std::ostream& os, TypeCheckKind rhs); -// Note: HTypeCheckInstruction is just a helper class, not an abstract instruction with an -// `IsTypeCheckInstruction()`. (New virtual methods in the HInstruction class have a high cost.) -class HTypeCheckInstruction : public HVariableInputSizeInstruction { +class HInstanceOf FINAL : public HExpression<2> { public: - HTypeCheckInstruction(HInstruction* object, - HInstruction* target_class_or_null, - TypeCheckKind check_kind, - Handle<mirror::Class> klass, - uint32_t dex_pc, - ArenaAllocator* allocator, - HIntConstant* bitstring_path_to_root, - HIntConstant* bitstring_mask, - SideEffects side_effects) - : HVariableInputSizeInstruction( - side_effects, - dex_pc, - allocator, - /* number_of_inputs */ check_kind == TypeCheckKind::kBitstringCheck ? 4u : 2u, - kArenaAllocTypeCheckInputs), - klass_(klass) { + HInstanceOf(HInstruction* object, + HLoadClass* target_class, + TypeCheckKind check_kind, + uint32_t dex_pc) + : HExpression(DataType::Type::kBool, + SideEffectsForArchRuntimeCalls(check_kind), + dex_pc) { SetPackedField<TypeCheckKindField>(check_kind); SetPackedFlag<kFlagMustDoNullCheck>(true); - SetPackedFlag<kFlagValidTargetClassRTI>(false); SetRawInputAt(0, object); - SetRawInputAt(1, target_class_or_null); - DCHECK_EQ(check_kind == TypeCheckKind::kBitstringCheck, bitstring_path_to_root != nullptr); - DCHECK_EQ(check_kind == TypeCheckKind::kBitstringCheck, bitstring_mask != nullptr); - if (check_kind == TypeCheckKind::kBitstringCheck) { - DCHECK(target_class_or_null->IsNullConstant()); - SetRawInputAt(2, bitstring_path_to_root); - SetRawInputAt(3, bitstring_mask); - } else { - DCHECK(target_class_or_null->IsLoadClass()); - } + SetRawInputAt(1, target_class); } HLoadClass* GetTargetClass() const { - DCHECK_NE(GetTypeCheckKind(), TypeCheckKind::kBitstringCheck); HInstruction* load_class = InputAt(1); DCHECK(load_class->IsLoadClass()); return load_class->AsLoadClass(); } - uint32_t GetBitstringPathToRoot() const { - DCHECK_EQ(GetTypeCheckKind(), TypeCheckKind::kBitstringCheck); - HInstruction* path_to_root = InputAt(2); - DCHECK(path_to_root->IsIntConstant()); - return static_cast<uint32_t>(path_to_root->AsIntConstant()->GetValue()); - } - - uint32_t GetBitstringMask() const { - DCHECK_EQ(GetTypeCheckKind(), TypeCheckKind::kBitstringCheck); - HInstruction* mask = InputAt(3); - DCHECK(mask->IsIntConstant()); - return static_cast<uint32_t>(mask->AsIntConstant()->GetValue()); - } - bool IsClonable() const OVERRIDE { return true; } bool CanBeMoved() const OVERRIDE { return true; } - bool InstructionDataEquals(const HInstruction* other) const OVERRIDE { - DCHECK(other->IsInstanceOf() || other->IsCheckCast()) << other->DebugName(); - return GetPackedFields() == down_cast<const HTypeCheckInstruction*>(other)->GetPackedFields(); + bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { + return true; + } + + bool NeedsEnvironment() const OVERRIDE { + return CanCallRuntime(GetTypeCheckKind()); } + // Used only in code generation. bool MustDoNullCheck() const { return GetPackedFlag<kFlagMustDoNullCheck>(); } void ClearMustDoNullCheck() { SetPackedFlag<kFlagMustDoNullCheck>(false); } TypeCheckKind GetTypeCheckKind() const { return GetPackedField<TypeCheckKindField>(); } bool IsExactCheck() const { return GetTypeCheckKind() == TypeCheckKind::kExactCheck; } - ReferenceTypeInfo GetTargetClassRTI() { - if (GetPackedFlag<kFlagValidTargetClassRTI>()) { - // Note: The is_exact flag from the return value should not be used. - return ReferenceTypeInfo::CreateUnchecked(klass_, /* is_exact */ true); - } else { - return ReferenceTypeInfo::CreateInvalid(); - } - } - - // Target class RTI is marked as valid by RTP if the klass_ is admissible. - void SetValidTargetClassRTI() REQUIRES_SHARED(Locks::mutator_lock_) { - DCHECK(klass_ != nullptr); - SetPackedFlag<kFlagValidTargetClassRTI>(true); - } - - Handle<mirror::Class> GetClass() const { - return klass_; - } - - protected: - DEFAULT_COPY_CONSTRUCTOR(TypeCheckInstruction); - - private: - static constexpr size_t kFieldTypeCheckKind = kNumberOfGenericPackedBits; - static constexpr size_t kFieldTypeCheckKindSize = - MinimumBitsToStore(static_cast<size_t>(TypeCheckKind::kLast)); - static constexpr size_t kFlagMustDoNullCheck = kFieldTypeCheckKind + kFieldTypeCheckKindSize; - static constexpr size_t kFlagValidTargetClassRTI = kFlagMustDoNullCheck + 1; - static constexpr size_t kNumberOfInstanceOfPackedBits = kFlagValidTargetClassRTI + 1; - static_assert(kNumberOfInstanceOfPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); - using TypeCheckKindField = BitField<TypeCheckKind, kFieldTypeCheckKind, kFieldTypeCheckKindSize>; - - Handle<mirror::Class> klass_; -}; - -class HInstanceOf FINAL : public HTypeCheckInstruction { - public: - HInstanceOf(HInstruction* object, - HInstruction* target_class_or_null, - TypeCheckKind check_kind, - Handle<mirror::Class> klass, - uint32_t dex_pc, - ArenaAllocator* allocator, - HIntConstant* bitstring_path_to_root, - HIntConstant* bitstring_mask) - : HTypeCheckInstruction(object, - target_class_or_null, - check_kind, - klass, - dex_pc, - allocator, - bitstring_path_to_root, - bitstring_mask, - SideEffectsForArchRuntimeCalls(check_kind)) {} - - DataType::Type GetType() const OVERRIDE { return DataType::Type::kBool; } - - bool NeedsEnvironment() const OVERRIDE { - return CanCallRuntime(GetTypeCheckKind()); - } - static bool CanCallRuntime(TypeCheckKind check_kind) { // Mips currently does runtime calls for any other checks. return check_kind != TypeCheckKind::kExactCheck; @@ -6780,6 +6682,15 @@ class HInstanceOf FINAL : public HTypeCheckInstruction { protected: DEFAULT_COPY_CONSTRUCTOR(InstanceOf); + + private: + static constexpr size_t kFieldTypeCheckKind = kNumberOfExpressionPackedBits; + static constexpr size_t kFieldTypeCheckKindSize = + MinimumBitsToStore(static_cast<size_t>(TypeCheckKind::kLast)); + static constexpr size_t kFlagMustDoNullCheck = kFieldTypeCheckKind + kFieldTypeCheckKindSize; + static constexpr size_t kNumberOfInstanceOfPackedBits = kFlagMustDoNullCheck + 1; + static_assert(kNumberOfInstanceOfPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); + using TypeCheckKindField = BitField<TypeCheckKind, kFieldTypeCheckKind, kFieldTypeCheckKindSize>; }; class HBoundType FINAL : public HExpression<1> { @@ -6829,25 +6740,31 @@ class HBoundType FINAL : public HExpression<1> { ReferenceTypeInfo upper_bound_; }; -class HCheckCast FINAL : public HTypeCheckInstruction { +class HCheckCast FINAL : public HTemplateInstruction<2> { public: HCheckCast(HInstruction* object, - HInstruction* target_class_or_null, + HLoadClass* target_class, TypeCheckKind check_kind, - Handle<mirror::Class> klass, - uint32_t dex_pc, - ArenaAllocator* allocator, - HIntConstant* bitstring_path_to_root, - HIntConstant* bitstring_mask) - : HTypeCheckInstruction(object, - target_class_or_null, - check_kind, - klass, - dex_pc, - allocator, - bitstring_path_to_root, - bitstring_mask, - SideEffects::CanTriggerGC()) {} + uint32_t dex_pc) + : HTemplateInstruction(SideEffects::CanTriggerGC(), dex_pc) { + SetPackedField<TypeCheckKindField>(check_kind); + SetPackedFlag<kFlagMustDoNullCheck>(true); + SetRawInputAt(0, object); + SetRawInputAt(1, target_class); + } + + HLoadClass* GetTargetClass() const { + HInstruction* load_class = InputAt(1); + DCHECK(load_class->IsLoadClass()); + return load_class->AsLoadClass(); + } + + bool IsClonable() const OVERRIDE { return true; } + bool CanBeMoved() const OVERRIDE { return true; } + + bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { + return true; + } bool NeedsEnvironment() const OVERRIDE { // Instruction may throw a CheckCastError. @@ -6856,10 +6773,24 @@ class HCheckCast FINAL : public HTypeCheckInstruction { bool CanThrow() const OVERRIDE { return true; } + bool MustDoNullCheck() const { return GetPackedFlag<kFlagMustDoNullCheck>(); } + void ClearMustDoNullCheck() { SetPackedFlag<kFlagMustDoNullCheck>(false); } + TypeCheckKind GetTypeCheckKind() const { return GetPackedField<TypeCheckKindField>(); } + bool IsExactCheck() const { return GetTypeCheckKind() == TypeCheckKind::kExactCheck; } + DECLARE_INSTRUCTION(CheckCast); protected: DEFAULT_COPY_CONSTRUCTOR(CheckCast); + + private: + static constexpr size_t kFieldTypeCheckKind = kNumberOfGenericPackedBits; + static constexpr size_t kFieldTypeCheckKindSize = + MinimumBitsToStore(static_cast<size_t>(TypeCheckKind::kLast)); + static constexpr size_t kFlagMustDoNullCheck = kFieldTypeCheckKind + kFieldTypeCheckKindSize; + static constexpr size_t kNumberOfCheckCastPackedBits = kFlagMustDoNullCheck + 1; + static_assert(kNumberOfCheckCastPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); + using TypeCheckKindField = BitField<TypeCheckKind, kFieldTypeCheckKind, kFieldTypeCheckKindSize>; }; /** diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index c35c490118..47ef194574 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -382,7 +382,8 @@ class OptimizingCompiler FINAL : public Compiler { PassObserver* pass_observer, VariableSizedHandleScope* handles) const; - void GenerateJitDebugInfo(debug::MethodDebugInfo method_debug_info); + void GenerateJitDebugInfo(ArtMethod* method, debug::MethodDebugInfo method_debug_info) + REQUIRES_SHARED(Locks::mutator_lock_); std::unique_ptr<OptimizingCompilerStats> compilation_stats_; @@ -1248,7 +1249,7 @@ bool OptimizingCompiler::JitCompile(Thread* self, info.frame_size_in_bytes = method_header->GetFrameSizeInBytes(); info.code_info = nullptr; info.cfi = jni_compiled_method.GetCfi(); - GenerateJitDebugInfo(info); + GenerateJitDebugInfo(method, info); } Runtime::Current()->GetJit()->AddMemoryUsage(method, allocator.BytesUsed()); @@ -1372,7 +1373,7 @@ bool OptimizingCompiler::JitCompile(Thread* self, info.frame_size_in_bytes = method_header->GetFrameSizeInBytes(); info.code_info = stack_map_size == 0 ? nullptr : stack_map_data; info.cfi = ArrayRef<const uint8_t>(*codegen->GetAssembler()->cfi().data()); - GenerateJitDebugInfo(info); + GenerateJitDebugInfo(method, info); } Runtime::Current()->GetJit()->AddMemoryUsage(method, allocator.BytesUsed()); @@ -1396,7 +1397,7 @@ bool OptimizingCompiler::JitCompile(Thread* self, return true; } -void OptimizingCompiler::GenerateJitDebugInfo(debug::MethodDebugInfo info) { +void OptimizingCompiler::GenerateJitDebugInfo(ArtMethod* method, debug::MethodDebugInfo info) { const CompilerOptions& compiler_options = GetCompilerDriver()->GetCompilerOptions(); DCHECK(compiler_options.GenerateAnyDebugInfo()); @@ -1412,6 +1413,11 @@ void OptimizingCompiler::GenerateJitDebugInfo(debug::MethodDebugInfo info) { MutexLock mu(Thread::Current(), g_jit_debug_mutex); JITCodeEntry* entry = CreateJITCodeEntry(elf_file); IncrementJITCodeEntryRefcount(entry, info.code_address); + + VLOG(jit) + << "JIT mini-debug-info added for " << ArtMethod::PrettyMethod(method) + << " size=" << PrettySize(elf_file.size()) + << " total_size=" << PrettySize(GetJITCodeEntryMemUsage()); } } // namespace art diff --git a/compiler/optimizing/optimizing_compiler_stats.h b/compiler/optimizing/optimizing_compiler_stats.h index a6a2f46d2e..0023265e50 100644 --- a/compiler/optimizing/optimizing_compiler_stats.h +++ b/compiler/optimizing/optimizing_compiler_stats.h @@ -99,7 +99,6 @@ enum class MethodCompilationStat { kConstructorFenceRemovedLSE, kConstructorFenceRemovedPFRA, kConstructorFenceRemovedCFRE, - kBitstringTypeCheck, kJitOutOfMemoryForCommit, kLastStat }; diff --git a/compiler/optimizing/prepare_for_register_allocation.cc b/compiler/optimizing/prepare_for_register_allocation.cc index 59733397bf..f843c008d8 100644 --- a/compiler/optimizing/prepare_for_register_allocation.cc +++ b/compiler/optimizing/prepare_for_register_allocation.cc @@ -34,20 +34,6 @@ void PrepareForRegisterAllocation::Run() { } } -void PrepareForRegisterAllocation::VisitCheckCast(HCheckCast* check_cast) { - // Record only those bitstring type checks that make it to the codegen stage. - if (check_cast->GetTypeCheckKind() == TypeCheckKind::kBitstringCheck) { - MaybeRecordStat(stats_, MethodCompilationStat::kBitstringTypeCheck); - } -} - -void PrepareForRegisterAllocation::VisitInstanceOf(HInstanceOf* instance_of) { - // Record only those bitstring type checks that make it to the codegen stage. - if (instance_of->GetTypeCheckKind() == TypeCheckKind::kBitstringCheck) { - MaybeRecordStat(stats_, MethodCompilationStat::kBitstringTypeCheck); - } -} - void PrepareForRegisterAllocation::VisitNullCheck(HNullCheck* check) { check->ReplaceWith(check->InputAt(0)); } diff --git a/compiler/optimizing/prepare_for_register_allocation.h b/compiler/optimizing/prepare_for_register_allocation.h index f6e4d3ef99..2c64f016c1 100644 --- a/compiler/optimizing/prepare_for_register_allocation.h +++ b/compiler/optimizing/prepare_for_register_allocation.h @@ -40,8 +40,6 @@ class PrepareForRegisterAllocation : public HGraphDelegateVisitor { "prepare_for_register_allocation"; private: - void VisitCheckCast(HCheckCast* check_cast) OVERRIDE; - void VisitInstanceOf(HInstanceOf* instance_of) OVERRIDE; void VisitNullCheck(HNullCheck* check) OVERRIDE; void VisitDivZeroCheck(HDivZeroCheck* check) OVERRIDE; void VisitBoundsCheck(HBoundsCheck* check) OVERRIDE; diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc index 178d7fd0f0..8bb124e066 100644 --- a/compiler/optimizing/reference_type_propagation.cc +++ b/compiler/optimizing/reference_type_propagation.cc @@ -87,7 +87,6 @@ class ReferenceTypePropagation::RTPVisitor : public HGraphDelegateVisitor { void VisitDeoptimize(HDeoptimize* deopt) OVERRIDE; void VisitNewInstance(HNewInstance* new_instance) OVERRIDE; void VisitLoadClass(HLoadClass* load_class) OVERRIDE; - void VisitInstanceOf(HInstanceOf* load_class) OVERRIDE; void VisitClinitCheck(HClinitCheck* clinit_check) OVERRIDE; void VisitLoadString(HLoadString* instr) OVERRIDE; void VisitLoadException(HLoadException* instr) OVERRIDE; @@ -172,12 +171,6 @@ void ReferenceTypePropagation::ValidateTypes() { << "NullCheck " << instr->GetReferenceTypeInfo() << "Input(0) " << instr->InputAt(0)->GetReferenceTypeInfo(); } - } else if (instr->IsInstanceOf()) { - HInstanceOf* iof = instr->AsInstanceOf(); - DCHECK(!iof->GetTargetClassRTI().IsValid() || iof->GetTargetClassRTI().IsExact()); - } else if (instr->IsCheckCast()) { - HCheckCast* check = instr->AsCheckCast(); - DCHECK(!check->GetTargetClassRTI().IsValid() || check->GetTargetClassRTI().IsExact()); } } } @@ -506,7 +499,8 @@ void ReferenceTypePropagation::RTPVisitor::BoundTypeForIfInstanceOf(HBasicBlock* return; } - ReferenceTypeInfo class_rti = instanceOf->GetTargetClassRTI(); + HLoadClass* load_class = instanceOf->InputAt(1)->AsLoadClass(); + ReferenceTypeInfo class_rti = load_class->GetLoadedClassRTI(); if (!class_rti.IsValid()) { // He have loaded an unresolved class. Don't bother bounding the type. return; @@ -650,20 +644,15 @@ void ReferenceTypePropagation::RTPVisitor::VisitUnresolvedStaticFieldGet( void ReferenceTypePropagation::RTPVisitor::VisitLoadClass(HLoadClass* instr) { ScopedObjectAccess soa(Thread::Current()); - if (IsAdmissible(instr->GetClass().Get())) { - instr->SetValidLoadedClassRTI(); + Handle<mirror::Class> resolved_class = instr->GetClass(); + if (IsAdmissible(resolved_class.Get())) { + instr->SetLoadedClassRTI(ReferenceTypeInfo::Create( + resolved_class, /* is_exact */ true)); } instr->SetReferenceTypeInfo( ReferenceTypeInfo::Create(handle_cache_->GetClassClassHandle(), /* is_exact */ true)); } -void ReferenceTypePropagation::RTPVisitor::VisitInstanceOf(HInstanceOf* instr) { - ScopedObjectAccess soa(Thread::Current()); - if (IsAdmissible(instr->GetClass().Get())) { - instr->SetValidTargetClassRTI(); - } -} - void ReferenceTypePropagation::RTPVisitor::VisitClinitCheck(HClinitCheck* instr) { instr->SetReferenceTypeInfo(instr->InputAt(0)->GetReferenceTypeInfo()); } @@ -731,6 +720,8 @@ void ReferenceTypePropagation::RTPVisitor::VisitBoundType(HBoundType* instr) { } void ReferenceTypePropagation::RTPVisitor::VisitCheckCast(HCheckCast* check_cast) { + HLoadClass* load_class = check_cast->InputAt(1)->AsLoadClass(); + ReferenceTypeInfo class_rti = load_class->GetLoadedClassRTI(); HBoundType* bound_type = check_cast->GetNext()->AsBoundType(); if (bound_type == nullptr || bound_type->GetUpperBound().IsValid()) { // The next instruction is not an uninitialized BoundType. This must be @@ -739,14 +730,12 @@ void ReferenceTypePropagation::RTPVisitor::VisitCheckCast(HCheckCast* check_cast } DCHECK_EQ(bound_type->InputAt(0), check_cast->InputAt(0)); - ScopedObjectAccess soa(Thread::Current()); - Handle<mirror::Class> klass = check_cast->GetClass(); - if (IsAdmissible(klass.Get())) { + if (class_rti.IsValid()) { DCHECK(is_first_run_); - check_cast->SetValidTargetClassRTI(); + ScopedObjectAccess soa(Thread::Current()); // This is the first run of RTP and class is resolved. - bool is_exact = klass->CannotBeAssignedFromOtherTypes(); - bound_type->SetUpperBound(ReferenceTypeInfo::Create(klass, is_exact), + bool is_exact = class_rti.GetTypeHandle()->CannotBeAssignedFromOtherTypes(); + bound_type->SetUpperBound(ReferenceTypeInfo::Create(class_rti.GetTypeHandle(), is_exact), /* CheckCast succeeds for nulls. */ true); } else { // This is the first run of RTP and class is unresolved. Remove the binding. diff --git a/compiler/optimizing/sharpening.cc b/compiler/optimizing/sharpening.cc index dffef17587..1e49411c72 100644 --- a/compiler/optimizing/sharpening.cc +++ b/compiler/optimizing/sharpening.cc @@ -236,75 +236,6 @@ HLoadClass::LoadKind HSharpening::ComputeLoadClassKind( return load_kind; } -static inline bool CanUseTypeCheckBitstring(ObjPtr<mirror::Class> klass, - CodeGenerator* codegen, - CompilerDriver* compiler_driver) - REQUIRES_SHARED(Locks::mutator_lock_) { - DCHECK(!klass->IsProxyClass()); - DCHECK(!klass->IsArrayClass()); - - if (Runtime::Current()->UseJitCompilation()) { - // If we're JITting, try to assign a type check bitstring (fall through). - } else if (codegen->GetCompilerOptions().IsBootImage()) { - const char* descriptor = klass->GetDexFile().StringByTypeIdx(klass->GetDexTypeIndex()); - if (!compiler_driver->IsImageClass(descriptor)) { - return false; - } - // If the target is a boot image class, try to assign a type check bitstring (fall through). - // (If --force-determinism, this was already done; repeating is OK and yields the same result.) - } else { - // TODO: Use the bitstring also for AOT app compilation if the target class has a bitstring - // already assigned in the boot image. - return false; - } - - // Try to assign a type check bitstring. - MutexLock subtype_check_lock(Thread::Current(), *Locks::subtype_check_lock_); - if ((false) && // FIXME: Inliner does not respect compiler_driver->IsClassToCompile() - // and we're hitting an unassigned bitstring in dex2oat_image_test. b/26687569 - kIsDebugBuild && - codegen->GetCompilerOptions().IsBootImage() && - codegen->GetCompilerOptions().IsForceDeterminism()) { - SubtypeCheckInfo::State old_state = SubtypeCheck<ObjPtr<mirror::Class>>::GetState(klass); - CHECK(old_state == SubtypeCheckInfo::kAssigned || old_state == SubtypeCheckInfo::kOverflowed) - << klass->PrettyDescriptor() << "/" << old_state - << " in " << codegen->GetGraph()->PrettyMethod(); - } - SubtypeCheckInfo::State state = SubtypeCheck<ObjPtr<mirror::Class>>::EnsureAssigned(klass); - return state == SubtypeCheckInfo::kAssigned; -} - -TypeCheckKind HSharpening::ComputeTypeCheckKind(ObjPtr<mirror::Class> klass, - CodeGenerator* codegen, - CompilerDriver* compiler_driver, - bool needs_access_check) { - if (klass == nullptr) { - return TypeCheckKind::kUnresolvedCheck; - } else if (klass->IsInterface()) { - return TypeCheckKind::kInterfaceCheck; - } else if (klass->IsArrayClass()) { - if (klass->GetComponentType()->IsObjectClass()) { - return TypeCheckKind::kArrayObjectCheck; - } else if (klass->CannotBeAssignedFromOtherTypes()) { - return TypeCheckKind::kExactCheck; - } else { - return TypeCheckKind::kArrayCheck; - } - } else if (klass->IsFinal()) { // TODO: Consider using bitstring for final classes. - return TypeCheckKind::kExactCheck; - } else if (kUseBitstringTypeCheck && - !needs_access_check && - CanUseTypeCheckBitstring(klass, codegen, compiler_driver)) { - // TODO: We should not need the `!needs_access_check` check but getting rid of that - // requires rewriting some optimizations in instruction simplifier. - return TypeCheckKind::kBitstringCheck; - } else if (klass->IsAbstract()) { - return TypeCheckKind::kAbstractClassCheck; - } else { - return TypeCheckKind::kClassHierarchyCheck; - } -} - void HSharpening::ProcessLoadString( HLoadString* load_string, CodeGenerator* codegen, diff --git a/compiler/optimizing/sharpening.h b/compiler/optimizing/sharpening.h index fa3e948eeb..6df7d6d91e 100644 --- a/compiler/optimizing/sharpening.h +++ b/compiler/optimizing/sharpening.h @@ -44,10 +44,12 @@ class HSharpening : public HOptimization { static constexpr const char* kSharpeningPassName = "sharpening"; - // Used by Sharpening and InstructionSimplifier. - static void SharpenInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke, - CodeGenerator* codegen, - CompilerDriver* compiler_driver); + // Used by the builder. + static void ProcessLoadString(HLoadString* load_string, + CodeGenerator* codegen, + CompilerDriver* compiler_driver, + const DexCompilationUnit& dex_compilation_unit, + VariableSizedHandleScope* handles); // Used by the builder and the inliner. static HLoadClass::LoadKind ComputeLoadClassKind(HLoadClass* load_class, @@ -56,19 +58,10 @@ class HSharpening : public HOptimization { const DexCompilationUnit& dex_compilation_unit) REQUIRES_SHARED(Locks::mutator_lock_); - // Used by the builder. - static TypeCheckKind ComputeTypeCheckKind(ObjPtr<mirror::Class> klass, - CodeGenerator* codegen, - CompilerDriver* compiler_driver, - bool needs_access_check) - REQUIRES_SHARED(Locks::mutator_lock_); - - // Used by the builder. - static void ProcessLoadString(HLoadString* load_string, - CodeGenerator* codegen, - CompilerDriver* compiler_driver, - const DexCompilationUnit& dex_compilation_unit, - VariableSizedHandleScope* handles); + // Used by Sharpening and InstructionSimplifier. + static void SharpenInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke, + CodeGenerator* codegen, + CompilerDriver* compiler_driver); private: CodeGenerator* codegen_; diff --git a/compiler/utils/x86/assembler_x86.cc b/compiler/utils/x86/assembler_x86.cc index 9fcede5e97..8640e2db0e 100644 --- a/compiler/utils/x86/assembler_x86.cc +++ b/compiler/utils/x86/assembler_x86.cc @@ -2100,6 +2100,14 @@ void X86Assembler::addl(const Address& address, const Immediate& imm) { } +void X86Assembler::addw(const Address& address, const Immediate& imm) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + CHECK(imm.is_uint16() || imm.is_int16()) << imm.value(); + EmitUint8(0x66); + EmitComplex(0, address, imm, /* is_16_op */ true); +} + + void X86Assembler::adcl(Register reg, const Immediate& imm) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitComplex(2, Operand(reg), imm); @@ -2751,14 +2759,20 @@ void X86Assembler::EmitOperand(int reg_or_opcode, const Operand& operand) { } -void X86Assembler::EmitImmediate(const Immediate& imm) { - EmitInt32(imm.value()); +void X86Assembler::EmitImmediate(const Immediate& imm, bool is_16_op) { + if (is_16_op) { + EmitUint8(imm.value() & 0xFF); + EmitUint8(imm.value() >> 8); + } else { + EmitInt32(imm.value()); + } } void X86Assembler::EmitComplex(int reg_or_opcode, const Operand& operand, - const Immediate& immediate) { + const Immediate& immediate, + bool is_16_op) { CHECK_GE(reg_or_opcode, 0); CHECK_LT(reg_or_opcode, 8); if (immediate.is_int8()) { @@ -2769,11 +2783,11 @@ void X86Assembler::EmitComplex(int reg_or_opcode, } else if (operand.IsRegister(EAX)) { // Use short form if the destination is eax. EmitUint8(0x05 + (reg_or_opcode << 3)); - EmitImmediate(immediate); + EmitImmediate(immediate, is_16_op); } else { EmitUint8(0x81); EmitOperand(reg_or_opcode, operand); - EmitImmediate(immediate); + EmitImmediate(immediate, is_16_op); } } diff --git a/compiler/utils/x86/assembler_x86.h b/compiler/utils/x86/assembler_x86.h index f3b516cb7e..a085677083 100644 --- a/compiler/utils/x86/assembler_x86.h +++ b/compiler/utils/x86/assembler_x86.h @@ -634,6 +634,7 @@ class X86Assembler FINAL : public Assembler { void addl(const Address& address, Register reg); void addl(const Address& address, const Immediate& imm); + void addw(const Address& address, const Immediate& imm); void adcl(Register dst, Register src); void adcl(Register reg, const Immediate& imm); @@ -817,8 +818,9 @@ class X86Assembler FINAL : public Assembler { inline void EmitOperandSizeOverride(); void EmitOperand(int rm, const Operand& operand); - void EmitImmediate(const Immediate& imm); - void EmitComplex(int rm, const Operand& operand, const Immediate& immediate); + void EmitImmediate(const Immediate& imm, bool is_16_op = false); + void EmitComplex( + int rm, const Operand& operand, const Immediate& immediate, bool is_16_op = false); void EmitLabel(Label* label, int instruction_size); void EmitLabelLink(Label* label); void EmitLabelLink(NearLabel* label); diff --git a/compiler/utils/x86/assembler_x86_test.cc b/compiler/utils/x86/assembler_x86_test.cc index 36c5c3c0c4..937dd80c4e 100644 --- a/compiler/utils/x86/assembler_x86_test.cc +++ b/compiler/utils/x86/assembler_x86_test.cc @@ -258,6 +258,10 @@ TEST_F(AssemblerX86Test, MovlLoad) { DriverStr(RepeatRA(&x86::X86Assembler::movl, "movl {mem}, %{reg}"), "movl-load"); } +TEST_F(AssemblerX86Test, Addw) { + DriverStr(RepeatAI(&x86::X86Assembler::addw, /*imm_bytes*/ 2U, "addw ${imm}, {mem}"), "addw"); +} + TEST_F(AssemblerX86Test, MovlStore) { DriverStr(RepeatAR(&x86::X86Assembler::movl, "movl %{reg}, {mem}"), "movl-store"); } diff --git a/compiler/utils/x86_64/assembler_x86_64.cc b/compiler/utils/x86_64/assembler_x86_64.cc index 51f61ca756..feabf260af 100644 --- a/compiler/utils/x86_64/assembler_x86_64.cc +++ b/compiler/utils/x86_64/assembler_x86_64.cc @@ -2608,6 +2608,15 @@ void X86_64Assembler::addl(const Address& address, const Immediate& imm) { } +void X86_64Assembler::addw(const Address& address, const Immediate& imm) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + CHECK(imm.is_uint16() || imm.is_int16()) << imm.value(); + EmitUint8(0x66); + EmitOptionalRex32(address); + EmitComplex(0, address, imm, /* is_16_op */ true); +} + + void X86_64Assembler::subl(CpuRegister dst, CpuRegister src) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitOptionalRex32(dst, src); @@ -3387,8 +3396,11 @@ void X86_64Assembler::EmitOperand(uint8_t reg_or_opcode, const Operand& operand) } -void X86_64Assembler::EmitImmediate(const Immediate& imm) { - if (imm.is_int32()) { +void X86_64Assembler::EmitImmediate(const Immediate& imm, bool is_16_op) { + if (is_16_op) { + EmitUint8(imm.value() & 0xFF); + EmitUint8(imm.value() >> 8); + } else if (imm.is_int32()) { EmitInt32(static_cast<int32_t>(imm.value())); } else { EmitInt64(imm.value()); @@ -3398,7 +3410,8 @@ void X86_64Assembler::EmitImmediate(const Immediate& imm) { void X86_64Assembler::EmitComplex(uint8_t reg_or_opcode, const Operand& operand, - const Immediate& immediate) { + const Immediate& immediate, + bool is_16_op) { CHECK_GE(reg_or_opcode, 0); CHECK_LT(reg_or_opcode, 8); if (immediate.is_int8()) { @@ -3409,11 +3422,11 @@ void X86_64Assembler::EmitComplex(uint8_t reg_or_opcode, } else if (operand.IsRegister(CpuRegister(RAX))) { // Use short form if the destination is eax. EmitUint8(0x05 + (reg_or_opcode << 3)); - EmitImmediate(immediate); + EmitImmediate(immediate, is_16_op); } else { EmitUint8(0x81); EmitOperand(reg_or_opcode, operand); - EmitImmediate(immediate); + EmitImmediate(immediate, is_16_op); } } diff --git a/compiler/utils/x86_64/assembler_x86_64.h b/compiler/utils/x86_64/assembler_x86_64.h index 0d24a751c0..7a5fdb502f 100644 --- a/compiler/utils/x86_64/assembler_x86_64.h +++ b/compiler/utils/x86_64/assembler_x86_64.h @@ -693,6 +693,7 @@ class X86_64Assembler FINAL : public Assembler { void addl(CpuRegister reg, const Address& address); void addl(const Address& address, CpuRegister reg); void addl(const Address& address, const Immediate& imm); + void addw(const Address& address, const Immediate& imm); void addq(CpuRegister reg, const Immediate& imm); void addq(CpuRegister dst, CpuRegister src); @@ -904,8 +905,9 @@ class X86_64Assembler FINAL : public Assembler { void EmitOperandSizeOverride(); void EmitOperand(uint8_t rm, const Operand& operand); - void EmitImmediate(const Immediate& imm); - void EmitComplex(uint8_t rm, const Operand& operand, const Immediate& immediate); + void EmitImmediate(const Immediate& imm, bool is_16_op = false); + void EmitComplex( + uint8_t rm, const Operand& operand, const Immediate& immediate, bool is_16_op = false); void EmitLabel(Label* label, int instruction_size); void EmitLabelLink(Label* label); void EmitLabelLink(NearLabel* label); diff --git a/compiler/utils/x86_64/assembler_x86_64_test.cc b/compiler/utils/x86_64/assembler_x86_64_test.cc index 0cb3ffd39f..5e6c83396a 100644 --- a/compiler/utils/x86_64/assembler_x86_64_test.cc +++ b/compiler/utils/x86_64/assembler_x86_64_test.cc @@ -578,6 +578,11 @@ TEST_F(AssemblerX86_64Test, AddlImm) { "add ${imm}, %{reg}"), "addli"); } +TEST_F(AssemblerX86_64Test, Addw) { + DriverStr( + RepeatAI(&x86_64::X86_64Assembler::addw, /*imm_bytes*/2U, "addw ${imm}, {mem}"), "addw"); +} + TEST_F(AssemblerX86_64Test, ImulqReg1) { DriverStr(RepeatR(&x86_64::X86_64Assembler::imulq, "imulq %{reg}"), "imulq"); } |