diff options
Diffstat (limited to 'compiler/optimizing')
36 files changed, 1214 insertions, 593 deletions
diff --git a/compiler/optimizing/boolean_simplifier.cc b/compiler/optimizing/boolean_simplifier.cc index 06328f2490..30c89f2d15 100644 --- a/compiler/optimizing/boolean_simplifier.cc +++ b/compiler/optimizing/boolean_simplifier.cc @@ -72,8 +72,8 @@ static HInstruction* GetOppositeCondition(HInstruction* cond) { return graph->GetIntConstant(0); } } else { - // General case when 'cond' is another instruction of type boolean. - DCHECK_EQ(cond->GetType(), Primitive::Type::kPrimBoolean); + // General case when 'cond' is another instruction of type boolean, + // as verified by SSAChecker. return new (allocator) HBooleanNot(cond); } } @@ -120,8 +120,11 @@ void HBooleanSimplifier::Run() { phi->ReplaceWith(replacement); merge_block->RemovePhi(phi); - // Link the start/end blocks and remove empty branches. - graph_->MergeEmptyBranches(block, merge_block); + // Delete the true branch and merge the resulting chain of blocks + // 'block->false_block->merge_block' into one. + true_block->DisconnectAndDelete(); + block->MergeWith(false_block); + block->MergeWith(merge_block); // Remove the original condition if it is now unused. if (!if_condition->HasUses()) { diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc index 818d671b5b..ebd8243c2b 100644 --- a/compiler/optimizing/builder.cc +++ b/compiler/optimizing/builder.cc @@ -21,6 +21,7 @@ #include "class_linker.h" #include "dex_file-inl.h" #include "dex_instruction-inl.h" +#include "dex/verified_method.h" #include "driver/compiler_driver-inl.h" #include "driver/compiler_options.h" #include "mirror/class_loader.h" @@ -587,7 +588,7 @@ bool HGraphBuilder::BuildInvoke(const Instruction& instruction, const char* descriptor = dex_file_->StringDataByIdx(proto_id.shorty_idx_); Primitive::Type return_type = Primitive::GetType(descriptor[0]); bool is_instance_call = invoke_type != kStatic; - const size_t number_of_arguments = strlen(descriptor) - (is_instance_call ? 0 : 1); + size_t number_of_arguments = strlen(descriptor) - (is_instance_call ? 0 : 1); MethodReference target_method(dex_file_, method_idx); uintptr_t direct_code; @@ -605,7 +606,15 @@ bool HGraphBuilder::BuildInvoke(const Instruction& instruction, } DCHECK(optimized_invoke_type != kSuper); + // By default, consider that the called method implicitly requires + // an initialization check of its declaring method. + HInvokeStaticOrDirect::ClinitCheckRequirement clinit_check_requirement = + HInvokeStaticOrDirect::ClinitCheckRequirement::kImplicit; + // Potential class initialization check, in the case of a static method call. + HClinitCheck* clinit_check = nullptr; + HInvoke* invoke = nullptr; + if (optimized_invoke_type == kVirtual) { invoke = new (arena_) HInvokeVirtual( arena_, number_of_arguments, return_type, dex_pc, method_idx, table_index); @@ -620,9 +629,76 @@ bool HGraphBuilder::BuildInvoke(const Instruction& instruction, bool is_recursive = (target_method.dex_method_index == dex_compilation_unit_->GetDexMethodIndex()); DCHECK(!is_recursive || (target_method.dex_file == dex_compilation_unit_->GetDexFile())); + + if (optimized_invoke_type == kStatic) { + ScopedObjectAccess soa(Thread::Current()); + StackHandleScope<4> hs(soa.Self()); + Handle<mirror::DexCache> dex_cache(hs.NewHandle( + dex_compilation_unit_->GetClassLinker()->FindDexCache( + *dex_compilation_unit_->GetDexFile()))); + Handle<mirror::ClassLoader> class_loader(hs.NewHandle( + soa.Decode<mirror::ClassLoader*>(dex_compilation_unit_->GetClassLoader()))); + mirror::ArtMethod* resolved_method = compiler_driver_->ResolveMethod( + soa, dex_cache, class_loader, dex_compilation_unit_, method_idx, + optimized_invoke_type); + + if (resolved_method == nullptr) { + MaybeRecordStat(MethodCompilationStat::kNotCompiledUnresolvedMethod); + return false; + } + + const DexFile& outer_dex_file = *outer_compilation_unit_->GetDexFile(); + Handle<mirror::DexCache> outer_dex_cache(hs.NewHandle( + outer_compilation_unit_->GetClassLinker()->FindDexCache(outer_dex_file))); + Handle<mirror::Class> referrer_class(hs.NewHandle(GetOutermostCompilingClass())); + + // The index at which the method's class is stored in the DexCache's type array. + uint32_t storage_index = DexFile::kDexNoIndex; + bool is_referrer_class = (resolved_method->GetDeclaringClass() == referrer_class.Get()); + if (is_referrer_class) { + storage_index = referrer_class->GetDexTypeIndex(); + } else if (outer_dex_cache.Get() == dex_cache.Get()) { + // Get `storage_index` from IsClassOfStaticMethodAvailableToReferrer. + compiler_driver_->IsClassOfStaticMethodAvailableToReferrer(outer_dex_cache.Get(), + referrer_class.Get(), + resolved_method, + method_idx, + &storage_index); + } + + if (referrer_class.Get()->IsSubClass(resolved_method->GetDeclaringClass())) { + // If the referrer class is the declaring class or a subclass + // of the declaring class, no class initialization is needed + // before the static method call. + clinit_check_requirement = HInvokeStaticOrDirect::ClinitCheckRequirement::kNone; + } else if (storage_index != DexFile::kDexNoIndex) { + // If the method's class type index is available, check + // whether we should add an explicit class initialization + // check for its declaring class before the static method call. + + // TODO: find out why this check is needed. + bool is_in_dex_cache = compiler_driver_->CanAssumeTypeIsPresentInDexCache( + *outer_compilation_unit_->GetDexFile(), storage_index); + bool is_initialized = + resolved_method->GetDeclaringClass()->IsInitialized() && is_in_dex_cache; + + if (is_initialized) { + clinit_check_requirement = HInvokeStaticOrDirect::ClinitCheckRequirement::kNone; + } else { + clinit_check_requirement = HInvokeStaticOrDirect::ClinitCheckRequirement::kExplicit; + HLoadClass* load_class = + new (arena_) HLoadClass(storage_index, is_referrer_class, dex_pc); + current_block_->AddInstruction(load_class); + clinit_check = new (arena_) HClinitCheck(load_class, dex_pc); + current_block_->AddInstruction(clinit_check); + ++number_of_arguments; + } + } + } + invoke = new (arena_) HInvokeStaticOrDirect( arena_, number_of_arguments, return_type, dex_pc, target_method.dex_method_index, - is_recursive, invoke_type, optimized_invoke_type); + is_recursive, invoke_type, optimized_invoke_type, clinit_check_requirement); } size_t start_index = 0; @@ -655,6 +731,12 @@ bool HGraphBuilder::BuildInvoke(const Instruction& instruction, } } + if (clinit_check_requirement == HInvokeStaticOrDirect::ClinitCheckRequirement::kExplicit) { + // Add the class initialization check as last input of `invoke`. + DCHECK(clinit_check != nullptr); + invoke->SetArgumentAt(argument_index++, clinit_check); + } + DCHECK_EQ(argument_index, number_of_arguments); current_block_->AddInstruction(invoke); latest_result_ = invoke; @@ -732,7 +814,6 @@ bool HGraphBuilder::IsOutermostCompilingClass(uint16_t type_index) const { return compiling_class.Get() == cls.Get(); } - bool HGraphBuilder::BuildStaticFieldAccess(const Instruction& instruction, uint32_t dex_pc, bool is_put) { @@ -764,7 +845,7 @@ bool HGraphBuilder::BuildStaticFieldAccess(const Instruction& instruction, if (is_referrer_class) { storage_index = referrer_class->GetDexTypeIndex(); } else if (outer_dex_cache.Get() != dex_cache.Get()) { - // The compiler driver cannot currently understand multple dex caches involved. Just bailout. + // The compiler driver cannot currently understand multiple dex caches involved. Just bailout. return false; } else { std::pair<bool, bool> pair = compiler_driver_->IsFastStaticField( @@ -984,6 +1065,7 @@ void HGraphBuilder::BuildFillArrayData(const Instruction& instruction, uint32_t default: LOG(FATAL) << "Unknown element width for " << payload->element_width; } + graph_->SetHasArrayAccesses(true); } void HGraphBuilder::BuildFillWideArrayData(HInstruction* object, diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index b14b69ba39..5163395cac 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -612,7 +612,7 @@ void CodeGenerator::BuildVMapTable(std::vector<uint8_t>* data) const { } void CodeGenerator::BuildStackMaps(std::vector<uint8_t>* data) { - uint32_t size = stack_map_stream_.ComputeNeededSize(); + uint32_t size = stack_map_stream_.PrepareForFillIn(); data->resize(size); MemoryRegion region(data->data(), size); stack_map_stream_.FillIn(region); @@ -654,7 +654,8 @@ void CodeGenerator::RecordPcInfo(HInstruction* instruction, if (instruction == nullptr) { // For stack overflow checks. - stack_map_stream_.AddStackMapEntry(dex_pc, pc_info.native_pc, 0, 0, 0, inlining_depth); + stack_map_stream_.BeginStackMapEntry(dex_pc, pc_info.native_pc, 0, 0, 0, inlining_depth); + stack_map_stream_.EndStackMapEntry(); return; } LocationSummary* locations = instruction->GetLocations(); @@ -672,12 +673,12 @@ void CodeGenerator::RecordPcInfo(HInstruction* instruction, } // The register mask must be a subset of callee-save registers. DCHECK_EQ(register_mask & core_callee_save_mask_, register_mask); - stack_map_stream_.AddStackMapEntry(dex_pc, - pc_info.native_pc, - register_mask, - locations->GetStackMask(), - environment_size, - inlining_depth); + stack_map_stream_.BeginStackMapEntry(dex_pc, + pc_info.native_pc, + register_mask, + locations->GetStackMask(), + environment_size, + inlining_depth); // Walk over the environment, and record the location of dex registers. for (size_t i = 0; i < environment_size; ++i) { @@ -823,6 +824,7 @@ void CodeGenerator::RecordPcInfo(HInstruction* instruction, LOG(FATAL) << "Unexpected kind " << location.GetKind(); } } + stack_map_stream_.EndStackMapEntry(); } bool CodeGenerator::CanMoveNullCheckToUser(HNullCheck* null_check) { diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index 38fa04315a..01748a9f5c 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -176,7 +176,6 @@ class LoadClassSlowPathARM : public SlowPathCodeARM { InvokeRuntimeCallingConvention calling_convention; __ LoadImmediate(calling_convention.GetRegisterAt(0), cls_->GetTypeIndex()); - arm_codegen->LoadCurrentMethod(calling_convention.GetRegisterAt(1)); int32_t entry_point_offset = do_clinit_ ? QUICK_ENTRY_POINT(pInitializeStaticStorage) : QUICK_ENTRY_POINT(pInitializeType); @@ -222,7 +221,6 @@ class LoadStringSlowPathARM : public SlowPathCodeARM { SaveLiveRegisters(codegen, locations); InvokeRuntimeCallingConvention calling_convention; - arm_codegen->LoadCurrentMethod(calling_convention.GetRegisterAt(1)); __ LoadImmediate(calling_convention.GetRegisterAt(0), instruction_->GetStringIndex()); arm_codegen->InvokeRuntime( QUICK_ENTRY_POINT(pResolveString), instruction_, instruction_->GetDexPc(), this); @@ -1243,6 +1241,14 @@ void InstructionCodeGeneratorARM::VisitReturn(HReturn* ret) { } void LocationsBuilderARM::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { + // Explicit clinit checks triggered by static invokes must have been + // pruned by art::PrepareForRegisterAllocation, but this step is not + // run in baseline. So we remove them manually here if we find them. + // TODO: Instead of this local workaround, address this properly. + if (invoke->IsStaticWithExplicitClinitCheck()) { + invoke->RemoveClinitCheckOrLoadClassAsLastInput(); + } + IntrinsicLocationsBuilderARM intrinsic(GetGraph()->GetArena(), codegen_->GetInstructionSetFeatures()); if (intrinsic.TryDispatch(invoke)) { @@ -1267,6 +1273,10 @@ static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorARM* codegen) } void InstructionCodeGeneratorARM::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { + // Explicit clinit checks triggered by static invokes must have been + // pruned by art::PrepareForRegisterAllocation. + DCHECK(!invoke->IsStaticWithExplicitClinitCheck()); + if (TryGenerateIntrinsicCode(invoke, codegen_)) { return; } @@ -3898,9 +3908,11 @@ void InstructionCodeGeneratorARM::VisitInstanceOf(HInstanceOf* instruction) { SlowPathCodeARM* slow_path = nullptr; // Return 0 if `obj` is null. - // TODO: avoid this check if we know obj is not null. - __ cmp(obj, ShifterOperand(0)); - __ b(&zero, EQ); + // avoid null check if we know obj is not null. + if (instruction->MustDoNullCheck()) { + __ cmp(obj, ShifterOperand(0)); + __ b(&zero, EQ); + } // Compare the class of `obj` with `cls`. __ LoadFromOffset(kLoadWord, out, obj, class_offset); __ cmp(out, ShifterOperand(cls)); @@ -3919,8 +3931,12 @@ void InstructionCodeGeneratorARM::VisitInstanceOf(HInstanceOf* instruction) { __ LoadImmediate(out, 1); __ b(&done); } - __ Bind(&zero); - __ LoadImmediate(out, 0); + + if (instruction->MustDoNullCheck() || instruction->IsClassFinal()) { + __ Bind(&zero); + __ LoadImmediate(out, 0); + } + if (slow_path != nullptr) { __ Bind(slow_path->GetExitLabel()); } @@ -3946,9 +3962,11 @@ void InstructionCodeGeneratorARM::VisitCheckCast(HCheckCast* instruction) { instruction, locations->InAt(1), locations->GetTemp(0), instruction->GetDexPc()); codegen_->AddSlowPath(slow_path); - // TODO: avoid this check if we know obj is not null. - __ cmp(obj, ShifterOperand(0)); - __ b(slow_path->GetExitLabel(), EQ); + // avoid null check if we know obj is not null. + if (instruction->MustDoNullCheck()) { + __ cmp(obj, ShifterOperand(0)); + __ b(slow_path->GetExitLabel(), EQ); + } // Compare the class of `obj` with `cls`. __ LoadFromOffset(kLoadWord, temp, obj, class_offset); __ cmp(temp, ShifterOperand(cls)); diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index 23ba339a98..dada4ce5bd 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -173,14 +173,13 @@ class LoadClassSlowPathARM64 : public SlowPathCodeARM64 { InvokeRuntimeCallingConvention calling_convention; __ Mov(calling_convention.GetRegisterAt(0).W(), cls_->GetTypeIndex()); - arm64_codegen->LoadCurrentMethod(calling_convention.GetRegisterAt(1).W()); int32_t entry_point_offset = do_clinit_ ? QUICK_ENTRY_POINT(pInitializeStaticStorage) : QUICK_ENTRY_POINT(pInitializeType); arm64_codegen->InvokeRuntime(entry_point_offset, at_, dex_pc_, this); if (do_clinit_) { - CheckEntrypointTypes<kQuickInitializeStaticStorage, void*, uint32_t, mirror::ArtMethod*>(); + CheckEntrypointTypes<kQuickInitializeStaticStorage, void*, uint32_t>(); } else { - CheckEntrypointTypes<kQuickInitializeType, void*, uint32_t, mirror::ArtMethod*>(); + CheckEntrypointTypes<kQuickInitializeType, void*, uint32_t>(); } // Move the class to the desired location. @@ -225,11 +224,10 @@ class LoadStringSlowPathARM64 : public SlowPathCodeARM64 { SaveLiveRegisters(codegen, locations); InvokeRuntimeCallingConvention calling_convention; - arm64_codegen->LoadCurrentMethod(calling_convention.GetRegisterAt(1).W()); __ Mov(calling_convention.GetRegisterAt(0).W(), instruction_->GetStringIndex()); arm64_codegen->InvokeRuntime( QUICK_ENTRY_POINT(pResolveString), instruction_, instruction_->GetDexPc(), this); - CheckEntrypointTypes<kQuickResolveString, void*, uint32_t, mirror::ArtMethod*>(); + CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>(); Primitive::Type type = instruction_->GetType(); arm64_codegen->MoveLocation(locations->Out(), calling_convention.GetReturnLocation(type), type); @@ -1452,8 +1450,10 @@ void InstructionCodeGeneratorARM64::VisitCheckCast(HCheckCast* instruction) { instruction, locations->InAt(1), LocationFrom(obj_cls), instruction->GetDexPc()); codegen_->AddSlowPath(slow_path); - // TODO: avoid this check if we know obj is not null. - __ Cbz(obj, slow_path->GetExitLabel()); + // Avoid null check if we know obj is not null. + if (instruction->MustDoNullCheck()) { + __ Cbz(obj, slow_path->GetExitLabel()); + } // Compare the class of `obj` with `cls`. __ Ldr(obj_cls, HeapOperand(obj, mirror::Object::ClassOffset())); __ Cmp(obj_cls, cls); @@ -1855,9 +1855,11 @@ void InstructionCodeGeneratorARM64::VisitInstanceOf(HInstanceOf* instruction) { vixl::Label done; // Return 0 if `obj` is null. - // TODO: Avoid this check if we know `obj` is not null. - __ Mov(out, 0); - __ Cbz(obj, &done); + // Avoid null check if we know `obj` is not null. + if (instruction->MustDoNullCheck()) { + __ Mov(out, 0); + __ Cbz(obj, &done); + } // Compare the class of `obj` with `cls`. __ Ldr(out, HeapOperand(obj, mirror::Object::ClassOffset())); @@ -1966,6 +1968,14 @@ void LocationsBuilderARM64::VisitInvokeVirtual(HInvokeVirtual* invoke) { } void LocationsBuilderARM64::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { + // Explicit clinit checks triggered by static invokes must have been + // pruned by art::PrepareForRegisterAllocation, but this step is not + // run in baseline. So we remove them manually here if we find them. + // TODO: Instead of this local workaround, address this properly. + if (invoke->IsStaticWithExplicitClinitCheck()) { + invoke->RemoveClinitCheckOrLoadClassAsLastInput(); + } + IntrinsicLocationsBuilderARM64 intrinsic(GetGraph()->GetArena()); if (intrinsic.TryDispatch(invoke)) { return; @@ -2016,6 +2026,10 @@ void CodeGeneratorARM64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invok } void InstructionCodeGeneratorARM64::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { + // Explicit clinit checks triggered by static invokes must have been + // pruned by art::PrepareForRegisterAllocation. + DCHECK(!invoke->IsStaticWithExplicitClinitCheck()); + if (TryGenerateIntrinsicCode(invoke, codegen_)) { return; } diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 3dcfca6a0c..f0c14f6700 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -174,7 +174,6 @@ class LoadStringSlowPathX86 : public SlowPathCodeX86 { SaveLiveRegisters(codegen, locations); InvokeRuntimeCallingConvention calling_convention; - x86_codegen->LoadCurrentMethod(calling_convention.GetRegisterAt(1)); __ movl(calling_convention.GetRegisterAt(0), Immediate(instruction_->GetStringIndex())); __ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pResolveString))); RecordPcInfo(codegen, instruction_, instruction_->GetDexPc()); @@ -208,7 +207,6 @@ class LoadClassSlowPathX86 : public SlowPathCodeX86 { InvokeRuntimeCallingConvention calling_convention; __ movl(calling_convention.GetRegisterAt(0), Immediate(cls_->GetTypeIndex())); - x86_codegen->LoadCurrentMethod(calling_convention.GetRegisterAt(1)); __ fs()->call(Address::Absolute(do_clinit_ ? QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pInitializeStaticStorage) : QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pInitializeType))); @@ -1196,6 +1194,14 @@ void InstructionCodeGeneratorX86::VisitReturn(HReturn* ret) { } void LocationsBuilderX86::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { + // Explicit clinit checks triggered by static invokes must have been + // pruned by art::PrepareForRegisterAllocation, but this step is not + // run in baseline. So we remove them manually here if we find them. + // TODO: Instead of this local workaround, address this properly. + if (invoke->IsStaticWithExplicitClinitCheck()) { + invoke->RemoveClinitCheckOrLoadClassAsLastInput(); + } + IntrinsicLocationsBuilderX86 intrinsic(codegen_); if (intrinsic.TryDispatch(invoke)) { return; @@ -1214,6 +1220,10 @@ static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorX86* codegen) } void InstructionCodeGeneratorX86::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { + // Explicit clinit checks triggered by static invokes must have been + // pruned by art::PrepareForRegisterAllocation. + DCHECK(!invoke->IsStaticWithExplicitClinitCheck()); + if (TryGenerateIntrinsicCode(invoke, codegen_)) { return; } @@ -4250,9 +4260,11 @@ void InstructionCodeGeneratorX86::VisitInstanceOf(HInstanceOf* instruction) { SlowPathCodeX86* slow_path = nullptr; // Return 0 if `obj` is null. - // TODO: avoid this check if we know obj is not null. - __ testl(obj, obj); - __ j(kEqual, &zero); + // Avoid null check if we know obj is not null. + if (instruction->MustDoNullCheck()) { + __ testl(obj, obj); + __ j(kEqual, &zero); + } __ movl(out, Address(obj, class_offset)); // Compare the class of `obj` with `cls`. if (cls.IsRegister()) { @@ -4277,8 +4289,12 @@ void InstructionCodeGeneratorX86::VisitInstanceOf(HInstanceOf* instruction) { __ movl(out, Immediate(1)); __ jmp(&done); } - __ Bind(&zero); - __ movl(out, Immediate(0)); + + if (instruction->MustDoNullCheck() || instruction->IsClassFinal()) { + __ Bind(&zero); + __ movl(out, Immediate(0)); + } + if (slow_path != nullptr) { __ Bind(slow_path->GetExitLabel()); } @@ -4303,11 +4319,13 @@ void InstructionCodeGeneratorX86::VisitCheckCast(HCheckCast* instruction) { instruction, locations->InAt(1), locations->GetTemp(0), instruction->GetDexPc()); codegen_->AddSlowPath(slow_path); - // TODO: avoid this check if we know obj is not null. - __ testl(obj, obj); - __ j(kEqual, slow_path->GetExitLabel()); - __ movl(temp, Address(obj, class_offset)); + // Avoid null check if we know obj is not null. + if (instruction->MustDoNullCheck()) { + __ testl(obj, obj); + __ j(kEqual, slow_path->GetExitLabel()); + } + __ movl(temp, Address(obj, class_offset)); // Compare the class of `obj` with `cls`. if (cls.IsRegister()) { __ cmpl(temp, cls.AsRegister<Register>()); diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index b404f8de3d..642900f461 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -197,7 +197,6 @@ class LoadClassSlowPathX86_64 : public SlowPathCodeX86_64 { InvokeRuntimeCallingConvention calling_convention; __ movl(CpuRegister(calling_convention.GetRegisterAt(0)), Immediate(cls_->GetTypeIndex())); - x64_codegen->LoadCurrentMethod(CpuRegister(calling_convention.GetRegisterAt(1))); __ gs()->call(Address::Absolute((do_clinit_ ? QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pInitializeStaticStorage) : QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pInitializeType)) , true)); @@ -244,7 +243,6 @@ class LoadStringSlowPathX86_64 : public SlowPathCodeX86_64 { SaveLiveRegisters(codegen, locations); InvokeRuntimeCallingConvention calling_convention; - x64_codegen->LoadCurrentMethod(CpuRegister(calling_convention.GetRegisterAt(1))); __ movl(CpuRegister(calling_convention.GetRegisterAt(0)), Immediate(instruction_->GetStringIndex())); __ gs()->call(Address::Absolute( @@ -1291,6 +1289,14 @@ Location InvokeDexCallingConventionVisitor::GetNextLocation(Primitive::Type type } void LocationsBuilderX86_64::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { + // Explicit clinit checks triggered by static invokes must have been + // pruned by art::PrepareForRegisterAllocation, but this step is not + // run in baseline. So we remove them manually here if we find them. + // TODO: Instead of this local workaround, address this properly. + if (invoke->IsStaticWithExplicitClinitCheck()) { + invoke->RemoveClinitCheckOrLoadClassAsLastInput(); + } + IntrinsicLocationsBuilderX86_64 intrinsic(codegen_); if (intrinsic.TryDispatch(invoke)) { return; @@ -1309,6 +1315,10 @@ static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorX86_64* codeg } void InstructionCodeGeneratorX86_64::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { + // Explicit clinit checks triggered by static invokes must have been + // pruned by art::PrepareForRegisterAllocation. + DCHECK(!invoke->IsStaticWithExplicitClinitCheck()); + if (TryGenerateIntrinsicCode(invoke, codegen_)) { return; } @@ -4181,9 +4191,11 @@ void InstructionCodeGeneratorX86_64::VisitInstanceOf(HInstanceOf* instruction) { SlowPathCodeX86_64* slow_path = nullptr; // Return 0 if `obj` is null. - // TODO: avoid this check if we know obj is not null. - __ testl(obj, obj); - __ j(kEqual, &zero); + // Avoid null check if we know obj is not null. + if (instruction->MustDoNullCheck()) { + __ testl(obj, obj); + __ j(kEqual, &zero); + } // Compare the class of `obj` with `cls`. __ movl(out, Address(obj, class_offset)); if (cls.IsRegister()) { @@ -4207,8 +4219,12 @@ void InstructionCodeGeneratorX86_64::VisitInstanceOf(HInstanceOf* instruction) { __ movl(out, Immediate(1)); __ jmp(&done); } - __ Bind(&zero); - __ movl(out, Immediate(0)); + + if (instruction->MustDoNullCheck() || instruction->IsClassFinal()) { + __ Bind(&zero); + __ movl(out, Immediate(0)); + } + if (slow_path != nullptr) { __ Bind(slow_path->GetExitLabel()); } @@ -4233,9 +4249,11 @@ void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) { instruction, locations->InAt(1), locations->GetTemp(0), instruction->GetDexPc()); codegen_->AddSlowPath(slow_path); - // TODO: avoid this check if we know obj is not null. - __ testl(obj, obj); - __ j(kEqual, slow_path->GetExitLabel()); + // Avoid null check if we know obj is not null. + if (instruction->MustDoNullCheck()) { + __ testl(obj, obj); + __ j(kEqual, slow_path->GetExitLabel()); + } // Compare the class of `obj` with `cls`. __ movl(temp, Address(obj, class_offset)); if (cls.IsRegister()) { diff --git a/compiler/optimizing/constant_folding.h b/compiler/optimizing/constant_folding.h index ac00824e33..66ff57855e 100644 --- a/compiler/optimizing/constant_folding.h +++ b/compiler/optimizing/constant_folding.h @@ -32,8 +32,8 @@ namespace art { */ class HConstantFolding : public HOptimization { public: - explicit HConstantFolding(HGraph* graph) - : HOptimization(graph, true, kConstantFoldingPassName) {} + explicit HConstantFolding(HGraph* graph, const char* name = kConstantFoldingPassName) + : HOptimization(graph, true, name) {} void Run() OVERRIDE; diff --git a/compiler/optimizing/constant_folding_test.cc b/compiler/optimizing/constant_folding_test.cc index 02ad675dc3..422223f5e0 100644 --- a/compiler/optimizing/constant_folding_test.cc +++ b/compiler/optimizing/constant_folding_test.cc @@ -572,14 +572,19 @@ TEST(ConstantFolding, IntConstantFoldingAndJumps) { }; // Expected difference after dead code elimination. - diff_t expected_dce_diff = { - { " 3: IntConstant\n", removed }, - { " 13: IntConstant\n", removed }, - { " 18: IntConstant\n", removed }, - { " 24: IntConstant\n", removed }, - { " 34: IntConstant\n", removed }, - }; - std::string expected_after_dce = Patch(expected_after_cf, expected_dce_diff); + std::string expected_after_dce = + "BasicBlock 0, succ: 1\n" + " 5: IntConstant []\n" + " 30: SuspendCheck\n" + " 32: IntConstant []\n" + " 33: IntConstant []\n" + " 35: IntConstant [28]\n" + " 31: Goto 1\n" + "BasicBlock 1, pred: 0, succ: 5\n" + " 21: SuspendCheck\n" + " 28: Return(35)\n" + "BasicBlock 5, pred: 1\n" + " 29: Exit\n"; TestCode(data, expected_before, @@ -647,13 +652,15 @@ TEST(ConstantFolding, ConstantCondition) { ASSERT_EQ(inst->AsIntConstant()->GetValue(), 1); }; - // Expected difference after dead code elimination. - diff_t expected_dce_diff = { - { " 3: IntConstant [9, 15, 22]\n", " 3: IntConstant [9, 22]\n" }, - { " 22: Phi(3, 5) [15]\n", " 22: Phi(3, 5)\n" }, - { " 15: Add(22, 3)\n", removed } - }; - std::string expected_after_dce = Patch(expected_after_cf, expected_dce_diff); + // Expected graph after dead code elimination. + std::string expected_after_dce = + "BasicBlock 0, succ: 1\n" + " 19: SuspendCheck\n" + " 20: Goto 1\n" + "BasicBlock 1, pred: 0, succ: 4\n" + " 17: ReturnVoid\n" + "BasicBlock 4, pred: 1\n" + " 18: Exit\n"; TestCode(data, expected_before, diff --git a/compiler/optimizing/dead_code_elimination.cc b/compiler/optimizing/dead_code_elimination.cc index 8045cc5027..91cd60acce 100644 --- a/compiler/optimizing/dead_code_elimination.cc +++ b/compiler/optimizing/dead_code_elimination.cc @@ -20,10 +20,78 @@ namespace art { -void HDeadCodeElimination::Run() { +static void MarkReachableBlocks(HBasicBlock* block, ArenaBitVector* visited) { + int block_id = block->GetBlockId(); + if (visited->IsBitSet(block_id)) { + return; + } + visited->SetBit(block_id); + + HInstruction* last_instruction = block->GetLastInstruction(); + if (last_instruction->IsIf()) { + HIf* if_instruction = last_instruction->AsIf(); + HInstruction* condition = if_instruction->InputAt(0); + if (!condition->IsIntConstant()) { + MarkReachableBlocks(if_instruction->IfTrueSuccessor(), visited); + MarkReachableBlocks(if_instruction->IfFalseSuccessor(), visited); + } else if (condition->AsIntConstant()->IsOne()) { + MarkReachableBlocks(if_instruction->IfTrueSuccessor(), visited); + } else { + DCHECK(condition->AsIntConstant()->IsZero()); + MarkReachableBlocks(if_instruction->IfFalseSuccessor(), visited); + } + } else { + for (size_t i = 0, e = block->GetSuccessors().Size(); i < e; ++i) { + MarkReachableBlocks(block->GetSuccessors().Get(i), visited); + } + } +} + +void HDeadCodeElimination::MaybeRecordDeadBlock(HBasicBlock* block) { + if (stats_ != nullptr) { + stats_->RecordStat(MethodCompilationStat::kRemovedDeadInstruction, + block->GetPhis().CountSize() + block->GetInstructions().CountSize()); + } +} + +void HDeadCodeElimination::RemoveDeadBlocks() { + // Classify blocks as reachable/unreachable. + ArenaAllocator* allocator = graph_->GetArena(); + ArenaBitVector live_blocks(allocator, graph_->GetBlocks().Size(), false); + MarkReachableBlocks(graph_->GetEntryBlock(), &live_blocks); + + // Remove all dead blocks. Process blocks in post-order, because removal needs + // the block's chain of dominators. + for (HPostOrderIterator it(*graph_); !it.Done(); it.Advance()) { + HBasicBlock* block = it.Current(); + if (live_blocks.IsBitSet(block->GetBlockId())) { + continue; + } + MaybeRecordDeadBlock(block); + block->DisconnectAndDelete(); + } + + // Connect successive blocks created by dead branches. Order does not matter. + for (HReversePostOrderIterator it(*graph_); !it.Done();) { + HBasicBlock* block = it.Current(); + if (block->IsEntryBlock() || block->GetSuccessors().Size() != 1u) { + it.Advance(); + continue; + } + HBasicBlock* successor = block->GetSuccessors().Get(0); + if (successor->IsExitBlock() || successor->GetPredecessors().Size() != 1u) { + it.Advance(); + continue; + } + block->MergeWith(successor); + + // Reiterate on this block in case it can be merged with its new successor. + } +} + +void HDeadCodeElimination::RemoveDeadInstructions() { // Process basic blocks in post-order in the dominator tree, so that - // a dead instruction depending on another dead instruction is - // removed. + // a dead instruction depending on another dead instruction is removed. for (HPostOrderIterator b(*graph_); !b.Done(); b.Advance()) { HBasicBlock* block = b.Current(); // Traverse this block's instructions in backward order and remove @@ -47,4 +115,9 @@ void HDeadCodeElimination::Run() { } } +void HDeadCodeElimination::Run() { + RemoveDeadBlocks(); + RemoveDeadInstructions(); +} + } // namespace art diff --git a/compiler/optimizing/dead_code_elimination.h b/compiler/optimizing/dead_code_elimination.h index cee9364c84..0bea0fc1c2 100644 --- a/compiler/optimizing/dead_code_elimination.h +++ b/compiler/optimizing/dead_code_elimination.h @@ -40,6 +40,10 @@ class HDeadCodeElimination : public HOptimization { "dead_code_elimination"; private: + void MaybeRecordDeadBlock(HBasicBlock* block); + void RemoveDeadBlocks(); + void RemoveDeadInstructions(); + DISALLOW_COPY_AND_ASSIGN(HDeadCodeElimination); }; diff --git a/compiler/optimizing/dead_code_elimination_test.cc b/compiler/optimizing/dead_code_elimination_test.cc index 98ae1ec5d3..3209d3eb18 100644 --- a/compiler/optimizing/dead_code_elimination_test.cc +++ b/compiler/optimizing/dead_code_elimination_test.cc @@ -169,20 +169,25 @@ TEST(DeadCodeElimination, AdditionsAndInconditionalJumps) { "BasicBlock 5, pred: 4\n" " 28: Exit\n"; - // Expected difference after dead code elimination. - diff_t expected_diff = { - { " 13: IntConstant [14]\n", removed }, - { " 24: IntConstant [25]\n", removed }, - { " 14: Add(19, 13) [25]\n", removed }, - // The SuspendCheck instruction following this Add instruction - // inserts the latter in an environment, thus making it "used" and - // therefore non removable. It ensues that some other Add and - // IntConstant instructions cannot be removed, as they are direct - // or indirect inputs of the initial Add instruction. - { " 19: Add(9, 18) [14]\n", " 19: Add(9, 18) []\n" }, - { " 25: Add(14, 24)\n", removed }, - }; - std::string expected_after = Patch(expected_before, expected_diff); + // The SuspendCheck instruction following this Add instruction + // inserts the latter in an environment, thus making it "used" and + // therefore non removable. It ensures that some other Add and + // IntConstant instructions cannot be removed, as they are direct + // or indirect inputs of the initial Add instruction. + std::string expected_after = + "BasicBlock 0, succ: 1\n" + " 3: IntConstant [9]\n" + " 5: IntConstant [9]\n" + " 18: IntConstant [19]\n" + " 29: SuspendCheck\n" + " 30: Goto 1\n" + "BasicBlock 1, pred: 0, succ: 5\n" + " 9: Add(3, 5) [19]\n" + " 19: Add(9, 18) []\n" + " 21: SuspendCheck\n" + " 27: ReturnVoid\n" + "BasicBlock 5, pred: 1\n" + " 28: Exit\n"; TestCode(data, expected_before, expected_after); } diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc index 8950635d6a..890676467f 100644 --- a/compiler/optimizing/graph_checker.cc +++ b/compiler/optimizing/graph_checker.cc @@ -191,6 +191,30 @@ void GraphChecker::VisitInstruction(HInstruction* instruction) { } } +void GraphChecker::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { + VisitInstruction(invoke); + + if (invoke->IsStaticWithExplicitClinitCheck()) { + size_t last_input_index = invoke->InputCount() - 1; + HInstruction* last_input = invoke->InputAt(last_input_index); + if (last_input == nullptr) { + AddError(StringPrintf("Static invoke %s:%d marked as having an explicit clinit check " + "has a null pointer as last input.", + invoke->DebugName(), + invoke->GetId())); + } + if (!last_input->IsClinitCheck() && !last_input->IsLoadClass()) { + AddError(StringPrintf("Static invoke %s:%d marked as having an explicit clinit check " + "has a last instruction (%s:%d) which is neither a clinit check " + "nor a load class instruction.", + invoke->DebugName(), + invoke->GetId(), + last_input->DebugName(), + last_input->GetId())); + } + } +} + void SSAChecker::VisitBasicBlock(HBasicBlock* block) { super_type::VisitBasicBlock(block); @@ -264,6 +288,8 @@ void SSAChecker::CheckLoop(HBasicBlock* loop_header) { } } + const ArenaBitVector& loop_blocks = loop_header->GetLoopInformation()->GetBlocks(); + // Ensure there is only one back edge per loop. size_t num_back_edges = loop_header->GetLoopInformation()->GetBackEdges().Size(); @@ -276,16 +302,27 @@ void SSAChecker::CheckLoop(HBasicBlock* loop_header) { "Loop defined by header %d has several back edges: %zu.", id, num_back_edges)); + } else { + DCHECK_EQ(num_back_edges, 1u); + int back_edge_id = loop_header->GetLoopInformation()->GetBackEdges().Get(0)->GetBlockId(); + if (!loop_blocks.IsBitSet(back_edge_id)) { + AddError(StringPrintf( + "Loop defined by header %d has an invalid back edge %d.", + id, + back_edge_id)); + } } - // Ensure all blocks in the loop are dominated by the loop header. - const ArenaBitVector& loop_blocks = - loop_header->GetLoopInformation()->GetBlocks(); + // Ensure all blocks in the loop are live and dominated by the loop header. for (uint32_t i : loop_blocks.Indexes()) { HBasicBlock* loop_block = GetGraph()->GetBlocks().Get(i); - if (!loop_header->Dominates(loop_block)) { + if (loop_block == nullptr) { + AddError(StringPrintf("Loop defined by header %d contains a previously removed block %d.", + id, + i)); + } else if (!loop_header->Dominates(loop_block)) { AddError(StringPrintf("Loop block %d not dominated by loop header %d.", - loop_block->GetBlockId(), + i, id)); } } @@ -296,7 +333,7 @@ void SSAChecker::CheckLoop(HBasicBlock* loop_header) { if (!loop_blocks.IsSubsetOf(&outer_info->GetBlocks())) { AddError(StringPrintf("Blocks of loop defined by header %d are not a subset of blocks of " "an outer loop defined by header %d.", - loop_header->GetBlockId(), + id, outer_info->GetHeader()->GetBlockId())); } } @@ -483,7 +520,7 @@ void SSAChecker::VisitBinaryOperation(HBinaryOperation* op) { Primitive::PrettyDescriptor(op->InputAt(1)->GetType()))); } } else { - if (PrimitiveKind(op->InputAt(1)->GetType()) != PrimitiveKind(op->InputAt(0)->GetType())) { + if (PrimitiveKind(op->InputAt(0)->GetType()) != PrimitiveKind(op->InputAt(1)->GetType())) { AddError(StringPrintf( "Binary operation %s %d has inputs of different types: " "%s, and %s.", @@ -508,7 +545,7 @@ void SSAChecker::VisitBinaryOperation(HBinaryOperation* op) { "from its input type: %s vs %s.", op->DebugName(), op->GetId(), Primitive::PrettyDescriptor(op->GetType()), - Primitive::PrettyDescriptor(op->InputAt(1)->GetType()))); + Primitive::PrettyDescriptor(op->InputAt(0)->GetType()))); } } } diff --git a/compiler/optimizing/graph_checker.h b/compiler/optimizing/graph_checker.h index 24fee373f9..45e8804edb 100644 --- a/compiler/optimizing/graph_checker.h +++ b/compiler/optimizing/graph_checker.h @@ -42,6 +42,9 @@ class GraphChecker : public HGraphDelegateVisitor { // Check `instruction`. void VisitInstruction(HInstruction* instruction) OVERRIDE; + // Perform control-flow graph checks on instruction. + void VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) OVERRIDE; + // Was the last visit of the graph valid? bool IsValid() const { return errors_.empty(); diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index bffd639e83..37b57533c1 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -130,6 +130,16 @@ bool HInliner::TryInline(HInvoke* invoke_instruction, return false; } + if (invoke_instruction->IsInvokeStaticOrDirect() && + invoke_instruction->AsInvokeStaticOrDirect()->IsStaticWithImplicitClinitCheck()) { + // Case of a static method that cannot be inlined because it implicitly + // requires an initialization check of its declaring class. + VLOG(compiler) << "Method " << PrettyMethod(method_index, caller_dex_file) + << " is not inlined because it is static and requires a clinit" + << " check that cannot be emitted due to Dex cache limitations"; + return false; + } + if (!TryBuildAndInline(resolved_method, invoke_instruction, method_index, can_use_dex_cache)) { resolved_method->SetShouldNotInline(); return false; diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index 98c0eedeb2..2df7c166d8 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -62,6 +62,7 @@ class InstructionSimplifierVisitor : public HGraphVisitor { void VisitSub(HSub* instruction) OVERRIDE; void VisitUShr(HUShr* instruction) OVERRIDE; void VisitXor(HXor* instruction) OVERRIDE; + void VisitInstanceOf(HInstanceOf* instruction) OVERRIDE; OptimizingCompilerStats* stats_; bool simplification_occurred_ = false; @@ -159,6 +160,10 @@ void InstructionSimplifierVisitor::VisitNullCheck(HNullCheck* null_check) { void InstructionSimplifierVisitor::VisitCheckCast(HCheckCast* check_cast) { HLoadClass* load_class = check_cast->InputAt(1)->AsLoadClass(); + if (!check_cast->InputAt(0)->CanBeNull()) { + check_cast->ClearMustDoNullCheck(); + } + if (!load_class->IsResolved()) { // If the class couldn't be resolve it's not safe to compare against it. It's // default type would be Top which might be wider that the actual class type @@ -176,6 +181,12 @@ void InstructionSimplifierVisitor::VisitCheckCast(HCheckCast* check_cast) { } } +void InstructionSimplifierVisitor::VisitInstanceOf(HInstanceOf* instruction) { + if (!instruction->InputAt(0)->CanBeNull()) { + instruction->ClearMustDoNullCheck(); + } +} + void InstructionSimplifierVisitor::VisitSuspendCheck(HSuspendCheck* check) { HBasicBlock* block = check->GetBlock(); // Currently always keep the suspend check at entry. @@ -427,9 +438,16 @@ void InstructionSimplifierVisitor::VisitMul(HMul* instruction) { if (Primitive::IsIntOrLongType(type)) { int64_t factor = Int64FromConstant(input_cst); - // We expect the `0` case to have been handled in the constant folding pass. - DCHECK_NE(factor, 0); - if (IsPowerOfTwo(factor)) { + // Even though constant propagation also takes care of the zero case, other + // optimizations can lead to having a zero multiplication. + if (factor == 0) { + // Replace code looking like + // MUL dst, src, 0 + // with + // 0 + instruction->ReplaceWith(input_cst); + instruction->GetBlock()->RemoveInstruction(instruction); + } else if (IsPowerOfTwo(factor)) { // Replace code looking like // MUL dst, src, pow_of_2 // with diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc index 932192e4fd..abdf04ebb1 100644 --- a/compiler/optimizing/intrinsics_arm.cc +++ b/compiler/optimizing/intrinsics_arm.cc @@ -79,6 +79,7 @@ static void MoveFromReturnRegister(Location trg, Primitive::Type type, CodeGener static void MoveArguments(HInvoke* invoke, ArenaAllocator* arena, CodeGeneratorARM* codegen) { if (invoke->InputCount() == 0) { + // No argument to move. return; } diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc index 117d6a4279..7a753b2da9 100644 --- a/compiler/optimizing/intrinsics_arm64.cc +++ b/compiler/optimizing/intrinsics_arm64.cc @@ -88,6 +88,7 @@ static void MoveFromReturnRegister(Location trg, static void MoveArguments(HInvoke* invoke, ArenaAllocator* arena, CodeGeneratorARM64* codegen) { if (invoke->InputCount() == 0) { + // No argument to move. return; } diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc index a8e2cdf1f6..7275edb695 100644 --- a/compiler/optimizing/intrinsics_x86.cc +++ b/compiler/optimizing/intrinsics_x86.cc @@ -113,6 +113,7 @@ static void MoveFromReturnRegister(Location target, static void MoveArguments(HInvoke* invoke, ArenaAllocator* arena, CodeGeneratorX86* codegen) { if (invoke->InputCount() == 0) { + // No argument to move. return; } @@ -1038,7 +1039,7 @@ static void CreateLongIntToVoidLocations(ArenaAllocator* arena, Primitive::Type LocationSummary::kNoCall, kIntrinsified); locations->SetInAt(0, Location::RequiresRegister()); - HInstruction *value = invoke->InputAt(1); + HInstruction* value = invoke->InputAt(1); if (size == Primitive::kPrimByte) { locations->SetInAt(1, Location::ByteRegisterOrConstant(EDX, value)); } else { diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc index 5d24d1fbfb..35daaf60bb 100644 --- a/compiler/optimizing/intrinsics_x86_64.cc +++ b/compiler/optimizing/intrinsics_x86_64.cc @@ -105,6 +105,7 @@ static void MoveFromReturnRegister(Location trg, static void MoveArguments(HInvoke* invoke, ArenaAllocator* arena, CodeGeneratorX86_64* codegen) { if (invoke->InputCount() == 0) { + // No argument to move. return; } diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index 6ab57b8e50..c158ddf4ee 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -672,6 +672,11 @@ void HPhi::AddInput(HInstruction* input) { input->AddUseAt(this, inputs_.Size() - 1); } +void HPhi::RemoveInputAt(size_t index) { + RemoveAsUserOfInput(index); + inputs_.DeleteAt(index); +} + #define DEFINE_ACCEPT(name, super) \ void H##name::Accept(HGraphVisitor* visitor) { \ visitor->Visit##name(this); \ @@ -867,6 +872,15 @@ bool HBasicBlock::HasSinglePhi() const { return !GetPhis().IsEmpty() && GetFirstPhi()->GetNext() == nullptr; } +size_t HInstructionList::CountSize() const { + size_t size = 0; + HInstruction* current = first_instruction_; + for (; current != nullptr; current = current->GetNext()) { + size++; + } + return size; +} + void HInstructionList::SetBlockOfInstructions(HBasicBlock* block) const { for (HInstruction* current = first_instruction_; current != nullptr; @@ -898,40 +912,167 @@ void HInstructionList::Add(const HInstructionList& instruction_list) { } } -void HBasicBlock::DisconnectFromAll() { - DCHECK(dominated_blocks_.IsEmpty()) << "Unimplemented scenario"; +void HBasicBlock::DisconnectAndDelete() { + // Dominators must be removed after all the blocks they dominate. This way + // a loop header is removed last, a requirement for correct loop information + // iteration. + DCHECK(dominated_blocks_.IsEmpty()); + + // Remove the block from all loops it is included in. + for (HLoopInformationOutwardIterator it(*this); !it.Done(); it.Advance()) { + HLoopInformation* loop_info = it.Current(); + loop_info->Remove(this); + if (loop_info->IsBackEdge(*this)) { + // This deliberately leaves the loop in an inconsistent state and will + // fail SSAChecker unless the entire loop is removed during the pass. + loop_info->RemoveBackEdge(this); + } + } + // Disconnect the block from its predecessors and update their control-flow + // instructions. for (size_t i = 0, e = predecessors_.Size(); i < e; ++i) { - predecessors_.Get(i)->successors_.Delete(this); + HBasicBlock* predecessor = predecessors_.Get(i); + HInstruction* last_instruction = predecessor->GetLastInstruction(); + predecessor->RemoveInstruction(last_instruction); + predecessor->RemoveSuccessor(this); + if (predecessor->GetSuccessors().Size() == 1u) { + DCHECK(last_instruction->IsIf()); + predecessor->AddInstruction(new (graph_->GetArena()) HGoto()); + } else { + // The predecessor has no remaining successors and therefore must be dead. + // We deliberately leave it without a control-flow instruction so that the + // SSAChecker fails unless it is not removed during the pass too. + DCHECK_EQ(predecessor->GetSuccessors().Size(), 0u); + } } + predecessors_.Reset(); + + // Disconnect the block from its successors and update their dominators + // and phis. for (size_t i = 0, e = successors_.Size(); i < e; ++i) { - successors_.Get(i)->predecessors_.Delete(this); - } - dominator_->dominated_blocks_.Delete(this); + HBasicBlock* successor = successors_.Get(i); + // Delete this block from the list of predecessors. + size_t this_index = successor->GetPredecessorIndexOf(this); + successor->predecessors_.DeleteAt(this_index); + + // Check that `successor` has other predecessors, otherwise `this` is the + // dominator of `successor` which violates the order DCHECKed at the top. + DCHECK(!successor->predecessors_.IsEmpty()); + + // Recompute the successor's dominator. + HBasicBlock* old_dominator = successor->GetDominator(); + HBasicBlock* new_dominator = successor->predecessors_.Get(0); + for (size_t j = 1, f = successor->predecessors_.Size(); j < f; ++j) { + new_dominator = graph_->FindCommonDominator( + new_dominator, successor->predecessors_.Get(j)); + } + if (old_dominator != new_dominator) { + successor->SetDominator(new_dominator); + old_dominator->RemoveDominatedBlock(successor); + new_dominator->AddDominatedBlock(successor); + } - predecessors_.Reset(); + // Remove this block's entries in the successor's phis. + if (successor->predecessors_.Size() == 1u) { + // The successor has just one predecessor left. Replace phis with the only + // remaining input. + for (HInstructionIterator phi_it(successor->GetPhis()); !phi_it.Done(); phi_it.Advance()) { + HPhi* phi = phi_it.Current()->AsPhi(); + phi->ReplaceWith(phi->InputAt(1 - this_index)); + successor->RemovePhi(phi); + } + } else { + for (HInstructionIterator phi_it(successor->GetPhis()); !phi_it.Done(); phi_it.Advance()) { + phi_it.Current()->AsPhi()->RemoveInputAt(this_index); + } + } + } successors_.Reset(); - dominator_ = nullptr; - graph_ = nullptr; + + // Disconnect from the dominator. + dominator_->RemoveDominatedBlock(this); + SetDominator(nullptr); + + // Delete from the graph. The function safely deletes remaining instructions + // and updates the reverse post order. + graph_->DeleteDeadBlock(this); + SetGraph(nullptr); } void HBasicBlock::MergeWith(HBasicBlock* other) { - DCHECK(successors_.IsEmpty()) << "Unimplemented block merge scenario"; - DCHECK(dominated_blocks_.IsEmpty() - || (dominated_blocks_.Size() == 1 && dominated_blocks_.Get(0) == other)) - << "Unimplemented block merge scenario"; + DCHECK_EQ(GetGraph(), other->GetGraph()); + DCHECK(GetDominatedBlocks().Contains(other)); + DCHECK_EQ(GetSuccessors().Size(), 1u); + DCHECK_EQ(GetSuccessors().Get(0), other); + DCHECK_EQ(other->GetPredecessors().Size(), 1u); + DCHECK_EQ(other->GetPredecessors().Get(0), this); DCHECK(other->GetPhis().IsEmpty()); + // Move instructions from `other` to `this`. + DCHECK(EndsWithControlFlowInstruction()); + RemoveInstruction(GetLastInstruction()); + instructions_.Add(other->GetInstructions()); + other->instructions_.SetBlockOfInstructions(this); + other->instructions_.Clear(); + + // Remove `other` from the loops it is included in. + for (HLoopInformationOutwardIterator it(*other); !it.Done(); it.Advance()) { + HLoopInformation* loop_info = it.Current(); + loop_info->Remove(other); + if (loop_info->IsBackEdge(*other)) { + loop_info->ClearBackEdges(); + loop_info->AddBackEdge(this); + } + } + + // Update links to the successors of `other`. successors_.Reset(); - dominated_blocks_.Reset(); + while (!other->successors_.IsEmpty()) { + HBasicBlock* successor = other->successors_.Get(0); + successor->ReplacePredecessor(other, this); + } + + // Update the dominator tree. + dominated_blocks_.Delete(other); + for (size_t i = 0, e = other->GetDominatedBlocks().Size(); i < e; ++i) { + HBasicBlock* dominated = other->GetDominatedBlocks().Get(i); + dominated_blocks_.Add(dominated); + dominated->SetDominator(this); + } + other->dominated_blocks_.Reset(); + other->dominator_ = nullptr; + + // Clear the list of predecessors of `other` in preparation of deleting it. + other->predecessors_.Reset(); + + // Delete `other` from the graph. The function updates reverse post order. + graph_->DeleteDeadBlock(other); + other->SetGraph(nullptr); +} + +void HBasicBlock::MergeWithInlined(HBasicBlock* other) { + DCHECK_NE(GetGraph(), other->GetGraph()); + DCHECK(GetDominatedBlocks().IsEmpty()); + DCHECK(GetSuccessors().IsEmpty()); + DCHECK(!EndsWithControlFlowInstruction()); + DCHECK_EQ(other->GetPredecessors().Size(), 1u); + DCHECK(other->GetPredecessors().Get(0)->IsEntryBlock()); + DCHECK(other->GetPhis().IsEmpty()); + DCHECK(!other->IsInLoop()); + + // Move instructions from `other` to `this`. instructions_.Add(other->GetInstructions()); - other->GetInstructions().SetBlockOfInstructions(this); + other->instructions_.SetBlockOfInstructions(this); - while (!other->GetSuccessors().IsEmpty()) { - HBasicBlock* successor = other->GetSuccessors().Get(0); + // Update links to the successors of `other`. + successors_.Reset(); + while (!other->successors_.IsEmpty()) { + HBasicBlock* successor = other->successors_.Get(0); successor->ReplacePredecessor(other, this); } + // Update the dominator tree. for (size_t i = 0, e = other->GetDominatedBlocks().Size(); i < e; ++i) { HBasicBlock* dominated = other->GetDominatedBlocks().Get(i); dominated_blocks_.Add(dominated); @@ -973,6 +1114,24 @@ static void MakeRoomFor(GrowableArray<HBasicBlock*>* blocks, } } +void HGraph::DeleteDeadBlock(HBasicBlock* block) { + DCHECK_EQ(block->GetGraph(), this); + DCHECK(block->GetSuccessors().IsEmpty()); + DCHECK(block->GetPredecessors().IsEmpty()); + DCHECK(block->GetDominatedBlocks().IsEmpty()); + DCHECK(block->GetDominator() == nullptr); + + for (HBackwardInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) { + block->RemoveInstruction(it.Current()); + } + for (HBackwardInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) { + block->RemovePhi(it.Current()->AsPhi()); + } + + reverse_post_order_.Delete(block); + blocks_.Put(block->GetBlockId(), nullptr); +} + void HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) { if (GetBlocks().Size() == 3) { // Simple case of an entry block, a body block, and an exit block. @@ -1005,7 +1164,7 @@ void HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) { HBasicBlock* first = entry_block_->GetSuccessors().Get(0); DCHECK(!first->IsInLoop()); - at->MergeWith(first); + at->MergeWithInlined(first); exit_block_->ReplaceWith(to); // Update all predecessors of the exit block (now the `to` block) @@ -1113,7 +1272,7 @@ void HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) { // - Remove suspend checks, that hold an environment. // We must do this after the other blocks have been inlined, otherwise ids of // constants could overlap with the inner graph. - int parameter_index = 0; + size_t parameter_index = 0; for (HInstructionIterator it(entry_block_->GetInstructions()); !it.Done(); it.Advance()) { HInstruction* current = it.Current(); if (current->IsNullConstant()) { @@ -1126,6 +1285,14 @@ void HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) { // TODO: Don't duplicate floating-point constants. current->MoveBefore(outer_graph->GetEntryBlock()->GetLastInstruction()); } else if (current->IsParameterValue()) { + if (kIsDebugBuild + && invoke->IsInvokeStaticOrDirect() + && invoke->AsInvokeStaticOrDirect()->IsStaticWithExplicitClinitCheck()) { + // Ensure we do not use the last input of `invoke`, as it + // contains a clinit check which is not an actual argument. + size_t last_input_index = invoke->InputCount() - 1; + DCHECK(parameter_index != last_input_index); + } current->ReplaceWith(invoke->InputAt(parameter_index++)); } else { DCHECK(current->IsGoto() || current->IsSuspendCheck()); @@ -1137,53 +1304,6 @@ void HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) { invoke->GetBlock()->RemoveInstruction(invoke); } -void HGraph::MergeEmptyBranches(HBasicBlock* start_block, HBasicBlock* end_block) { - // Find the two branches of an If. - DCHECK_EQ(start_block->GetSuccessors().Size(), 2u); - HBasicBlock* left_branch = start_block->GetSuccessors().Get(0); - HBasicBlock* right_branch = start_block->GetSuccessors().Get(1); - - // Make sure this is a diamond control-flow path. - DCHECK_EQ(left_branch->GetSuccessors().Get(0), end_block); - DCHECK_EQ(right_branch->GetSuccessors().Get(0), end_block); - DCHECK_EQ(end_block->GetPredecessors().Size(), 2u); - DCHECK_EQ(start_block, end_block->GetDominator()); - - // Disconnect the branches and merge the two blocks. This will move - // all instructions from 'end_block' to 'start_block'. - DCHECK(left_branch->IsSingleGoto()); - DCHECK(right_branch->IsSingleGoto()); - left_branch->DisconnectFromAll(); - right_branch->DisconnectFromAll(); - start_block->RemoveInstruction(start_block->GetLastInstruction()); - start_block->MergeWith(end_block); - - // Delete the now redundant blocks from the graph. - blocks_.Put(left_branch->GetBlockId(), nullptr); - blocks_.Put(right_branch->GetBlockId(), nullptr); - blocks_.Put(end_block->GetBlockId(), nullptr); - - // Update reverse post order. - reverse_post_order_.Delete(left_branch); - reverse_post_order_.Delete(right_branch); - reverse_post_order_.Delete(end_block); - - // Update loops which contain the code. - for (HLoopInformationOutwardIterator it(*start_block); !it.Done(); it.Advance()) { - HLoopInformation* loop_info = it.Current(); - DCHECK(loop_info->Contains(*left_branch)); - DCHECK(loop_info->Contains(*right_branch)); - DCHECK(loop_info->Contains(*end_block)); - loop_info->Remove(left_branch); - loop_info->Remove(right_branch); - loop_info->Remove(end_block); - if (loop_info->IsBackEdge(*end_block)) { - loop_info->RemoveBackEdge(end_block); - loop_info->AddBackEdge(start_block); - } - } -} - std::ostream& operator<<(std::ostream& os, const ReferenceTypeInfo& rhs) { ScopedObjectAccess soa(Thread::Current()); os << "[" diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 0993a18046..946091f70d 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -97,6 +97,9 @@ class HInstructionList { void AddAfter(HInstruction* cursor, const HInstructionList& instruction_list); void Add(const HInstructionList& instruction_list); + // Return the number of instructions in the list. This is an expensive operation. + size_t CountSize() const; + private: HInstruction* first_instruction_; HInstruction* last_instruction_; @@ -168,7 +171,8 @@ class HGraph : public ArenaObject<kArenaAllocMisc> { // Inline this graph in `outer_graph`, replacing the given `invoke` instruction. void InlineInto(HGraph* outer_graph, HInvoke* invoke); - void MergeEmptyBranches(HBasicBlock* start_block, HBasicBlock* end_block); + // Removes `block` from the graph. + void DeleteDeadBlock(HBasicBlock* block); void SplitCriticalEdge(HBasicBlock* block, HBasicBlock* successor); void SimplifyLoop(HBasicBlock* header); @@ -248,8 +252,9 @@ class HGraph : public ArenaObject<kArenaAllocMisc> { return CreateConstant(value, &cached_long_constants_); } - private: HBasicBlock* FindCommonDominator(HBasicBlock* first, HBasicBlock* second) const; + + private: void VisitBlockForDominatorTree(HBasicBlock* block, HBasicBlock* predecessor, GrowableArray<size_t>* visits); @@ -451,6 +456,7 @@ class HBasicBlock : public ArenaObject<kArenaAllocMisc> { HBasicBlock* GetDominator() const { return dominator_; } void SetDominator(HBasicBlock* dominator) { dominator_ = dominator; } void AddDominatedBlock(HBasicBlock* block) { dominated_blocks_.Add(block); } + void RemoveDominatedBlock(HBasicBlock* block) { dominated_blocks_.Delete(block); } void ReplaceDominatedBlock(HBasicBlock* existing, HBasicBlock* new_block) { for (size_t i = 0, e = dominated_blocks_.Size(); i < e; ++i) { if (dominated_blocks_.Get(i) == existing) { @@ -550,7 +556,7 @@ class HBasicBlock : public ArenaObject<kArenaAllocMisc> { // that this method does not update the graph, reverse post order, loop // information, nor make sure the blocks are consistent (for example ending // with a control flow instruction). - void MergeWith(HBasicBlock* other); + void MergeWithInlined(HBasicBlock* other); // Replace `this` with `other`. Predecessors, successors, and dominated blocks // of `this` are moved to `other`. @@ -559,12 +565,17 @@ class HBasicBlock : public ArenaObject<kArenaAllocMisc> { // with a control flow instruction). void ReplaceWith(HBasicBlock* other); - // Disconnects `this` from all its predecessors, successors and the dominator. - // It assumes that `this` does not dominate any blocks. - // Note that this method does not update the graph, reverse post order, loop - // information, nor make sure the blocks are consistent (for example ending - // with a control flow instruction). - void DisconnectFromAll(); + // Merge `other` at the end of `this`. This method updates loops, reverse post + // order, links to predecessors, successors, dominators and deletes the block + // from the graph. The two blocks must be successive, i.e. `this` the only + // predecessor of `other` and vice versa. + void MergeWith(HBasicBlock* other); + + // Disconnects `this` from all its predecessors, successors and dominator, + // removes it from all loops it is included in and eventually from the graph. + // The block must not dominate any other block. Predecessors and successors + // are safely updated. + void DisconnectAndDelete(); void AddInstruction(HInstruction* instruction); void InsertInstructionBefore(HInstruction* instruction, HInstruction* cursor); @@ -1154,8 +1165,6 @@ class HInstruction : public ArenaObject<kArenaAllocMisc> { virtual bool CanThrow() const { return false; } bool HasSideEffects() const { return side_effects_.HasSideEffects(); } - virtual bool ActAsNullConstant() const { return false; } - // Does not apply for all instructions, but having this at top level greatly // simplifies the null check elimination. virtual bool CanBeNull() const { @@ -2080,8 +2089,6 @@ class HNullConstant : public HConstant { size_t ComputeHashCode() const OVERRIDE { return 0; } - bool ActAsNullConstant() const OVERRIDE { return true; } - DECLARE_INSTRUCTION(NullConstant); private: @@ -2103,11 +2110,6 @@ class HIntConstant : public HConstant { size_t ComputeHashCode() const OVERRIDE { return GetValue(); } - // TODO: Null is represented by the `0` constant. In most cases we replace it - // with a HNullConstant but we don't do it when comparing (a != null). This - // method is an workaround until we fix the above. - bool ActAsNullConstant() const OVERRIDE { return value_ == 0; } - bool IsMinusOne() const OVERRIDE { return GetValue() == -1; } bool IsZero() const OVERRIDE { return GetValue() == 0; } bool IsOne() const OVERRIDE { return GetValue() == 1; } @@ -2220,6 +2222,14 @@ class HInvoke : public HInstruction { class HInvokeStaticOrDirect : public HInvoke { public: + // Requirements of this method call regarding the class + // initialization (clinit) check of its declaring class. + enum class ClinitCheckRequirement { + kNone, // Class already initialized. + kExplicit, // Static call having explicit clinit check as last input. + kImplicit, // Static call implicitly requiring a clinit check. + }; + HInvokeStaticOrDirect(ArenaAllocator* arena, uint32_t number_of_arguments, Primitive::Type return_type, @@ -2227,11 +2237,13 @@ class HInvokeStaticOrDirect : public HInvoke { uint32_t dex_method_index, bool is_recursive, InvokeType original_invoke_type, - InvokeType invoke_type) + InvokeType invoke_type, + ClinitCheckRequirement clinit_check_requirement) : HInvoke(arena, number_of_arguments, return_type, dex_pc, dex_method_index), original_invoke_type_(original_invoke_type), invoke_type_(invoke_type), - is_recursive_(is_recursive) {} + is_recursive_(is_recursive), + clinit_check_requirement_(clinit_check_requirement) {} bool CanDoImplicitNullCheckOn(HInstruction* obj) const OVERRIDE { UNUSED(obj); @@ -2245,12 +2257,60 @@ class HInvokeStaticOrDirect : public HInvoke { bool IsRecursive() const { return is_recursive_; } bool NeedsDexCache() const OVERRIDE { return !IsRecursive(); } + // Is this instruction a call to a static method? + bool IsStatic() const { + return GetInvokeType() == kStatic; + } + + // Remove the art::HClinitCheck or art::HLoadClass instruction as + // last input (only relevant for static calls with explicit clinit + // check). + void RemoveClinitCheckOrLoadClassAsLastInput() { + DCHECK(IsStaticWithExplicitClinitCheck()); + size_t last_input_index = InputCount() - 1; + HInstruction* last_input = InputAt(last_input_index); + DCHECK(last_input != nullptr); + DCHECK(last_input->IsClinitCheck() || last_input->IsLoadClass()) << last_input->DebugName(); + RemoveAsUserOfInput(last_input_index); + inputs_.DeleteAt(last_input_index); + clinit_check_requirement_ = ClinitCheckRequirement::kImplicit; + DCHECK(IsStaticWithImplicitClinitCheck()); + } + + // Is this a call to a static method whose declaring class has an + // explicit intialization check in the graph? + bool IsStaticWithExplicitClinitCheck() const { + return IsStatic() && (clinit_check_requirement_ == ClinitCheckRequirement::kExplicit); + } + + // Is this a call to a static method whose declaring class has an + // implicit intialization check requirement? + bool IsStaticWithImplicitClinitCheck() const { + return IsStatic() && (clinit_check_requirement_ == ClinitCheckRequirement::kImplicit); + } + DECLARE_INSTRUCTION(InvokeStaticOrDirect); + protected: + const HUserRecord<HInstruction*> InputRecordAt(size_t i) const OVERRIDE { + const HUserRecord<HInstruction*> input_record = HInvoke::InputRecordAt(i); + if (kIsDebugBuild && IsStaticWithExplicitClinitCheck() && (i == InputCount() - 1)) { + HInstruction* input = input_record.GetInstruction(); + // `input` is the last input of a static invoke marked as having + // an explicit clinit check. It must either be: + // - an art::HClinitCheck instruction, set by art::HGraphBuilder; or + // - an art::HLoadClass instruction, set by art::PrepareForRegisterAllocation. + DCHECK(input != nullptr); + DCHECK(input->IsClinitCheck() || input->IsLoadClass()) << input->DebugName(); + } + return input_record; + } + private: const InvokeType original_invoke_type_; const InvokeType invoke_type_; const bool is_recursive_; + ClinitCheckRequirement clinit_check_requirement_; DISALLOW_COPY_AND_ASSIGN(HInvokeStaticOrDirect); }; @@ -2755,6 +2815,7 @@ class HPhi : public HInstruction { size_t InputCount() const OVERRIDE { return inputs_.Size(); } void AddInput(HInstruction* input); + void RemoveInputAt(size_t index); Primitive::Type GetType() const OVERRIDE { return type_; } void SetType(Primitive::Type type) { type_ = type; } @@ -3223,7 +3284,6 @@ class HLoadString : public HExpression<0> { DISALLOW_COPY_AND_ASSIGN(HLoadString); }; -// TODO: Pass this check to HInvokeStaticOrDirect nodes. /** * Performs an initialization check on its Class object input. */ @@ -3364,6 +3424,7 @@ class HInstanceOf : public HExpression<2> { uint32_t dex_pc) : HExpression(Primitive::kPrimBoolean, SideEffects::None()), class_is_final_(class_is_final), + must_do_null_check_(true), dex_pc_(dex_pc) { SetRawInputAt(0, object); SetRawInputAt(1, constant); @@ -3383,10 +3444,15 @@ class HInstanceOf : public HExpression<2> { bool IsClassFinal() const { return class_is_final_; } + // Used only in code generation. + bool MustDoNullCheck() const { return must_do_null_check_; } + void ClearMustDoNullCheck() { must_do_null_check_ = false; } + DECLARE_INSTRUCTION(InstanceOf); private: const bool class_is_final_; + bool must_do_null_check_; const uint32_t dex_pc_; DISALLOW_COPY_AND_ASSIGN(HInstanceOf); @@ -3427,6 +3493,7 @@ class HCheckCast : public HTemplateInstruction<2> { uint32_t dex_pc) : HTemplateInstruction(SideEffects::None()), class_is_final_(class_is_final), + must_do_null_check_(true), dex_pc_(dex_pc) { SetRawInputAt(0, object); SetRawInputAt(1, constant); @@ -3445,6 +3512,9 @@ class HCheckCast : public HTemplateInstruction<2> { bool CanThrow() const OVERRIDE { return true; } + bool MustDoNullCheck() const { return must_do_null_check_; } + void ClearMustDoNullCheck() { must_do_null_check_ = false; } + uint32_t GetDexPc() const { return dex_pc_; } bool IsClassFinal() const { return class_is_final_; } @@ -3453,6 +3523,7 @@ class HCheckCast : public HTemplateInstruction<2> { private: const bool class_is_final_; + bool must_do_null_check_; const uint32_t dex_pc_; DISALLOW_COPY_AND_ASSIGN(HCheckCast); diff --git a/compiler/optimizing/optimization.cc b/compiler/optimizing/optimization.cc index b13e07eb22..c46a21955c 100644 --- a/compiler/optimizing/optimization.cc +++ b/compiler/optimizing/optimization.cc @@ -21,9 +21,9 @@ namespace art { -void HOptimization::MaybeRecordStat(MethodCompilationStat compilation_stat) const { +void HOptimization::MaybeRecordStat(MethodCompilationStat compilation_stat, size_t count) const { if (stats_ != nullptr) { - stats_->RecordStat(compilation_stat); + stats_->RecordStat(compilation_stat, count); } } diff --git a/compiler/optimizing/optimization.h b/compiler/optimizing/optimization.h index 8b2028177b..ccf8de9f6a 100644 --- a/compiler/optimizing/optimization.h +++ b/compiler/optimizing/optimization.h @@ -48,7 +48,7 @@ class HOptimization : public ValueObject { void Check(); protected: - void MaybeRecordStat(MethodCompilationStat compilation_stat) const; + void MaybeRecordStat(MethodCompilationStat compilation_stat, size_t count = 1) const; HGraph* const graph_; // Used to record stats about the optimization. diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index 218894fe02..05451bcaa6 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -324,11 +324,11 @@ static void RunOptimizations(HGraph* graph, HDeadCodeElimination dce2(graph, stats, "dead_code_elimination_final"); HConstantFolding fold1(graph); InstructionSimplifier simplify1(graph, stats); - HBooleanSimplifier boolean_not(graph); + HBooleanSimplifier boolean_simplify(graph); HInliner inliner(graph, dex_compilation_unit, dex_compilation_unit, driver, stats); - HConstantFolding fold2(graph); + HConstantFolding fold2(graph, "constant_folding_after_inlining"); SideEffectsAnalysis side_effects(graph); GVNOptimization gvn(graph, side_effects); LICM licm(graph, side_effects); @@ -343,10 +343,10 @@ static void RunOptimizations(HGraph* graph, &dce1, &fold1, &simplify1, + &inliner, // BooleanSimplifier depends on the InstructionSimplifier removing redundant // suspend checks to recognize empty blocks. - &boolean_not, - &inliner, + &boolean_simplify, &fold2, &side_effects, &gvn, diff --git a/compiler/optimizing/optimizing_compiler_stats.h b/compiler/optimizing/optimizing_compiler_stats.h index e6508c9851..65c84e6942 100644 --- a/compiler/optimizing/optimizing_compiler_stats.h +++ b/compiler/optimizing/optimizing_compiler_stats.h @@ -58,8 +58,8 @@ class OptimizingCompilerStats { public: OptimizingCompilerStats() {} - void RecordStat(MethodCompilationStat stat) { - compile_stats_[stat]++; + void RecordStat(MethodCompilationStat stat, size_t count = 1) { + compile_stats_[stat] += count; } void Log() const { diff --git a/compiler/optimizing/prepare_for_register_allocation.cc b/compiler/optimizing/prepare_for_register_allocation.cc index f5d8d82571..fa6b3c292c 100644 --- a/compiler/optimizing/prepare_for_register_allocation.cc +++ b/compiler/optimizing/prepare_for_register_allocation.cc @@ -79,4 +79,26 @@ void PrepareForRegisterAllocation::VisitCondition(HCondition* condition) { } } +void PrepareForRegisterAllocation::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { + if (invoke->IsStaticWithExplicitClinitCheck()) { + size_t last_input_index = invoke->InputCount() - 1; + HInstruction* last_input = invoke->InputAt(last_input_index); + DCHECK(last_input->IsLoadClass()) << last_input->DebugName(); + + // Remove a load class instruction as last input of a static + // invoke, which has been added (along with a clinit check, + // removed by PrepareForRegisterAllocation::VisitClinitCheck + // previously) by the graph builder during the creation of the + // static invoke instruction, but is no longer required at this + // stage (i.e., after inlining has been performed). + invoke->RemoveClinitCheckOrLoadClassAsLastInput(); + + // If the load class instruction is no longer used, remove it from + // the graph. + if (!last_input->HasUses()) { + last_input->GetBlock()->RemoveInstruction(last_input); + } + } +} + } // namespace art diff --git a/compiler/optimizing/prepare_for_register_allocation.h b/compiler/optimizing/prepare_for_register_allocation.h index c28507c925..d7f277fa0d 100644 --- a/compiler/optimizing/prepare_for_register_allocation.h +++ b/compiler/optimizing/prepare_for_register_allocation.h @@ -39,6 +39,7 @@ class PrepareForRegisterAllocation : public HGraphDelegateVisitor { void VisitBoundType(HBoundType* bound_type) OVERRIDE; void VisitClinitCheck(HClinitCheck* check) OVERRIDE; void VisitCondition(HCondition* condition) OVERRIDE; + void VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) OVERRIDE; DISALLOW_COPY_AND_ASSIGN(PrepareForRegisterAllocation); }; diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc index de6941c983..12b1c2b9bd 100644 --- a/compiler/optimizing/reference_type_propagation.cc +++ b/compiler/optimizing/reference_type_propagation.cc @@ -68,11 +68,11 @@ void ReferenceTypePropagation::BoundTypeForIfNotNull(HBasicBlock* block) { } HInstruction* input0 = ifInput->InputAt(0); HInstruction* input1 = ifInput->InputAt(1); - HInstruction* obj; + HInstruction* obj = nullptr; - if ((input0->GetType() == Primitive::kPrimNot) && input1->ActAsNullConstant()) { + if (input1->IsNullConstant()) { obj = input0; - } else if ((input1->GetType() == Primitive::kPrimNot) && input0->ActAsNullConstant()) { + } else if (input0->IsNullConstant()) { obj = input1; } else { return; diff --git a/compiler/optimizing/register_allocator.cc b/compiler/optimizing/register_allocator.cc index f8e00f6873..0fdf051957 100644 --- a/compiler/optimizing/register_allocator.cc +++ b/compiler/optimizing/register_allocator.cc @@ -378,7 +378,7 @@ void RegisterAllocator::ProcessInstruction(HInstruction* instruction) { // Split just before first register use. size_t first_register_use = current->FirstRegisterUse(); if (first_register_use != kNoLifetime) { - LiveInterval* split = Split(current, first_register_use - 1); + LiveInterval* split = SplitBetween(current, current->GetStart(), first_register_use - 1); // Don't add directly to `unhandled`, it needs to be sorted and the start // of this new interval might be after intervals already in the list. AddSorted(&unhandled, split); @@ -997,7 +997,7 @@ bool RegisterAllocator::AllocateBlockedReg(LiveInterval* current) { // If the first use of that instruction is after the last use of the found // register, we split this interval just before its first register use. AllocateSpillSlotFor(current); - LiveInterval* split = Split(current, first_register_use - 1); + LiveInterval* split = SplitBetween(current, current->GetStart(), first_register_use - 1); if (current == split) { DumpInterval(std::cerr, current); DumpAllIntervals(std::cerr); @@ -1100,6 +1100,31 @@ void RegisterAllocator::AddSorted(GrowableArray<LiveInterval*>* array, LiveInter } } +LiveInterval* RegisterAllocator::SplitBetween(LiveInterval* interval, size_t from, size_t to) { + HBasicBlock* block_from = liveness_.GetBlockFromPosition(from); + HBasicBlock* block_to = liveness_.GetBlockFromPosition(to); + DCHECK(block_from != nullptr); + DCHECK(block_to != nullptr); + + // Both locations are in the same block. We split at the given location. + if (block_from == block_to) { + return Split(interval, to); + } + + // If `to` is in a loop, find the outermost loop header which does not contain `from`. + for (HLoopInformationOutwardIterator it(*block_to); !it.Done(); it.Advance()) { + HBasicBlock* header = it.Current()->GetHeader(); + if (block_from->GetLifetimeStart() >= header->GetLifetimeStart()) { + break; + } + block_to = header; + } + + // Split at the start of the found block, to piggy back on existing moves + // due to resolution if non-linear control flow (see `ConnectSplitSiblings`). + return Split(interval, block_to->GetLifetimeStart()); +} + LiveInterval* RegisterAllocator::Split(LiveInterval* interval, size_t position) { DCHECK_GE(position, interval->GetStart()); DCHECK(!interval->IsDeadAt(position)); diff --git a/compiler/optimizing/register_allocator.h b/compiler/optimizing/register_allocator.h index 717be75533..dc9c708eea 100644 --- a/compiler/optimizing/register_allocator.h +++ b/compiler/optimizing/register_allocator.h @@ -86,8 +86,12 @@ class RegisterAllocator { // Add `interval` in the given sorted list. static void AddSorted(GrowableArray<LiveInterval*>* array, LiveInterval* interval); - // Split `interval` at the position `at`. The new interval starts at `at`. - LiveInterval* Split(LiveInterval* interval, size_t at); + // Split `interval` at the position `position`. The new interval starts at `position`. + LiveInterval* Split(LiveInterval* interval, size_t position); + + // Split `interval` at a position between `from` and `to`. The method will try + // to find an optimal split position. + LiveInterval* SplitBetween(LiveInterval* interval, size_t from, size_t to); // Returns whether `reg` is blocked by the code generator. bool IsBlocked(int reg) const; diff --git a/compiler/optimizing/register_allocator_test.cc b/compiler/optimizing/register_allocator_test.cc index 182cd0e833..8c6d904a4c 100644 --- a/compiler/optimizing/register_allocator_test.cc +++ b/compiler/optimizing/register_allocator_test.cc @@ -854,6 +854,10 @@ TEST(RegisterAllocatorTest, SpillInactive) { X86InstructionSetFeatures::FromCppDefines()); x86::CodeGeneratorX86 codegen(graph, *features_x86.get(), CompilerOptions()); SsaLivenessAnalysis liveness(graph, &codegen); + // Populate the instructions in the liveness object, to please the register allocator. + for (size_t i = 0; i < 32; ++i) { + liveness.instructions_from_lifetime_position_.Add(user); + } RegisterAllocator register_allocator(&allocator, &codegen, liveness); register_allocator.unhandled_core_intervals_.Add(fourth); diff --git a/compiler/optimizing/ssa_liveness_analysis.h b/compiler/optimizing/ssa_liveness_analysis.h index fe70d3a861..97254edb5e 100644 --- a/compiler/optimizing/ssa_liveness_analysis.h +++ b/compiler/optimizing/ssa_liveness_analysis.h @@ -998,6 +998,15 @@ class SsaLivenessAnalysis : public ValueObject { return instructions_from_lifetime_position_.Get(index); } + HBasicBlock* GetBlockFromPosition(size_t index) const { + HInstruction* instruction = GetInstructionFromPosition(index / 2); + if (instruction == nullptr) { + // If we are at a block boundary, get the block following. + instruction = GetInstructionFromPosition((index / 2) + 1); + } + return instruction->GetBlock(); + } + HInstruction* GetTempUser(LiveInterval* temp) const { // A temporary shares the same lifetime start as the instruction that requires it. DCHECK(temp->IsTemp()); @@ -1068,6 +1077,8 @@ class SsaLivenessAnalysis : public ValueObject { GrowableArray<HInstruction*> instructions_from_lifetime_position_; size_t number_of_ssa_values_; + ART_FRIEND_TEST(RegisterAllocatorTest, SpillInactive); + DISALLOW_COPY_AND_ASSIGN(SsaLivenessAnalysis); }; diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc new file mode 100644 index 0000000000..8344fc3237 --- /dev/null +++ b/compiler/optimizing/stack_map_stream.cc @@ -0,0 +1,359 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "stack_map_stream.h" + +namespace art { + +void StackMapStream::BeginStackMapEntry(uint32_t dex_pc, + uint32_t native_pc_offset, + uint32_t register_mask, + BitVector* sp_mask, + uint32_t num_dex_registers, + uint8_t inlining_depth) { + DCHECK_EQ(0u, current_entry_.dex_pc) << "EndStackMapEntry not called after BeginStackMapEntry"; + current_entry_.dex_pc = dex_pc; + current_entry_.native_pc_offset = native_pc_offset; + current_entry_.register_mask = register_mask; + current_entry_.sp_mask = sp_mask; + current_entry_.num_dex_registers = num_dex_registers; + current_entry_.inlining_depth = inlining_depth; + current_entry_.dex_register_locations_start_index = dex_register_locations_.Size(); + current_entry_.inline_infos_start_index = inline_infos_.Size(); + current_entry_.dex_register_map_hash = 0; + current_entry_.same_dex_register_map_as_ = kNoSameDexMapFound; + if (num_dex_registers != 0) { + current_entry_.live_dex_registers_mask = + new (allocator_) ArenaBitVector(allocator_, num_dex_registers, true); + } else { + current_entry_.live_dex_registers_mask = nullptr; + } + + if (sp_mask != nullptr) { + stack_mask_max_ = std::max(stack_mask_max_, sp_mask->GetHighestBitSet()); + } + if (inlining_depth > 0) { + number_of_stack_maps_with_inline_info_++; + } + + dex_pc_max_ = std::max(dex_pc_max_, dex_pc); + native_pc_offset_max_ = std::max(native_pc_offset_max_, native_pc_offset); + register_mask_max_ = std::max(register_mask_max_, register_mask); +} + +void StackMapStream::EndStackMapEntry() { + current_entry_.same_dex_register_map_as_ = FindEntryWithTheSameDexMap(); + stack_maps_.Add(current_entry_); + current_entry_ = StackMapEntry(); +} + +void StackMapStream::AddDexRegisterEntry(uint16_t dex_register, + DexRegisterLocation::Kind kind, + int32_t value) { + DCHECK_LT(dex_register, current_entry_.num_dex_registers); + + if (kind != DexRegisterLocation::Kind::kNone) { + // Ensure we only use non-compressed location kind at this stage. + DCHECK(DexRegisterLocation::IsShortLocationKind(kind)) + << DexRegisterLocation::PrettyDescriptor(kind); + DexRegisterLocation location(kind, value); + + // Look for Dex register `location` in the location catalog (using the + // companion hash map of locations to indices). Use its index if it + // is already in the location catalog. If not, insert it (in the + // location catalog and the hash map) and use the newly created index. + auto it = location_catalog_entries_indices_.Find(location); + if (it != location_catalog_entries_indices_.end()) { + // Retrieve the index from the hash map. + dex_register_locations_.Add(it->second); + } else { + // Create a new entry in the location catalog and the hash map. + size_t index = location_catalog_entries_.Size(); + location_catalog_entries_.Add(location); + dex_register_locations_.Add(index); + location_catalog_entries_indices_.Insert(std::make_pair(location, index)); + } + + current_entry_.live_dex_registers_mask->SetBit(dex_register); + current_entry_.dex_register_map_hash += + (1 << (dex_register % (sizeof(current_entry_.dex_register_map_hash) * kBitsPerByte))); + current_entry_.dex_register_map_hash += static_cast<uint32_t>(value); + current_entry_.dex_register_map_hash += static_cast<uint32_t>(kind); + } +} + +void StackMapStream::AddInlineInfoEntry(uint32_t method_index) { + InlineInfoEntry entry; + entry.method_index = method_index; + inline_infos_.Add(entry); +} + +size_t StackMapStream::PrepareForFillIn() { + int stack_mask_number_of_bits = stack_mask_max_ + 1; // Need room for max element too. + stack_mask_size_ = RoundUp(stack_mask_number_of_bits, kBitsPerByte) / kBitsPerByte; + inline_info_size_ = ComputeInlineInfoSize(); + dex_register_maps_size_ = ComputeDexRegisterMapsSize(); + stack_maps_size_ = stack_maps_.Size() + * StackMap::ComputeStackMapSize(stack_mask_size_, + inline_info_size_, + dex_register_maps_size_, + dex_pc_max_, + native_pc_offset_max_, + register_mask_max_); + dex_register_location_catalog_size_ = ComputeDexRegisterLocationCatalogSize(); + + // Note: use RoundUp to word-size here if you want CodeInfo objects to be word aligned. + needed_size_ = CodeInfo::kFixedSize + + dex_register_location_catalog_size_ + + stack_maps_size_ + + dex_register_maps_size_ + + inline_info_size_; + + dex_register_location_catalog_start_ = CodeInfo::kFixedSize; + stack_maps_start_ = dex_register_location_catalog_start_ + dex_register_location_catalog_size_; + dex_register_maps_start_ = stack_maps_start_ + stack_maps_size_; + inline_infos_start_ = dex_register_maps_start_ + dex_register_maps_size_; + + return needed_size_; +} + +size_t StackMapStream::ComputeDexRegisterLocationCatalogSize() const { + size_t size = DexRegisterLocationCatalog::kFixedSize; + for (size_t location_catalog_entry_index = 0; + location_catalog_entry_index < location_catalog_entries_.Size(); + ++location_catalog_entry_index) { + DexRegisterLocation dex_register_location = + location_catalog_entries_.Get(location_catalog_entry_index); + size += DexRegisterLocationCatalog::EntrySize(dex_register_location); + } + return size; +} + +size_t StackMapStream::ComputeDexRegisterMapSize(const StackMapEntry& entry) const { + // Size of the map in bytes. + size_t size = DexRegisterMap::kFixedSize; + // Add the live bit mask for the Dex register liveness. + size += DexRegisterMap::GetLiveBitMaskSize(entry.num_dex_registers); + // Compute the size of the set of live Dex register entries. + size_t number_of_live_dex_registers = 0; + for (size_t dex_register_number = 0; + dex_register_number < entry.num_dex_registers; + ++dex_register_number) { + if (entry.live_dex_registers_mask->IsBitSet(dex_register_number)) { + ++number_of_live_dex_registers; + } + } + size_t map_entries_size_in_bits = + DexRegisterMap::SingleEntrySizeInBits(location_catalog_entries_.Size()) + * number_of_live_dex_registers; + size_t map_entries_size_in_bytes = + RoundUp(map_entries_size_in_bits, kBitsPerByte) / kBitsPerByte; + size += map_entries_size_in_bytes; + return size; +} + +size_t StackMapStream::ComputeDexRegisterMapsSize() const { + size_t size = 0; + for (size_t i = 0; i < stack_maps_.Size(); ++i) { + StackMapEntry entry = stack_maps_.Get(i); + if (entry.same_dex_register_map_as_ == kNoSameDexMapFound) { + // Entries with the same dex map will have the same offset. + size += ComputeDexRegisterMapSize(entry); + } + } + return size; +} + +size_t StackMapStream::ComputeInlineInfoSize() const { + return inline_infos_.Size() * InlineInfo::SingleEntrySize() + // For encoding the depth. + + (number_of_stack_maps_with_inline_info_ * InlineInfo::kFixedSize); +} + +void StackMapStream::FillIn(MemoryRegion region) { + DCHECK_EQ(0u, current_entry_.dex_pc) << "EndStackMapEntry not called after BeginStackMapEntry"; + DCHECK_NE(0u, needed_size_) << "PrepareForFillIn not called before FillIn"; + + CodeInfo code_info(region); + DCHECK_EQ(region.size(), needed_size_); + code_info.SetOverallSize(region.size()); + + MemoryRegion dex_register_locations_region = region.Subregion( + dex_register_maps_start_, dex_register_maps_size_); + + MemoryRegion inline_infos_region = region.Subregion( + inline_infos_start_, inline_info_size_); + + code_info.SetEncoding(inline_info_size_, + dex_register_maps_size_, + dex_pc_max_, + native_pc_offset_max_, + register_mask_max_); + code_info.SetNumberOfStackMaps(stack_maps_.Size()); + code_info.SetStackMaskSize(stack_mask_size_); + DCHECK_EQ(code_info.GetStackMapsSize(), stack_maps_size_); + + // Set the Dex register location catalog. + code_info.SetNumberOfDexRegisterLocationCatalogEntries(location_catalog_entries_.Size()); + MemoryRegion dex_register_location_catalog_region = region.Subregion( + dex_register_location_catalog_start_, dex_register_location_catalog_size_); + DexRegisterLocationCatalog dex_register_location_catalog(dex_register_location_catalog_region); + // Offset in `dex_register_location_catalog` where to store the next + // register location. + size_t location_catalog_offset = DexRegisterLocationCatalog::kFixedSize; + for (size_t i = 0, e = location_catalog_entries_.Size(); i < e; ++i) { + DexRegisterLocation dex_register_location = location_catalog_entries_.Get(i); + dex_register_location_catalog.SetRegisterInfo(location_catalog_offset, dex_register_location); + location_catalog_offset += DexRegisterLocationCatalog::EntrySize(dex_register_location); + } + // Ensure we reached the end of the Dex registers location_catalog. + DCHECK_EQ(location_catalog_offset, dex_register_location_catalog_region.size()); + + uintptr_t next_dex_register_map_offset = 0; + uintptr_t next_inline_info_offset = 0; + for (size_t i = 0, e = stack_maps_.Size(); i < e; ++i) { + StackMap stack_map = code_info.GetStackMapAt(i); + StackMapEntry entry = stack_maps_.Get(i); + + stack_map.SetDexPc(code_info, entry.dex_pc); + stack_map.SetNativePcOffset(code_info, entry.native_pc_offset); + stack_map.SetRegisterMask(code_info, entry.register_mask); + if (entry.sp_mask != nullptr) { + stack_map.SetStackMask(code_info, *entry.sp_mask); + } + + if (entry.num_dex_registers == 0) { + // No dex map available. + stack_map.SetDexRegisterMapOffset(code_info, StackMap::kNoDexRegisterMap); + } else { + // Search for an entry with the same dex map. + if (entry.same_dex_register_map_as_ != kNoSameDexMapFound) { + // If we have a hit reuse the offset. + stack_map.SetDexRegisterMapOffset(code_info, + code_info.GetStackMapAt(entry.same_dex_register_map_as_) + .GetDexRegisterMapOffset(code_info)); + } else { + // New dex registers maps should be added to the stack map. + MemoryRegion register_region = + dex_register_locations_region.Subregion( + next_dex_register_map_offset, + ComputeDexRegisterMapSize(entry)); + next_dex_register_map_offset += register_region.size(); + DexRegisterMap dex_register_map(register_region); + stack_map.SetDexRegisterMapOffset( + code_info, register_region.start() - dex_register_locations_region.start()); + + // Set the live bit mask. + dex_register_map.SetLiveBitMask(entry.num_dex_registers, *entry.live_dex_registers_mask); + + // Set the dex register location mapping data. + for (size_t dex_register_number = 0, index_in_dex_register_locations = 0; + dex_register_number < entry.num_dex_registers; + ++dex_register_number) { + if (entry.live_dex_registers_mask->IsBitSet(dex_register_number)) { + size_t location_catalog_entry_index = + dex_register_locations_.Get(entry.dex_register_locations_start_index + + index_in_dex_register_locations); + dex_register_map.SetLocationCatalogEntryIndex( + index_in_dex_register_locations, + location_catalog_entry_index, + entry.num_dex_registers, + location_catalog_entries_.Size()); + ++index_in_dex_register_locations; + } + } + } + } + + // Set the inlining info. + if (entry.inlining_depth != 0) { + MemoryRegion inline_region = inline_infos_region.Subregion( + next_inline_info_offset, + InlineInfo::kFixedSize + entry.inlining_depth * InlineInfo::SingleEntrySize()); + next_inline_info_offset += inline_region.size(); + InlineInfo inline_info(inline_region); + + // Currently relative to the dex register map. + stack_map.SetInlineDescriptorOffset( + code_info, inline_region.start() - dex_register_locations_region.start()); + + inline_info.SetDepth(entry.inlining_depth); + for (size_t j = 0; j < entry.inlining_depth; ++j) { + InlineInfoEntry inline_entry = inline_infos_.Get(j + entry.inline_infos_start_index); + inline_info.SetMethodReferenceIndexAtDepth(j, inline_entry.method_index); + } + } else { + if (inline_info_size_ != 0) { + stack_map.SetInlineDescriptorOffset(code_info, StackMap::kNoInlineInfo); + } + } + } +} + +size_t StackMapStream::FindEntryWithTheSameDexMap() { + size_t current_entry_index = stack_maps_.Size(); + auto entries_it = dex_map_hash_to_stack_map_indices_.find(current_entry_.dex_register_map_hash); + if (entries_it == dex_map_hash_to_stack_map_indices_.end()) { + // We don't have a perfect hash functions so we need a list to collect all stack maps + // which might have the same dex register map. + GrowableArray<uint32_t> stack_map_indices(allocator_, 1); + stack_map_indices.Add(current_entry_index); + dex_map_hash_to_stack_map_indices_.Put(current_entry_.dex_register_map_hash, stack_map_indices); + return kNoSameDexMapFound; + } + + // We might have collisions, so we need to check whether or not we really have a match. + for (size_t i = 0; i < entries_it->second.Size(); i++) { + size_t test_entry_index = entries_it->second.Get(i); + if (HaveTheSameDexMaps(stack_maps_.Get(test_entry_index), current_entry_)) { + return test_entry_index; + } + } + entries_it->second.Add(current_entry_index); + return kNoSameDexMapFound; +} + +bool StackMapStream::HaveTheSameDexMaps(const StackMapEntry& a, const StackMapEntry& b) const { + if (a.live_dex_registers_mask == nullptr && b.live_dex_registers_mask == nullptr) { + return true; + } + if (a.live_dex_registers_mask == nullptr || b.live_dex_registers_mask == nullptr) { + return false; + } + if (a.num_dex_registers != b.num_dex_registers) { + return false; + } + + int index_in_dex_register_locations = 0; + for (uint32_t i = 0; i < a.num_dex_registers; i++) { + if (a.live_dex_registers_mask->IsBitSet(i) != b.live_dex_registers_mask->IsBitSet(i)) { + return false; + } + if (a.live_dex_registers_mask->IsBitSet(i)) { + size_t a_loc = dex_register_locations_.Get( + a.dex_register_locations_start_index + index_in_dex_register_locations); + size_t b_loc = dex_register_locations_.Get( + b.dex_register_locations_start_index + index_in_dex_register_locations); + if (a_loc != b_loc) { + return false; + } + ++index_in_dex_register_locations; + } + } + return true; +} + +} // namespace art diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h index 9a9e068a9b..0c626be89f 100644 --- a/compiler/optimizing/stack_map_stream.h +++ b/compiler/optimizing/stack_map_stream.h @@ -70,13 +70,18 @@ class StackMapStream : public ValueObject { native_pc_offset_max_(0), register_mask_max_(0), number_of_stack_maps_with_inline_info_(0), - dex_map_hash_to_stack_map_indices_(std::less<uint32_t>(), allocator->Adapter()) {} - - // Compute bytes needed to encode a mask with the given maximum element. - static uint32_t StackMaskEncodingSize(int max_element) { - int number_of_bits = max_element + 1; // Need room for max element too. - return RoundUp(number_of_bits, kBitsPerByte) / kBitsPerByte; - } + dex_map_hash_to_stack_map_indices_(std::less<uint32_t>(), allocator->Adapter()), + current_entry_(), + stack_mask_size_(0), + inline_info_size_(0), + dex_register_maps_size_(0), + stack_maps_size_(0), + dex_register_location_catalog_size_(0), + dex_register_location_catalog_start_(0), + stack_maps_start_(0), + dex_register_maps_start_(0), + inline_infos_start_(0), + needed_size_(0) {} // See runtime/stack_map.h to know what these fields contain. struct StackMapEntry { @@ -90,380 +95,42 @@ class StackMapStream : public ValueObject { size_t inline_infos_start_index; BitVector* live_dex_registers_mask; uint32_t dex_register_map_hash; + size_t same_dex_register_map_as_; }; struct InlineInfoEntry { uint32_t method_index; }; - void AddStackMapEntry(uint32_t dex_pc, - uint32_t native_pc_offset, - uint32_t register_mask, - BitVector* sp_mask, - uint32_t num_dex_registers, - uint8_t inlining_depth) { - StackMapEntry entry; - entry.dex_pc = dex_pc; - entry.native_pc_offset = native_pc_offset; - entry.register_mask = register_mask; - entry.sp_mask = sp_mask; - entry.num_dex_registers = num_dex_registers; - entry.inlining_depth = inlining_depth; - entry.dex_register_locations_start_index = dex_register_locations_.Size(); - entry.inline_infos_start_index = inline_infos_.Size(); - entry.dex_register_map_hash = 0; - if (num_dex_registers != 0) { - entry.live_dex_registers_mask = - new (allocator_) ArenaBitVector(allocator_, num_dex_registers, true); - } else { - entry.live_dex_registers_mask = nullptr; - } - stack_maps_.Add(entry); - - if (sp_mask != nullptr) { - stack_mask_max_ = std::max(stack_mask_max_, sp_mask->GetHighestBitSet()); - } - if (inlining_depth > 0) { - number_of_stack_maps_with_inline_info_++; - } - - dex_pc_max_ = std::max(dex_pc_max_, dex_pc); - native_pc_offset_max_ = std::max(native_pc_offset_max_, native_pc_offset); - register_mask_max_ = std::max(register_mask_max_, register_mask); - } - - void AddInlineInfoEntry(uint32_t method_index) { - InlineInfoEntry entry; - entry.method_index = method_index; - inline_infos_.Add(entry); - } - - size_t ComputeNeededSize() { - size_t size = CodeInfo::kFixedSize - + ComputeDexRegisterLocationCatalogSize() - + ComputeStackMapsSize() - + ComputeDexRegisterMapsSize() - + ComputeInlineInfoSize(); - // Note: use RoundUp to word-size here if you want CodeInfo objects to be word aligned. - return size; - } - - size_t ComputeStackMaskSize() const { - return StackMaskEncodingSize(stack_mask_max_); - } - - size_t ComputeStackMapsSize() { - return stack_maps_.Size() * StackMap::ComputeStackMapSize( - ComputeStackMaskSize(), - ComputeInlineInfoSize(), - ComputeDexRegisterMapsSize(), - dex_pc_max_, - native_pc_offset_max_, - register_mask_max_); - } - - // Compute the size of the Dex register location catalog of `entry`. - size_t ComputeDexRegisterLocationCatalogSize() const { - size_t size = DexRegisterLocationCatalog::kFixedSize; - for (size_t location_catalog_entry_index = 0; - location_catalog_entry_index < location_catalog_entries_.Size(); - ++location_catalog_entry_index) { - DexRegisterLocation dex_register_location = - location_catalog_entries_.Get(location_catalog_entry_index); - size += DexRegisterLocationCatalog::EntrySize(dex_register_location); - } - return size; - } - - size_t ComputeDexRegisterMapSize(const StackMapEntry& entry) const { - // Size of the map in bytes. - size_t size = DexRegisterMap::kFixedSize; - // Add the live bit mask for the Dex register liveness. - size += DexRegisterMap::GetLiveBitMaskSize(entry.num_dex_registers); - // Compute the size of the set of live Dex register entries. - size_t number_of_live_dex_registers = 0; - for (size_t dex_register_number = 0; - dex_register_number < entry.num_dex_registers; - ++dex_register_number) { - if (entry.live_dex_registers_mask->IsBitSet(dex_register_number)) { - ++number_of_live_dex_registers; - } - } - size_t map_entries_size_in_bits = - DexRegisterMap::SingleEntrySizeInBits(location_catalog_entries_.Size()) - * number_of_live_dex_registers; - size_t map_entries_size_in_bytes = - RoundUp(map_entries_size_in_bits, kBitsPerByte) / kBitsPerByte; - size += map_entries_size_in_bytes; - return size; - } - - // Compute the size of all the Dex register maps. - size_t ComputeDexRegisterMapsSize() { - size_t size = 0; - for (size_t i = 0; i < stack_maps_.Size(); ++i) { - if (FindEntryWithTheSameDexMap(i) == kNoSameDexMapFound) { - // Entries with the same dex map will have the same offset. - size += ComputeDexRegisterMapSize(stack_maps_.Get(i)); - } - } - return size; - } - - // Compute the size of all the inline information pieces. - size_t ComputeInlineInfoSize() const { - return inline_infos_.Size() * InlineInfo::SingleEntrySize() - // For encoding the depth. - + (number_of_stack_maps_with_inline_info_ * InlineInfo::kFixedSize); - } + void BeginStackMapEntry(uint32_t dex_pc, + uint32_t native_pc_offset, + uint32_t register_mask, + BitVector* sp_mask, + uint32_t num_dex_registers, + uint8_t inlining_depth); + void EndStackMapEntry(); - size_t ComputeDexRegisterLocationCatalogStart() const { - return CodeInfo::kFixedSize; - } - - size_t ComputeStackMapsStart() const { - return ComputeDexRegisterLocationCatalogStart() + ComputeDexRegisterLocationCatalogSize(); - } - - size_t ComputeDexRegisterMapsStart() { - return ComputeStackMapsStart() + ComputeStackMapsSize(); - } - - size_t ComputeInlineInfoStart() { - return ComputeDexRegisterMapsStart() + ComputeDexRegisterMapsSize(); - } + void AddDexRegisterEntry(uint16_t dex_register, + DexRegisterLocation::Kind kind, + int32_t value); - void FillIn(MemoryRegion region) { - CodeInfo code_info(region); - DCHECK_EQ(region.size(), ComputeNeededSize()); - code_info.SetOverallSize(region.size()); + void AddInlineInfoEntry(uint32_t method_index); - size_t stack_mask_size = ComputeStackMaskSize(); - - size_t dex_register_map_size = ComputeDexRegisterMapsSize(); - size_t inline_info_size = ComputeInlineInfoSize(); - - MemoryRegion dex_register_locations_region = region.Subregion( - ComputeDexRegisterMapsStart(), - dex_register_map_size); - - MemoryRegion inline_infos_region = region.Subregion( - ComputeInlineInfoStart(), - inline_info_size); - - code_info.SetEncoding(inline_info_size, - dex_register_map_size, - dex_pc_max_, - native_pc_offset_max_, - register_mask_max_); - code_info.SetNumberOfStackMaps(stack_maps_.Size()); - code_info.SetStackMaskSize(stack_mask_size); - DCHECK_EQ(code_info.GetStackMapsSize(), ComputeStackMapsSize()); - - // Set the Dex register location catalog. - code_info.SetNumberOfDexRegisterLocationCatalogEntries( - location_catalog_entries_.Size()); - MemoryRegion dex_register_location_catalog_region = region.Subregion( - ComputeDexRegisterLocationCatalogStart(), - ComputeDexRegisterLocationCatalogSize()); - DexRegisterLocationCatalog dex_register_location_catalog(dex_register_location_catalog_region); - // Offset in `dex_register_location_catalog` where to store the next - // register location. - size_t location_catalog_offset = DexRegisterLocationCatalog::kFixedSize; - for (size_t i = 0, e = location_catalog_entries_.Size(); i < e; ++i) { - DexRegisterLocation dex_register_location = location_catalog_entries_.Get(i); - dex_register_location_catalog.SetRegisterInfo(location_catalog_offset, dex_register_location); - location_catalog_offset += DexRegisterLocationCatalog::EntrySize(dex_register_location); - } - // Ensure we reached the end of the Dex registers location_catalog. - DCHECK_EQ(location_catalog_offset, dex_register_location_catalog_region.size()); - - uintptr_t next_dex_register_map_offset = 0; - uintptr_t next_inline_info_offset = 0; - for (size_t i = 0, e = stack_maps_.Size(); i < e; ++i) { - StackMap stack_map = code_info.GetStackMapAt(i); - StackMapEntry entry = stack_maps_.Get(i); - - stack_map.SetDexPc(code_info, entry.dex_pc); - stack_map.SetNativePcOffset(code_info, entry.native_pc_offset); - stack_map.SetRegisterMask(code_info, entry.register_mask); - if (entry.sp_mask != nullptr) { - stack_map.SetStackMask(code_info, *entry.sp_mask); - } - - if (entry.num_dex_registers == 0) { - // No dex map available. - stack_map.SetDexRegisterMapOffset(code_info, StackMap::kNoDexRegisterMap); - } else { - // Search for an entry with the same dex map. - size_t entry_with_same_map = FindEntryWithTheSameDexMap(i); - if (entry_with_same_map != kNoSameDexMapFound) { - // If we have a hit reuse the offset. - stack_map.SetDexRegisterMapOffset(code_info, - code_info.GetStackMapAt(entry_with_same_map).GetDexRegisterMapOffset(code_info)); - } else { - // New dex registers maps should be added to the stack map. - MemoryRegion register_region = - dex_register_locations_region.Subregion( - next_dex_register_map_offset, - ComputeDexRegisterMapSize(entry)); - next_dex_register_map_offset += register_region.size(); - DexRegisterMap dex_register_map(register_region); - stack_map.SetDexRegisterMapOffset( - code_info, register_region.start() - dex_register_locations_region.start()); - - // Set the live bit mask. - dex_register_map.SetLiveBitMask(entry.num_dex_registers, *entry.live_dex_registers_mask); - - // Set the dex register location mapping data. - for (size_t dex_register_number = 0, index_in_dex_register_locations = 0; - dex_register_number < entry.num_dex_registers; - ++dex_register_number) { - if (entry.live_dex_registers_mask->IsBitSet(dex_register_number)) { - size_t location_catalog_entry_index = - dex_register_locations_.Get(entry.dex_register_locations_start_index - + index_in_dex_register_locations); - dex_register_map.SetLocationCatalogEntryIndex( - index_in_dex_register_locations, - location_catalog_entry_index, - entry.num_dex_registers, - location_catalog_entries_.Size()); - ++index_in_dex_register_locations; - } - } - } - } - - // Set the inlining info. - if (entry.inlining_depth != 0) { - MemoryRegion inline_region = inline_infos_region.Subregion( - next_inline_info_offset, - InlineInfo::kFixedSize + entry.inlining_depth * InlineInfo::SingleEntrySize()); - next_inline_info_offset += inline_region.size(); - InlineInfo inline_info(inline_region); - - // Currently relative to the dex register map. - stack_map.SetInlineDescriptorOffset( - code_info, inline_region.start() - dex_register_locations_region.start()); - - inline_info.SetDepth(entry.inlining_depth); - for (size_t j = 0; j < entry.inlining_depth; ++j) { - InlineInfoEntry inline_entry = inline_infos_.Get(j + entry.inline_infos_start_index); - inline_info.SetMethodReferenceIndexAtDepth(j, inline_entry.method_index); - } - } else { - if (inline_info_size != 0) { - stack_map.SetInlineDescriptorOffset(code_info, StackMap::kNoInlineInfo); - } - } - } - } - - void AddDexRegisterEntry(uint16_t dex_register, DexRegisterLocation::Kind kind, int32_t value) { - StackMapEntry entry = stack_maps_.Get(stack_maps_.Size() - 1); - DCHECK_LT(dex_register, entry.num_dex_registers); - - if (kind != DexRegisterLocation::Kind::kNone) { - // Ensure we only use non-compressed location kind at this stage. - DCHECK(DexRegisterLocation::IsShortLocationKind(kind)) - << DexRegisterLocation::PrettyDescriptor(kind); - DexRegisterLocation location(kind, value); - - // Look for Dex register `location` in the location catalog (using the - // companion hash map of locations to indices). Use its index if it - // is already in the location catalog. If not, insert it (in the - // location catalog and the hash map) and use the newly created index. - auto it = location_catalog_entries_indices_.Find(location); - if (it != location_catalog_entries_indices_.end()) { - // Retrieve the index from the hash map. - dex_register_locations_.Add(it->second); - } else { - // Create a new entry in the location catalog and the hash map. - size_t index = location_catalog_entries_.Size(); - location_catalog_entries_.Add(location); - dex_register_locations_.Add(index); - location_catalog_entries_indices_.Insert(std::make_pair(location, index)); - } - - entry.live_dex_registers_mask->SetBit(dex_register); - entry.dex_register_map_hash += - (1 << (dex_register % (sizeof(entry.dex_register_map_hash) * kBitsPerByte))); - entry.dex_register_map_hash += static_cast<uint32_t>(value); - entry.dex_register_map_hash += static_cast<uint32_t>(kind); - stack_maps_.Put(stack_maps_.Size() - 1, entry); - } - } + // Prepares the stream to fill in a memory region. Must be called before FillIn. + // Returns the size (in bytes) needed to store this stream. + size_t PrepareForFillIn(); + void FillIn(MemoryRegion region); private: - // Returns the index of an entry with the same dex register map - // or kNoSameDexMapFound if no such entry exists. - size_t FindEntryWithTheSameDexMap(size_t entry_index) { - StackMapEntry entry = stack_maps_.Get(entry_index); - auto entries_it = dex_map_hash_to_stack_map_indices_.find(entry.dex_register_map_hash); - if (entries_it == dex_map_hash_to_stack_map_indices_.end()) { - // We don't have a perfect hash functions so we need a list to collect all stack maps - // which might have the same dex register map. - GrowableArray<uint32_t> stack_map_indices(allocator_, 1); - stack_map_indices.Add(entry_index); - dex_map_hash_to_stack_map_indices_.Put(entry.dex_register_map_hash, stack_map_indices); - return kNoSameDexMapFound; - } - - // TODO: We don't need to add ourselves to the map if we can guarantee that - // FindEntryWithTheSameDexMap is called just once per stack map entry. - // A good way to do this is to cache the offset in the stack map entry. This - // is easier to do if we add markers when the stack map constructions begins - // and when it ends. + size_t ComputeDexRegisterLocationCatalogSize() const; + size_t ComputeDexRegisterMapSize(const StackMapEntry& entry) const; + size_t ComputeDexRegisterMapsSize() const; + size_t ComputeInlineInfoSize() const; - // We might have collisions, so we need to check whether or not we should - // add the entry to the map. `needs_to_be_added` keeps track of this. - bool needs_to_be_added = true; - size_t result = kNoSameDexMapFound; - for (size_t i = 0; i < entries_it->second.Size(); i++) { - size_t test_entry_index = entries_it->second.Get(i); - if (test_entry_index == entry_index) { - needs_to_be_added = false; - } else if (HaveTheSameDexMaps(stack_maps_.Get(test_entry_index), entry)) { - result = test_entry_index; - needs_to_be_added = false; - break; - } - } - if (needs_to_be_added) { - entries_it->second.Add(entry_index); - } - return result; - } - - bool HaveTheSameDexMaps(const StackMapEntry& a, const StackMapEntry& b) const { - if (a.live_dex_registers_mask == nullptr && b.live_dex_registers_mask == nullptr) { - return true; - } - if (a.live_dex_registers_mask == nullptr || b.live_dex_registers_mask == nullptr) { - return false; - } - if (a.num_dex_registers != b.num_dex_registers) { - return false; - } - - int index_in_dex_register_locations = 0; - for (uint32_t i = 0; i < a.num_dex_registers; i++) { - if (a.live_dex_registers_mask->IsBitSet(i) != b.live_dex_registers_mask->IsBitSet(i)) { - return false; - } - if (a.live_dex_registers_mask->IsBitSet(i)) { - size_t a_loc = dex_register_locations_.Get( - a.dex_register_locations_start_index + index_in_dex_register_locations); - size_t b_loc = dex_register_locations_.Get( - b.dex_register_locations_start_index + index_in_dex_register_locations); - if (a_loc != b_loc) { - return false; - } - ++index_in_dex_register_locations; - } - } - return true; - } + // Returns the index of an entry with the same dex register map as the current_entry, + // or kNoSameDexMapFound if no such entry exists. + size_t FindEntryWithTheSameDexMap(); + bool HaveTheSameDexMaps(const StackMapEntry& a, const StackMapEntry& b) const; ArenaAllocator* allocator_; GrowableArray<StackMapEntry> stack_maps_; @@ -476,8 +143,7 @@ class StackMapStream : public ValueObject { DexRegisterLocationHashFn> LocationCatalogEntriesIndices; LocationCatalogEntriesIndices location_catalog_entries_indices_; - // A set of concatenated maps of Dex register locations indices to - // `location_catalog_entries_`. + // A set of concatenated maps of Dex register locations indices to `location_catalog_entries_`. GrowableArray<size_t> dex_register_locations_; GrowableArray<InlineInfoEntry> inline_infos_; int stack_mask_max_; @@ -488,6 +154,18 @@ class StackMapStream : public ValueObject { ArenaSafeMap<uint32_t, GrowableArray<uint32_t>> dex_map_hash_to_stack_map_indices_; + StackMapEntry current_entry_; + size_t stack_mask_size_; + size_t inline_info_size_; + size_t dex_register_maps_size_; + size_t stack_maps_size_; + size_t dex_register_location_catalog_size_; + size_t dex_register_location_catalog_start_; + size_t stack_maps_start_; + size_t dex_register_maps_start_; + size_t inline_infos_start_; + size_t needed_size_; + static constexpr uint32_t kNoSameDexMapFound = -1; DISALLOW_COPY_AND_ASSIGN(StackMapStream); diff --git a/compiler/optimizing/stack_map_test.cc b/compiler/optimizing/stack_map_test.cc index 8d160bc81e..3291a77021 100644 --- a/compiler/optimizing/stack_map_test.cc +++ b/compiler/optimizing/stack_map_test.cc @@ -40,11 +40,12 @@ TEST(StackMapTest, Test1) { ArenaBitVector sp_mask(&arena, 0, false); size_t number_of_dex_registers = 2; - stream.AddStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0); + stream.BeginStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0); stream.AddDexRegisterEntry(0, Kind::kInStack, 0); // Short location. stream.AddDexRegisterEntry(1, Kind::kConstant, -2); // Short location. + stream.EndStackMapEntry(); - size_t size = stream.ComputeNeededSize(); + size_t size = stream.PrepareForFillIn(); void* memory = arena.Alloc(size, kArenaAllocMisc); MemoryRegion region(memory, size); stream.FillIn(region); @@ -123,20 +124,22 @@ TEST(StackMapTest, Test2) { sp_mask1.SetBit(2); sp_mask1.SetBit(4); size_t number_of_dex_registers = 2; - stream.AddStackMapEntry(0, 64, 0x3, &sp_mask1, number_of_dex_registers, 2); + stream.BeginStackMapEntry(0, 64, 0x3, &sp_mask1, number_of_dex_registers, 2); stream.AddDexRegisterEntry(0, Kind::kInStack, 0); // Short location. stream.AddDexRegisterEntry(1, Kind::kConstant, -2); // Large location. stream.AddInlineInfoEntry(42); stream.AddInlineInfoEntry(82); + stream.EndStackMapEntry(); ArenaBitVector sp_mask2(&arena, 0, true); sp_mask2.SetBit(3); sp_mask1.SetBit(8); - stream.AddStackMapEntry(1, 128, 0xFF, &sp_mask2, number_of_dex_registers, 0); + stream.BeginStackMapEntry(1, 128, 0xFF, &sp_mask2, number_of_dex_registers, 0); stream.AddDexRegisterEntry(0, Kind::kInRegister, 18); // Short location. stream.AddDexRegisterEntry(1, Kind::kInFpuRegister, 3); // Short location. + stream.EndStackMapEntry(); - size_t size = stream.ComputeNeededSize(); + size_t size = stream.PrepareForFillIn(); void* memory = arena.Alloc(size, kArenaAllocMisc); MemoryRegion region(memory, size); stream.FillIn(region); @@ -273,11 +276,12 @@ TEST(StackMapTest, TestNonLiveDexRegisters) { ArenaBitVector sp_mask(&arena, 0, false); uint32_t number_of_dex_registers = 2; - stream.AddStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0); + stream.BeginStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0); stream.AddDexRegisterEntry(0, Kind::kNone, 0); // No location. stream.AddDexRegisterEntry(1, Kind::kConstant, -2); // Large location. + stream.EndStackMapEntry(); - size_t size = stream.ComputeNeededSize(); + size_t size = stream.PrepareForFillIn(); void* memory = arena.Alloc(size, kArenaAllocMisc); MemoryRegion region(memory, size); stream.FillIn(region); @@ -353,7 +357,7 @@ TEST(StackMapTest, DexRegisterMapOffsetOverflow) { ArenaBitVector sp_mask(&arena, 0, false); uint32_t number_of_dex_registers = 1024; // Create the first stack map (and its Dex register map). - stream.AddStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0); + stream.BeginStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0); uint32_t number_of_dex_live_registers_in_dex_register_map_0 = number_of_dex_registers - 8; for (uint32_t i = 0; i < number_of_dex_live_registers_in_dex_register_map_0; ++i) { // Use two different Dex register locations to populate this map, @@ -362,13 +366,15 @@ TEST(StackMapTest, DexRegisterMapOffsetOverflow) { // art::DexRegisterMap::SingleEntrySizeInBits). stream.AddDexRegisterEntry(i, Kind::kConstant, i % 2); // Short location. } + stream.EndStackMapEntry(); // Create the second stack map (and its Dex register map). - stream.AddStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0); + stream.BeginStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0); for (uint32_t i = 0; i < number_of_dex_registers; ++i) { stream.AddDexRegisterEntry(i, Kind::kConstant, 0); // Short location. } + stream.EndStackMapEntry(); - size_t size = stream.ComputeNeededSize(); + size_t size = stream.PrepareForFillIn(); void* memory = arena.Alloc(size, kArenaAllocMisc); MemoryRegion region(memory, size); stream.FillIn(region); @@ -413,19 +419,22 @@ TEST(StackMapTest, TestShareDexRegisterMap) { ArenaBitVector sp_mask(&arena, 0, false); uint32_t number_of_dex_registers = 2; // First stack map. - stream.AddStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0); + stream.BeginStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0); stream.AddDexRegisterEntry(0, Kind::kInRegister, 0); // Short location. stream.AddDexRegisterEntry(1, Kind::kConstant, -2); // Large location. + stream.EndStackMapEntry(); // Second stack map, which should share the same dex register map. - stream.AddStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0); + stream.BeginStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0); stream.AddDexRegisterEntry(0, Kind::kInRegister, 0); // Short location. stream.AddDexRegisterEntry(1, Kind::kConstant, -2); // Large location. + stream.EndStackMapEntry(); // Third stack map (doesn't share the dex register map). - stream.AddStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0); + stream.BeginStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0); stream.AddDexRegisterEntry(0, Kind::kInRegister, 2); // Short location. stream.AddDexRegisterEntry(1, Kind::kConstant, -2); // Large location. + stream.EndStackMapEntry(); - size_t size = stream.ComputeNeededSize(); + size_t size = stream.PrepareForFillIn(); void* memory = arena.Alloc(size, kArenaAllocMisc); MemoryRegion region(memory, size); stream.FillIn(region); @@ -462,9 +471,10 @@ TEST(StackMapTest, TestNoDexRegisterMap) { ArenaBitVector sp_mask(&arena, 0, false); uint32_t number_of_dex_registers = 0; - stream.AddStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0); + stream.BeginStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0); + stream.EndStackMapEntry(); - size_t size = stream.ComputeNeededSize(); + size_t size = stream.PrepareForFillIn(); void* memory = arena.Alloc(size, kArenaAllocMisc); MemoryRegion region(memory, size); stream.FillIn(region); |