diff options
47 files changed, 1524 insertions, 1250 deletions
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index 11743f3f1b..32bde8e3b4 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -718,6 +718,9 @@ void ImageWriter::PruneNonImageClasses() { // contains data only valid during a real run. dex_cache->SetFieldObject<false>(mirror::DexCache::DexOffset(), nullptr); } + + // Drop the array class cache in the ClassLinker, as these are roots holding those classes live. + class_linker->DropFindArrayClassCache(); } bool ImageWriter::NonImageClassesVisitor(Class* klass, void* arg) { diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index 04952beb41..f7731069fb 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -1638,8 +1638,7 @@ void LocationsBuilderARM::VisitTypeConversion(HTypeConversion* conversion) { // Processing a Dex `long-to-double' instruction. locations->SetInAt(0, Location::RequiresRegister()); locations->SetOut(Location::RequiresFpuRegister()); - locations->AddTemp(Location::RequiresRegister()); - locations->AddTemp(Location::RequiresRegister()); + locations->AddTemp(Location::RequiresFpuRegister()); locations->AddTemp(Location::RequiresFpuRegister()); break; @@ -1857,29 +1856,21 @@ void InstructionCodeGeneratorARM::VisitTypeConversion(HTypeConversion* conversio Register high = in.AsRegisterPairHigh<Register>(); SRegister out_s = out.AsFpuRegisterPairLow<SRegister>(); DRegister out_d = FromLowSToD(out_s); - Register constant_low = locations->GetTemp(0).AsRegister<Register>(); - Register constant_high = locations->GetTemp(1).AsRegister<Register>(); - SRegister temp_s = locations->GetTemp(2).AsFpuRegisterPairLow<SRegister>(); + SRegister temp_s = locations->GetTemp(0).AsFpuRegisterPairLow<SRegister>(); DRegister temp_d = FromLowSToD(temp_s); - - // out_d = int-to-double(high) - __ vmovsr(out_s, high); - __ vcvtdi(out_d, out_s); - // Using vmovd to load the `k2Pow32EncodingForDouble` constant - // as an immediate value into `temp_d` does not work, as - // this instruction only transfers 8 significant bits of its - // immediate operand. Instead, use two 32-bit core - // registers to load `k2Pow32EncodingForDouble` into `temp_d`. - __ LoadImmediate(constant_low, Low32Bits(k2Pow32EncodingForDouble)); - __ LoadImmediate(constant_high, High32Bits(k2Pow32EncodingForDouble)); - __ vmovdrr(temp_d, constant_low, constant_high); - // out_d = out_d * 2^32 - __ vmuld(out_d, out_d, temp_d); - // temp_d = unsigned-to-double(low) - __ vmovsr(temp_s, low); - __ vcvtdu(temp_d, temp_s); - // out_d = out_d + temp_d - __ vaddd(out_d, out_d, temp_d); + SRegister constant_s = locations->GetTemp(1).AsFpuRegisterPairLow<SRegister>(); + DRegister constant_d = FromLowSToD(constant_s); + + // temp_d = int-to-double(high) + __ vmovsr(temp_s, high); + __ vcvtdi(temp_d, temp_s); + // constant_d = k2Pow32EncodingForDouble + __ LoadDImmediate(constant_d, bit_cast<double, int64_t>(k2Pow32EncodingForDouble)); + // out_d = unsigned-to-double(low) + __ vmovsr(out_s, low); + __ vcvtdu(out_d, out_s); + // out_d += temp_d * constant_d + __ vmlad(out_d, temp_d, constant_d); break; } @@ -2910,22 +2901,22 @@ void InstructionCodeGeneratorARM::VisitPhi(HPhi* instruction) { void InstructionCodeGeneratorARM::GenerateMemoryBarrier(MemBarrierKind kind) { // TODO (ported from quick): revisit Arm barrier kinds - DmbOptions flavour = DmbOptions::ISH; // quiet c++ warnings + DmbOptions flavor = DmbOptions::ISH; // quiet c++ warnings switch (kind) { case MemBarrierKind::kAnyStore: case MemBarrierKind::kLoadAny: case MemBarrierKind::kAnyAny: { - flavour = DmbOptions::ISH; + flavor = DmbOptions::ISH; break; } case MemBarrierKind::kStoreStore: { - flavour = DmbOptions::ISHST; + flavor = DmbOptions::ISHST; break; } default: LOG(FATAL) << "Unexpected memory barrier " << kind; } - __ dmb(flavour); + __ dmb(flavor); } void InstructionCodeGeneratorARM::GenerateWideAtomicLoad(Register addr, diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index 83f8d83bc4..07d0dd6b49 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -27,6 +27,7 @@ #include "mirror/class_loader.h" #include "mirror/dex_cache.h" #include "nodes.h" +#include "reference_type_propagation.h" #include "register_allocator.h" #include "ssa_phi_elimination.h" #include "scoped_thread_state_change.h" @@ -57,7 +58,7 @@ void HInliner::Run() { next_block = (i == blocks.Size() - 1) ? nullptr : blocks.Get(i + 1); for (HInstruction* instruction = block->GetFirstInstruction(); instruction != nullptr;) { HInstruction* next = instruction->GetNext(); - HInvokeStaticOrDirect* call = instruction->AsInvokeStaticOrDirect(); + HInvoke* call = instruction->AsInvoke(); // As long as the call is not intrinsified, it is worth trying to inline. if (call != nullptr && call->GetIntrinsic() == Intrinsics::kNone) { // We use the original invoke type to ensure the resolution of the called method @@ -69,6 +70,13 @@ void HInliner::Run() { bool should_inline = callee_name.find("$inline$") != std::string::npos; CHECK(!should_inline) << "Could not inline " << callee_name; } + } else { + if (kIsDebugBuild) { + std::string callee_name = + PrettyMethod(call->GetDexMethodIndex(), *outer_compilation_unit_.GetDexFile()); + bool must_not_inline = callee_name.find("$noinline$") != std::string::npos; + CHECK(!must_not_inline) << "Should not have inlined " << callee_name; + } } } instruction = next; @@ -76,6 +84,82 @@ void HInliner::Run() { } } +static bool IsMethodOrDeclaringClassFinal(ArtMethod* method) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return method->IsFinal() || method->GetDeclaringClass()->IsFinal(); +} + +/** + * Given the `resolved_method` looked up in the dex cache, try to find + * the actual runtime target of an interface or virtual call. + * Return nullptr if the runtime target cannot be proven. + */ +static ArtMethod* FindVirtualOrInterfaceTarget(HInvoke* invoke, ArtMethod* resolved_method) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + if (IsMethodOrDeclaringClassFinal(resolved_method)) { + // No need to lookup further, the resolved method will be the target. + return resolved_method; + } + + HInstruction* receiver = invoke->InputAt(0); + if (receiver->IsNullCheck()) { + // Due to multiple levels of inlining within the same pass, it might be that + // null check does not have the reference type of the actual receiver. + receiver = receiver->InputAt(0); + } + ReferenceTypeInfo info = receiver->GetReferenceTypeInfo(); + if (info.IsTop()) { + // We have no information on the receiver. + return nullptr; + } else if (!info.IsExact()) { + // We currently only support inlining with known receivers. + // TODO: Remove this check, we should be able to inline final methods + // on unknown receivers. + return nullptr; + } else if (info.GetTypeHandle()->IsInterface()) { + // Statically knowing that the receiver has an interface type cannot + // help us find what is the target method. + return nullptr; + } else if (!resolved_method->GetDeclaringClass()->IsAssignableFrom(info.GetTypeHandle().Get())) { + // The method that we're trying to call is not in the receiver's class or super classes. + return nullptr; + } + + ClassLinker* cl = Runtime::Current()->GetClassLinker(); + size_t pointer_size = cl->GetImagePointerSize(); + if (invoke->IsInvokeInterface()) { + resolved_method = info.GetTypeHandle()->FindVirtualMethodForInterface( + resolved_method, pointer_size); + } else { + DCHECK(invoke->IsInvokeVirtual()); + resolved_method = info.GetTypeHandle()->FindVirtualMethodForVirtual( + resolved_method, pointer_size); + } + + if (resolved_method == nullptr) { + // The information we had on the receiver was not enough to find + // the target method. Since we check above the exact type of the receiver, + // the only reason this can happen is an IncompatibleClassChangeError. + return nullptr; + } else if (resolved_method->IsAbstract()) { + // The information we had on the receiver was not enough to find + // the target method. Since we check above the exact type of the receiver, + // the only reason this can happen is an IncompatibleClassChangeError. + return nullptr; + } else if (IsMethodOrDeclaringClassFinal(resolved_method)) { + // A final method has to be the target method. + return resolved_method; + } else if (info.IsExact()) { + // If we found a method and the receiver's concrete type is statically + // known, we know for sure the target. + return resolved_method; + } else { + // Even if we did find a method, the receiver type was not enough to + // statically find the runtime target. + return nullptr; + } +} + bool HInliner::TryInline(HInvoke* invoke_instruction, uint32_t method_index, InvokeType invoke_type) const { @@ -83,19 +167,34 @@ bool HInliner::TryInline(HInvoke* invoke_instruction, const DexFile& caller_dex_file = *caller_compilation_unit_.GetDexFile(); VLOG(compiler) << "Try inlining " << PrettyMethod(method_index, caller_dex_file); - StackHandleScope<3> hs(soa.Self()); - Handle<mirror::DexCache> dex_cache( - hs.NewHandle(caller_compilation_unit_.GetClassLinker()->FindDexCache(caller_dex_file))); - Handle<mirror::ClassLoader> class_loader(hs.NewHandle( - soa.Decode<mirror::ClassLoader*>(caller_compilation_unit_.GetClassLoader()))); - ArtMethod* resolved_method(compiler_driver_->ResolveMethod( - soa, dex_cache, class_loader, &caller_compilation_unit_, method_index, invoke_type)); + ArtMethod* resolved_method = nullptr; + { + // Don't keep this handle scope on stack, otherwise we cannot do a reference type + // propagation while inlining. + StackHandleScope<2> hs(soa.Self()); + Handle<mirror::DexCache> dex_cache( + hs.NewHandle(caller_compilation_unit_.GetClassLinker()->FindDexCache(caller_dex_file))); + Handle<mirror::ClassLoader> class_loader(hs.NewHandle( + soa.Decode<mirror::ClassLoader*>(caller_compilation_unit_.GetClassLoader()))); + resolved_method = compiler_driver_->ResolveMethod( + soa, dex_cache, class_loader, &caller_compilation_unit_, method_index, invoke_type); + } if (resolved_method == nullptr) { VLOG(compiler) << "Method cannot be resolved " << PrettyMethod(method_index, caller_dex_file); return false; } + if (!invoke_instruction->IsInvokeStaticOrDirect()) { + resolved_method = FindVirtualOrInterfaceTarget(invoke_instruction, resolved_method); + if (resolved_method == nullptr) { + VLOG(compiler) << "Interface or virtual call to " + << PrettyMethod(method_index, caller_dex_file) + << "could not be statically determined"; + return false; + } + } + bool same_dex_file = true; const DexFile& outer_dex_file = *outer_compilation_unit_.GetDexFile(); if (resolved_method->GetDexFile()->GetLocation().compare(outer_dex_file.GetLocation()) != 0) { @@ -238,11 +337,13 @@ bool HInliner::TryBuildAndInline(ArtMethod* resolved_method, // Run simple optimizations on the graph. HDeadCodeElimination dce(callee_graph, stats_); HConstantFolding fold(callee_graph); + ReferenceTypePropagation type_propagation(callee_graph, handles_); InstructionSimplifier simplify(callee_graph, stats_); HOptimization* optimizations[] = { &dce, &fold, + &type_propagation, &simplify, }; @@ -256,6 +357,7 @@ bool HInliner::TryBuildAndInline(ArtMethod* resolved_method, outer_compilation_unit_, dex_compilation_unit, compiler_driver_, + handles_, stats_, depth_ + 1); inliner.Run(); diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h index b86c1ed505..ca713329f5 100644 --- a/compiler/optimizing/inliner.h +++ b/compiler/optimizing/inliner.h @@ -34,13 +34,15 @@ class HInliner : public HOptimization { const DexCompilationUnit& outer_compilation_unit, const DexCompilationUnit& caller_compilation_unit, CompilerDriver* compiler_driver, + StackHandleScopeCollection* handles, OptimizingCompilerStats* stats, size_t depth = 0) : HOptimization(outer_graph, true, kInlinerPassName, stats), outer_compilation_unit_(outer_compilation_unit), caller_compilation_unit_(caller_compilation_unit), compiler_driver_(compiler_driver), - depth_(depth) {} + depth_(depth), + handles_(handles) {} void Run() OVERRIDE; @@ -57,6 +59,7 @@ class HInliner : public HOptimization { const DexCompilationUnit& caller_compilation_unit_; CompilerDriver* const compiler_driver_; const size_t depth_; + StackHandleScopeCollection* const handles_; DISALLOW_COPY_AND_ASSIGN(HInliner); }; diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index f6ef2f7e82..bf0b9fac0f 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -326,7 +326,7 @@ static void RunOptimizations(HGraph* graph, InstructionSimplifier simplify1(graph, stats); HBooleanSimplifier boolean_simplify(graph); - HInliner inliner(graph, dex_compilation_unit, dex_compilation_unit, driver, stats); + HInliner inliner(graph, dex_compilation_unit, dex_compilation_unit, driver, handles, stats); HConstantFolding fold2(graph, "constant_folding_after_inlining"); SideEffectsAnalysis side_effects(graph); @@ -335,6 +335,8 @@ static void RunOptimizations(HGraph* graph, BoundsCheckElimination bce(graph); ReferenceTypePropagation type_propagation(graph, handles); InstructionSimplifier simplify2(graph, stats, "instruction_simplifier_after_types"); + InstructionSimplifier simplify3(graph, stats, "last_instruction_simplifier"); + ReferenceTypePropagation type_propagation2(graph, handles); IntrinsicsRecognizer intrinsics(graph, driver); @@ -343,7 +345,12 @@ static void RunOptimizations(HGraph* graph, &dce1, &fold1, &simplify1, + &type_propagation, + &simplify2, &inliner, + // Run another type propagation phase: inlining will open up more opprotunities + // to remove checkast/instanceof and null checks. + &type_propagation2, // BooleanSimplifier depends on the InstructionSimplifier removing redundant // suspend checks to recognize empty blocks. &boolean_simplify, @@ -352,8 +359,7 @@ static void RunOptimizations(HGraph* graph, &gvn, &licm, &bce, - &type_propagation, - &simplify2, + &simplify3, &dce2, }; diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc index 4f1f45769d..4edadef1a4 100644 --- a/compiler/optimizing/reference_type_propagation.cc +++ b/compiler/optimizing/reference_type_propagation.cc @@ -166,31 +166,34 @@ void ReferenceTypePropagation::BoundTypeForIfInstanceOf(HBasicBlock* block) { } } -void ReferenceTypePropagation::SetClassAsTypeInfo(HInstruction* instr, mirror::Class* klass) { +void ReferenceTypePropagation::SetClassAsTypeInfo(HInstruction* instr, + mirror::Class* klass, + bool is_exact) { if (klass != nullptr) { ScopedObjectAccess soa(Thread::Current()); MutableHandle<mirror::Class> handle = handles_->NewHandle(klass); - instr->SetReferenceTypeInfo(ReferenceTypeInfo::Create(handle, true)); + instr->SetReferenceTypeInfo(ReferenceTypeInfo::Create(handle, is_exact)); } } void ReferenceTypePropagation::UpdateReferenceTypeInfo(HInstruction* instr, uint16_t type_idx, - const DexFile& dex_file) { + const DexFile& dex_file, + bool is_exact) { DCHECK_EQ(instr->GetType(), Primitive::kPrimNot); ScopedObjectAccess soa(Thread::Current()); mirror::DexCache* dex_cache = Runtime::Current()->GetClassLinker()->FindDexCache(dex_file); // Get type from dex cache assuming it was populated by the verifier. - SetClassAsTypeInfo(instr, dex_cache->GetResolvedType(type_idx)); + SetClassAsTypeInfo(instr, dex_cache->GetResolvedType(type_idx), is_exact); } void ReferenceTypePropagation::VisitNewInstance(HNewInstance* instr) { - UpdateReferenceTypeInfo(instr, instr->GetTypeIndex(), instr->GetDexFile()); + UpdateReferenceTypeInfo(instr, instr->GetTypeIndex(), instr->GetDexFile(), /* is_exact */ true); } void ReferenceTypePropagation::VisitNewArray(HNewArray* instr) { - UpdateReferenceTypeInfo(instr, instr->GetTypeIndex(), instr->GetDexFile()); + UpdateReferenceTypeInfo(instr, instr->GetTypeIndex(), instr->GetDexFile(), /* is_exact */ true); } void ReferenceTypePropagation::UpdateFieldAccessTypeInfo(HInstruction* instr, @@ -206,7 +209,7 @@ void ReferenceTypePropagation::UpdateFieldAccessTypeInfo(HInstruction* instr, ArtField* field = cl->GetResolvedField(info.GetFieldIndex(), dex_cache); DCHECK(field != nullptr); mirror::Class* klass = field->GetType<false>(); - SetClassAsTypeInfo(instr, klass); + SetClassAsTypeInfo(instr, klass, /* is_exact */ false); } void ReferenceTypePropagation::VisitInstanceFieldGet(HInstanceFieldGet* instr) { diff --git a/compiler/optimizing/reference_type_propagation.h b/compiler/optimizing/reference_type_propagation.h index 74e425fb3e..0a1d4c496e 100644 --- a/compiler/optimizing/reference_type_propagation.h +++ b/compiler/optimizing/reference_type_propagation.h @@ -46,14 +46,17 @@ class ReferenceTypePropagation : public HOptimization { void VisitPhi(HPhi* phi); void VisitBasicBlock(HBasicBlock* block); void UpdateFieldAccessTypeInfo(HInstruction* instr, const FieldInfo& info); - void SetClassAsTypeInfo(HInstruction* instr, mirror::Class* klass); + void SetClassAsTypeInfo(HInstruction* instr, mirror::Class* klass, bool is_exact); void UpdateBoundType(HBoundType* bound_type) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void UpdatePhi(HPhi* phi) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void BoundTypeForIfNotNull(HBasicBlock* block); void BoundTypeForIfInstanceOf(HBasicBlock* block); - void UpdateReferenceTypeInfo(HInstruction* instr, uint16_t type_idx, const DexFile& dex_file); + void UpdateReferenceTypeInfo(HInstruction* instr, + uint16_t type_idx, + const DexFile& dex_file, + bool is_exact); void VisitInstanceFieldGet(HInstanceFieldGet* instr); void VisitStaticFieldGet(HStaticFieldGet* instr); diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc index 5663e3973d..42b9182d55 100644 --- a/compiler/optimizing/stack_map_stream.cc +++ b/compiler/optimizing/stack_map_stream.cc @@ -133,13 +133,13 @@ size_t StackMapStream::PrepareForFillIn() { 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_); + stack_map_encoding_ = StackMapEncoding::CreateFromSizes(stack_mask_size_, + inline_info_size_, + dex_register_maps_size_, + dex_pc_max_, + native_pc_offset_max_, + register_mask_max_); + stack_maps_size_ = stack_maps_.Size() * stack_map_encoding_.ComputeStackMapSize(); dex_register_location_catalog_size_ = ComputeDexRegisterLocationCatalogSize(); // Note: use RoundUp to word-size here if you want CodeInfo objects to be word aligned. @@ -235,14 +235,9 @@ void StackMapStream::FillIn(MemoryRegion region) { 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.SetEncoding(stack_map_encoding_); code_info.SetNumberOfStackMaps(stack_maps_.Size()); - code_info.SetStackMaskSize(stack_mask_size_); - DCHECK_EQ(code_info.GetStackMapsSize(), stack_maps_size_); + DCHECK_EQ(code_info.GetStackMapsSize(code_info.ExtractEncoding()), stack_maps_size_); // Set the Dex register location catalog. code_info.SetNumberOfDexRegisterLocationCatalogEntries(location_catalog_entries_.Size()); @@ -263,26 +258,27 @@ void StackMapStream::FillIn(MemoryRegion region) { 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); + StackMap stack_map = code_info.GetStackMapAt(i, stack_map_encoding_); 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); + stack_map.SetDexPc(stack_map_encoding_, entry.dex_pc); + stack_map.SetNativePcOffset(stack_map_encoding_, entry.native_pc_offset); + stack_map.SetRegisterMask(stack_map_encoding_, entry.register_mask); if (entry.sp_mask != nullptr) { - stack_map.SetStackMask(code_info, *entry.sp_mask); + stack_map.SetStackMask(stack_map_encoding_, *entry.sp_mask); } if (entry.num_dex_registers == 0) { // No dex map available. - stack_map.SetDexRegisterMapOffset(code_info, StackMap::kNoDexRegisterMap); + stack_map.SetDexRegisterMapOffset(stack_map_encoding_, 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)); + stack_map.SetDexRegisterMapOffset( + stack_map_encoding_, + code_info.GetStackMapAt(entry.same_dex_register_map_as_, stack_map_encoding_) + .GetDexRegisterMapOffset(stack_map_encoding_)); } else { // New dex registers maps should be added to the stack map. MemoryRegion register_region = dex_register_locations_region.Subregion( @@ -291,7 +287,7 @@ void StackMapStream::FillIn(MemoryRegion region) { 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()); + stack_map_encoding_, register_region.start() - dex_register_locations_region.start()); // Set the dex register location. FillInDexRegisterMap(dex_register_map, @@ -311,7 +307,7 @@ void StackMapStream::FillIn(MemoryRegion region) { // Currently relative to the dex register map. stack_map.SetInlineDescriptorOffset( - code_info, inline_region.start() - dex_register_locations_region.start()); + stack_map_encoding_, inline_region.start() - dex_register_locations_region.start()); inline_info.SetDepth(entry.inlining_depth); for (size_t depth = 0; depth < entry.inlining_depth; ++depth) { @@ -341,7 +337,7 @@ void StackMapStream::FillIn(MemoryRegion region) { } } else { if (inline_info_size_ != 0) { - stack_map.SetInlineDescriptorOffset(code_info, StackMap::kNoInlineInfo); + stack_map.SetInlineDescriptorOffset(stack_map_encoding_, StackMap::kNoInlineInfo); } } } diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h index 0af983b1bf..274d573350 100644 --- a/compiler/optimizing/stack_map_stream.h +++ b/compiler/optimizing/stack_map_stream.h @@ -171,6 +171,7 @@ class StackMapStream : public ValueObject { StackMapEntry current_entry_; InlineInfoEntry current_inline_info_; + StackMapEncoding stack_map_encoding_; size_t stack_mask_size_; size_t inline_info_size_; size_t dex_register_maps_size_; diff --git a/compiler/optimizing/stack_map_test.cc b/compiler/optimizing/stack_map_test.cc index 666fb604c0..b4ac1b4d1a 100644 --- a/compiler/optimizing/stack_map_test.cc +++ b/compiler/optimizing/stack_map_test.cc @@ -51,32 +51,33 @@ TEST(StackMapTest, Test1) { stream.FillIn(region); CodeInfo code_info(region); - ASSERT_EQ(0u, code_info.GetStackMaskSize()); + StackMapEncoding encoding = code_info.ExtractEncoding(); + ASSERT_EQ(0u, encoding.NumberOfBytesForStackMask()); ASSERT_EQ(1u, code_info.GetNumberOfStackMaps()); uint32_t number_of_location_catalog_entries = code_info.GetNumberOfDexRegisterLocationCatalogEntries(); ASSERT_EQ(2u, number_of_location_catalog_entries); - DexRegisterLocationCatalog location_catalog = code_info.GetDexRegisterLocationCatalog(); + DexRegisterLocationCatalog location_catalog = code_info.GetDexRegisterLocationCatalog(encoding); // The Dex register location catalog contains: // - one 1-byte short Dex register location, and // - one 5-byte large Dex register location. size_t expected_location_catalog_size = 1u + 5u; ASSERT_EQ(expected_location_catalog_size, location_catalog.Size()); - StackMap stack_map = code_info.GetStackMapAt(0); - ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0))); - ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64))); - ASSERT_EQ(0u, stack_map.GetDexPc(code_info)); - ASSERT_EQ(64u, stack_map.GetNativePcOffset(code_info)); - ASSERT_EQ(0x3u, stack_map.GetRegisterMask(code_info)); + StackMap stack_map = code_info.GetStackMapAt(0, encoding); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0, encoding))); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64, encoding))); + ASSERT_EQ(0u, stack_map.GetDexPc(encoding)); + ASSERT_EQ(64u, stack_map.GetNativePcOffset(encoding)); + ASSERT_EQ(0x3u, stack_map.GetRegisterMask(encoding)); - MemoryRegion stack_mask = stack_map.GetStackMask(code_info); + MemoryRegion stack_mask = stack_map.GetStackMask(encoding); ASSERT_TRUE(SameBits(stack_mask, sp_mask)); - ASSERT_TRUE(stack_map.HasDexRegisterMap(code_info)); + ASSERT_TRUE(stack_map.HasDexRegisterMap(encoding)); DexRegisterMap dex_register_map = - code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers); + code_info.GetDexRegisterMapOf(stack_map, encoding, number_of_dex_registers); ASSERT_TRUE(dex_register_map.IsDexRegisterLive(0)); ASSERT_TRUE(dex_register_map.IsDexRegisterLive(1)); ASSERT_EQ(2u, dex_register_map.GetNumberOfLiveDexRegisters(number_of_dex_registers)); @@ -86,16 +87,17 @@ TEST(StackMapTest, Test1) { size_t expected_dex_register_map_size = 1u + 1u; ASSERT_EQ(expected_dex_register_map_size, dex_register_map.Size()); - ASSERT_EQ(Kind::kInStack, - dex_register_map.GetLocationKind(0, number_of_dex_registers, code_info)); - ASSERT_EQ(Kind::kConstant, - dex_register_map.GetLocationKind(1, number_of_dex_registers, code_info)); - ASSERT_EQ(Kind::kInStack, - dex_register_map.GetLocationInternalKind(0, number_of_dex_registers, code_info)); - ASSERT_EQ(Kind::kConstantLargeValue, - dex_register_map.GetLocationInternalKind(1, number_of_dex_registers, code_info)); - ASSERT_EQ(0, dex_register_map.GetStackOffsetInBytes(0, number_of_dex_registers, code_info)); - ASSERT_EQ(-2, dex_register_map.GetConstant(1, number_of_dex_registers, code_info)); + ASSERT_EQ(Kind::kInStack, dex_register_map.GetLocationKind( + 0, number_of_dex_registers, code_info, encoding)); + ASSERT_EQ(Kind::kConstant, dex_register_map.GetLocationKind( + 1, number_of_dex_registers, code_info, encoding)); + ASSERT_EQ(Kind::kInStack, dex_register_map.GetLocationInternalKind( + 0, number_of_dex_registers, code_info, encoding)); + ASSERT_EQ(Kind::kConstantLargeValue, dex_register_map.GetLocationInternalKind( + 1, number_of_dex_registers, code_info, encoding)); + ASSERT_EQ(0, dex_register_map.GetStackOffsetInBytes( + 0, number_of_dex_registers, code_info, encoding)); + ASSERT_EQ(-2, dex_register_map.GetConstant(1, number_of_dex_registers, code_info, encoding)); size_t index0 = dex_register_map.GetLocationCatalogEntryIndex( 0, number_of_dex_registers, number_of_location_catalog_entries); @@ -112,7 +114,7 @@ TEST(StackMapTest, Test1) { ASSERT_EQ(0, location0.GetValue()); ASSERT_EQ(-2, location1.GetValue()); - ASSERT_FALSE(stack_map.HasInlineInfo(code_info)); + ASSERT_FALSE(stack_map.HasInlineInfo(encoding)); } TEST(StackMapTest, Test2) { @@ -148,13 +150,14 @@ TEST(StackMapTest, Test2) { stream.FillIn(region); CodeInfo code_info(region); - ASSERT_EQ(2u, code_info.GetStackMaskSize()); + StackMapEncoding encoding = code_info.ExtractEncoding(); + ASSERT_EQ(2u, encoding.NumberOfBytesForStackMask()); ASSERT_EQ(2u, code_info.GetNumberOfStackMaps()); uint32_t number_of_location_catalog_entries = code_info.GetNumberOfDexRegisterLocationCatalogEntries(); ASSERT_EQ(4u, number_of_location_catalog_entries); - DexRegisterLocationCatalog location_catalog = code_info.GetDexRegisterLocationCatalog(); + DexRegisterLocationCatalog location_catalog = code_info.GetDexRegisterLocationCatalog(encoding); // The Dex register location catalog contains: // - three 1-byte short Dex register locations, and // - one 5-byte large Dex register location. @@ -163,19 +166,19 @@ TEST(StackMapTest, Test2) { // First stack map. { - StackMap stack_map = code_info.GetStackMapAt(0); - ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0))); - ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64))); - ASSERT_EQ(0u, stack_map.GetDexPc(code_info)); - ASSERT_EQ(64u, stack_map.GetNativePcOffset(code_info)); - ASSERT_EQ(0x3u, stack_map.GetRegisterMask(code_info)); - - MemoryRegion stack_mask = stack_map.GetStackMask(code_info); + StackMap stack_map = code_info.GetStackMapAt(0, encoding); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0, encoding))); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64, encoding))); + ASSERT_EQ(0u, stack_map.GetDexPc(encoding)); + ASSERT_EQ(64u, stack_map.GetNativePcOffset(encoding)); + ASSERT_EQ(0x3u, stack_map.GetRegisterMask(encoding)); + + MemoryRegion stack_mask = stack_map.GetStackMask(encoding); ASSERT_TRUE(SameBits(stack_mask, sp_mask1)); - ASSERT_TRUE(stack_map.HasDexRegisterMap(code_info)); + ASSERT_TRUE(stack_map.HasDexRegisterMap(encoding)); DexRegisterMap dex_register_map = - code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers); + code_info.GetDexRegisterMapOf(stack_map, encoding, number_of_dex_registers); ASSERT_TRUE(dex_register_map.IsDexRegisterLive(0)); ASSERT_TRUE(dex_register_map.IsDexRegisterLive(1)); ASSERT_EQ(2u, dex_register_map.GetNumberOfLiveDexRegisters(number_of_dex_registers)); @@ -185,16 +188,17 @@ TEST(StackMapTest, Test2) { size_t expected_dex_register_map_size = 1u + 1u; ASSERT_EQ(expected_dex_register_map_size, dex_register_map.Size()); - ASSERT_EQ(Kind::kInStack, - dex_register_map.GetLocationKind(0, number_of_dex_registers, code_info)); - ASSERT_EQ(Kind::kConstant, - dex_register_map.GetLocationKind(1, number_of_dex_registers, code_info)); - ASSERT_EQ(Kind::kInStack, - dex_register_map.GetLocationInternalKind(0, number_of_dex_registers, code_info)); - ASSERT_EQ(Kind::kConstantLargeValue, - dex_register_map.GetLocationInternalKind(1, number_of_dex_registers, code_info)); - ASSERT_EQ(0, dex_register_map.GetStackOffsetInBytes(0, number_of_dex_registers, code_info)); - ASSERT_EQ(-2, dex_register_map.GetConstant(1, number_of_dex_registers, code_info)); + ASSERT_EQ(Kind::kInStack, dex_register_map.GetLocationKind( + 0, number_of_dex_registers, code_info, encoding)); + ASSERT_EQ(Kind::kConstant, dex_register_map.GetLocationKind( + 1, number_of_dex_registers, code_info, encoding)); + ASSERT_EQ(Kind::kInStack, dex_register_map.GetLocationInternalKind( + 0, number_of_dex_registers, code_info, encoding)); + ASSERT_EQ(Kind::kConstantLargeValue, dex_register_map.GetLocationInternalKind( + 1, number_of_dex_registers, code_info, encoding)); + ASSERT_EQ(0, dex_register_map.GetStackOffsetInBytes( + 0, number_of_dex_registers, code_info, encoding)); + ASSERT_EQ(-2, dex_register_map.GetConstant(1, number_of_dex_registers, code_info, encoding)); size_t index0 = dex_register_map.GetLocationCatalogEntryIndex( 0, number_of_dex_registers, number_of_location_catalog_entries); @@ -211,8 +215,8 @@ TEST(StackMapTest, Test2) { ASSERT_EQ(0, location0.GetValue()); ASSERT_EQ(-2, location1.GetValue()); - ASSERT_TRUE(stack_map.HasInlineInfo(code_info)); - InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map); + ASSERT_TRUE(stack_map.HasInlineInfo(encoding)); + InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map, encoding); ASSERT_EQ(2u, inline_info.GetDepth()); ASSERT_EQ(82u, inline_info.GetMethodIndexAtDepth(0)); ASSERT_EQ(42u, inline_info.GetMethodIndexAtDepth(1)); @@ -224,19 +228,19 @@ TEST(StackMapTest, Test2) { // Second stack map. { - StackMap stack_map = code_info.GetStackMapAt(1); - ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(1u))); - ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(128u))); - ASSERT_EQ(1u, stack_map.GetDexPc(code_info)); - ASSERT_EQ(128u, stack_map.GetNativePcOffset(code_info)); - ASSERT_EQ(0xFFu, stack_map.GetRegisterMask(code_info)); - - MemoryRegion stack_mask = stack_map.GetStackMask(code_info); + StackMap stack_map = code_info.GetStackMapAt(1, encoding); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(1u, encoding))); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(128u, encoding))); + ASSERT_EQ(1u, stack_map.GetDexPc(encoding)); + ASSERT_EQ(128u, stack_map.GetNativePcOffset(encoding)); + ASSERT_EQ(0xFFu, stack_map.GetRegisterMask(encoding)); + + MemoryRegion stack_mask = stack_map.GetStackMask(encoding); ASSERT_TRUE(SameBits(stack_mask, sp_mask2)); - ASSERT_TRUE(stack_map.HasDexRegisterMap(code_info)); + ASSERT_TRUE(stack_map.HasDexRegisterMap(encoding)); DexRegisterMap dex_register_map = - code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers); + code_info.GetDexRegisterMapOf(stack_map, encoding, number_of_dex_registers); ASSERT_TRUE(dex_register_map.IsDexRegisterLive(0)); ASSERT_TRUE(dex_register_map.IsDexRegisterLive(1)); ASSERT_EQ(2u, dex_register_map.GetNumberOfLiveDexRegisters(number_of_dex_registers)); @@ -246,16 +250,18 @@ TEST(StackMapTest, Test2) { size_t expected_dex_register_map_size = 1u + 1u; ASSERT_EQ(expected_dex_register_map_size, dex_register_map.Size()); - ASSERT_EQ(Kind::kInRegister, - dex_register_map.GetLocationKind(0, number_of_dex_registers, code_info)); - ASSERT_EQ(Kind::kInFpuRegister, - dex_register_map.GetLocationKind(1, number_of_dex_registers, code_info)); - ASSERT_EQ(Kind::kInRegister, - dex_register_map.GetLocationInternalKind(0, number_of_dex_registers, code_info)); - ASSERT_EQ(Kind::kInFpuRegister, - dex_register_map.GetLocationInternalKind(1, number_of_dex_registers, code_info)); - ASSERT_EQ(18, dex_register_map.GetMachineRegister(0, number_of_dex_registers, code_info)); - ASSERT_EQ(3, dex_register_map.GetMachineRegister(1, number_of_dex_registers, code_info)); + ASSERT_EQ(Kind::kInRegister, dex_register_map.GetLocationKind( + 0, number_of_dex_registers, code_info, encoding)); + ASSERT_EQ(Kind::kInFpuRegister, dex_register_map.GetLocationKind( + 1, number_of_dex_registers, code_info, encoding)); + ASSERT_EQ(Kind::kInRegister, dex_register_map.GetLocationInternalKind( + 0, number_of_dex_registers, code_info, encoding)); + ASSERT_EQ(Kind::kInFpuRegister, dex_register_map.GetLocationInternalKind( + 1, number_of_dex_registers, code_info, encoding)); + ASSERT_EQ(18, dex_register_map.GetMachineRegister( + 0, number_of_dex_registers, code_info, encoding)); + ASSERT_EQ(3, dex_register_map.GetMachineRegister( + 1, number_of_dex_registers, code_info, encoding)); size_t index0 = dex_register_map.GetLocationCatalogEntryIndex( 0, number_of_dex_registers, number_of_location_catalog_entries); @@ -272,7 +278,7 @@ TEST(StackMapTest, Test2) { ASSERT_EQ(18, location0.GetValue()); ASSERT_EQ(3, location1.GetValue()); - ASSERT_FALSE(stack_map.HasInlineInfo(code_info)); + ASSERT_FALSE(stack_map.HasInlineInfo(encoding)); } } @@ -294,28 +300,29 @@ TEST(StackMapTest, TestNonLiveDexRegisters) { stream.FillIn(region); CodeInfo code_info(region); - ASSERT_EQ(0u, code_info.GetStackMaskSize()); + StackMapEncoding encoding = code_info.ExtractEncoding(); + ASSERT_EQ(0u, encoding.NumberOfBytesForStackMask()); ASSERT_EQ(1u, code_info.GetNumberOfStackMaps()); uint32_t number_of_location_catalog_entries = code_info.GetNumberOfDexRegisterLocationCatalogEntries(); ASSERT_EQ(1u, number_of_location_catalog_entries); - DexRegisterLocationCatalog location_catalog = code_info.GetDexRegisterLocationCatalog(); + DexRegisterLocationCatalog location_catalog = code_info.GetDexRegisterLocationCatalog(encoding); // The Dex register location catalog contains: // - one 5-byte large Dex register location. size_t expected_location_catalog_size = 5u; ASSERT_EQ(expected_location_catalog_size, location_catalog.Size()); - StackMap stack_map = code_info.GetStackMapAt(0); - ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0))); - ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64))); - ASSERT_EQ(0u, stack_map.GetDexPc(code_info)); - ASSERT_EQ(64u, stack_map.GetNativePcOffset(code_info)); - ASSERT_EQ(0x3u, stack_map.GetRegisterMask(code_info)); + StackMap stack_map = code_info.GetStackMapAt(0, encoding); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0, encoding))); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64, encoding))); + ASSERT_EQ(0u, stack_map.GetDexPc(encoding)); + ASSERT_EQ(64u, stack_map.GetNativePcOffset(encoding)); + ASSERT_EQ(0x3u, stack_map.GetRegisterMask(encoding)); - ASSERT_TRUE(stack_map.HasDexRegisterMap(code_info)); + ASSERT_TRUE(stack_map.HasDexRegisterMap(encoding)); DexRegisterMap dex_register_map = - code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers); + code_info.GetDexRegisterMapOf(stack_map, encoding, number_of_dex_registers); ASSERT_FALSE(dex_register_map.IsDexRegisterLive(0)); ASSERT_TRUE(dex_register_map.IsDexRegisterLive(1)); ASSERT_EQ(1u, dex_register_map.GetNumberOfLiveDexRegisters(number_of_dex_registers)); @@ -325,15 +332,15 @@ TEST(StackMapTest, TestNonLiveDexRegisters) { size_t expected_dex_register_map_size = 1u + 0u; ASSERT_EQ(expected_dex_register_map_size, dex_register_map.Size()); - ASSERT_EQ(Kind::kNone, - dex_register_map.GetLocationKind(0, number_of_dex_registers, code_info)); - ASSERT_EQ(Kind::kConstant, - dex_register_map.GetLocationKind(1, number_of_dex_registers, code_info)); - ASSERT_EQ(Kind::kNone, - dex_register_map.GetLocationInternalKind(0, number_of_dex_registers, code_info)); - ASSERT_EQ(Kind::kConstantLargeValue, - dex_register_map.GetLocationInternalKind(1, number_of_dex_registers, code_info)); - ASSERT_EQ(-2, dex_register_map.GetConstant(1, number_of_dex_registers, code_info)); + ASSERT_EQ(Kind::kNone, dex_register_map.GetLocationKind( + 0, number_of_dex_registers, code_info, encoding)); + ASSERT_EQ(Kind::kConstant, dex_register_map.GetLocationKind( + 1, number_of_dex_registers, code_info, encoding)); + ASSERT_EQ(Kind::kNone, dex_register_map.GetLocationInternalKind( + 0, number_of_dex_registers, code_info, encoding)); + ASSERT_EQ(Kind::kConstantLargeValue, dex_register_map.GetLocationInternalKind( + 1, number_of_dex_registers, code_info, encoding)); + ASSERT_EQ(-2, dex_register_map.GetConstant(1, number_of_dex_registers, code_info, encoding)); size_t index0 = dex_register_map.GetLocationCatalogEntryIndex( 0, number_of_dex_registers, number_of_location_catalog_entries); @@ -350,7 +357,7 @@ TEST(StackMapTest, TestNonLiveDexRegisters) { ASSERT_EQ(0, location0.GetValue()); ASSERT_EQ(-2, location1.GetValue()); - ASSERT_FALSE(stack_map.HasInlineInfo(code_info)); + ASSERT_FALSE(stack_map.HasInlineInfo(encoding)); } // Generate a stack map whose dex register offset is @@ -387,6 +394,7 @@ TEST(StackMapTest, DexRegisterMapOffsetOverflow) { stream.FillIn(region); CodeInfo code_info(region); + StackMapEncoding encoding = code_info.ExtractEncoding(); // The location catalog contains two entries (DexRegisterLocation(kConstant, 0) // and DexRegisterLocation(kConstant, 1)), therefore the location catalog index // has a size of 1 bit. @@ -402,20 +410,20 @@ TEST(StackMapTest, DexRegisterMapOffsetOverflow) { // locations (that is, 127 bytes of data). // Hence it has a size of 255 bytes, and therefore... ASSERT_EQ(128u, DexRegisterMap::GetLiveBitMaskSize(number_of_dex_registers)); - StackMap stack_map0 = code_info.GetStackMapAt(0); + StackMap stack_map0 = code_info.GetStackMapAt(0, encoding); DexRegisterMap dex_register_map0 = - code_info.GetDexRegisterMapOf(stack_map0, number_of_dex_registers); + code_info.GetDexRegisterMapOf(stack_map0, encoding, number_of_dex_registers); ASSERT_EQ(127u, dex_register_map0.GetLocationMappingDataSize(number_of_dex_registers, number_of_location_catalog_entries)); ASSERT_EQ(255u, dex_register_map0.Size()); - StackMap stack_map1 = code_info.GetStackMapAt(1); - ASSERT_TRUE(stack_map1.HasDexRegisterMap(code_info)); + StackMap stack_map1 = code_info.GetStackMapAt(1, encoding); + ASSERT_TRUE(stack_map1.HasDexRegisterMap(encoding)); // ...the offset of the second Dex register map (relative to the // beginning of the Dex register maps region) is 255 (i.e., // kNoDexRegisterMapSmallEncoding). - ASSERT_NE(stack_map1.GetDexRegisterMapOffset(code_info), StackMap::kNoDexRegisterMap); - ASSERT_EQ(stack_map1.GetDexRegisterMapOffset(code_info), 0xFFu); + ASSERT_NE(stack_map1.GetDexRegisterMapOffset(encoding), StackMap::kNoDexRegisterMap); + ASSERT_EQ(stack_map1.GetDexRegisterMapOffset(encoding), 0xFFu); } TEST(StackMapTest, TestShareDexRegisterMap) { @@ -447,28 +455,30 @@ TEST(StackMapTest, TestShareDexRegisterMap) { stream.FillIn(region); CodeInfo ci(region); + StackMapEncoding encoding = ci.ExtractEncoding(); + // Verify first stack map. - StackMap sm0 = ci.GetStackMapAt(0); - DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm0, number_of_dex_registers); - ASSERT_EQ(0, dex_registers0.GetMachineRegister(0, number_of_dex_registers, ci)); - ASSERT_EQ(-2, dex_registers0.GetConstant(1, number_of_dex_registers, ci)); + StackMap sm0 = ci.GetStackMapAt(0, encoding); + DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm0, encoding, number_of_dex_registers); + ASSERT_EQ(0, dex_registers0.GetMachineRegister(0, number_of_dex_registers, ci, encoding)); + ASSERT_EQ(-2, dex_registers0.GetConstant(1, number_of_dex_registers, ci, encoding)); // Verify second stack map. - StackMap sm1 = ci.GetStackMapAt(1); - DexRegisterMap dex_registers1 = ci.GetDexRegisterMapOf(sm1, number_of_dex_registers); - ASSERT_EQ(0, dex_registers1.GetMachineRegister(0, number_of_dex_registers, ci)); - ASSERT_EQ(-2, dex_registers1.GetConstant(1, number_of_dex_registers, ci)); + StackMap sm1 = ci.GetStackMapAt(1, encoding); + DexRegisterMap dex_registers1 = ci.GetDexRegisterMapOf(sm1, encoding, number_of_dex_registers); + ASSERT_EQ(0, dex_registers1.GetMachineRegister(0, number_of_dex_registers, ci, encoding)); + ASSERT_EQ(-2, dex_registers1.GetConstant(1, number_of_dex_registers, ci, encoding)); // Verify third stack map. - StackMap sm2 = ci.GetStackMapAt(2); - DexRegisterMap dex_registers2 = ci.GetDexRegisterMapOf(sm2, number_of_dex_registers); - ASSERT_EQ(2, dex_registers2.GetMachineRegister(0, number_of_dex_registers, ci)); - ASSERT_EQ(-2, dex_registers2.GetConstant(1, number_of_dex_registers, ci)); + StackMap sm2 = ci.GetStackMapAt(2, encoding); + DexRegisterMap dex_registers2 = ci.GetDexRegisterMapOf(sm2, encoding, number_of_dex_registers); + ASSERT_EQ(2, dex_registers2.GetMachineRegister(0, number_of_dex_registers, ci, encoding)); + ASSERT_EQ(-2, dex_registers2.GetConstant(1, number_of_dex_registers, ci, encoding)); // Verify dex register map offsets. - ASSERT_EQ(sm0.GetDexRegisterMapOffset(ci), sm1.GetDexRegisterMapOffset(ci)); - ASSERT_NE(sm0.GetDexRegisterMapOffset(ci), sm2.GetDexRegisterMapOffset(ci)); - ASSERT_NE(sm1.GetDexRegisterMapOffset(ci), sm2.GetDexRegisterMapOffset(ci)); + ASSERT_EQ(sm0.GetDexRegisterMapOffset(encoding), sm1.GetDexRegisterMapOffset(encoding)); + ASSERT_NE(sm0.GetDexRegisterMapOffset(encoding), sm2.GetDexRegisterMapOffset(encoding)); + ASSERT_NE(sm1.GetDexRegisterMapOffset(encoding), sm2.GetDexRegisterMapOffset(encoding)); } TEST(StackMapTest, TestNoDexRegisterMap) { @@ -487,24 +497,25 @@ TEST(StackMapTest, TestNoDexRegisterMap) { stream.FillIn(region); CodeInfo code_info(region); - ASSERT_EQ(0u, code_info.GetStackMaskSize()); + StackMapEncoding encoding = code_info.ExtractEncoding(); + ASSERT_EQ(0u, encoding.NumberOfBytesForStackMask()); ASSERT_EQ(1u, code_info.GetNumberOfStackMaps()); uint32_t number_of_location_catalog_entries = code_info.GetNumberOfDexRegisterLocationCatalogEntries(); ASSERT_EQ(0u, number_of_location_catalog_entries); - DexRegisterLocationCatalog location_catalog = code_info.GetDexRegisterLocationCatalog(); + DexRegisterLocationCatalog location_catalog = code_info.GetDexRegisterLocationCatalog(encoding); ASSERT_EQ(0u, location_catalog.Size()); - StackMap stack_map = code_info.GetStackMapAt(0); - ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0))); - ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64))); - ASSERT_EQ(0u, stack_map.GetDexPc(code_info)); - ASSERT_EQ(64u, stack_map.GetNativePcOffset(code_info)); - ASSERT_EQ(0x3u, stack_map.GetRegisterMask(code_info)); + StackMap stack_map = code_info.GetStackMapAt(0, encoding); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0, encoding))); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64, encoding))); + ASSERT_EQ(0u, stack_map.GetDexPc(encoding)); + ASSERT_EQ(64u, stack_map.GetNativePcOffset(encoding)); + ASSERT_EQ(0x3u, stack_map.GetRegisterMask(encoding)); - ASSERT_FALSE(stack_map.HasDexRegisterMap(code_info)); - ASSERT_FALSE(stack_map.HasInlineInfo(code_info)); + ASSERT_FALSE(stack_map.HasDexRegisterMap(encoding)); + ASSERT_FALSE(stack_map.HasInlineInfo(encoding)); } TEST(StackMapTest, InlineTest) { @@ -579,16 +590,17 @@ TEST(StackMapTest, InlineTest) { stream.FillIn(region); CodeInfo ci(region); + StackMapEncoding encoding = ci.ExtractEncoding(); { // Verify first stack map. - StackMap sm0 = ci.GetStackMapAt(0); + StackMap sm0 = ci.GetStackMapAt(0, encoding); - DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm0, 2); - ASSERT_EQ(0, dex_registers0.GetStackOffsetInBytes(0, 2, ci)); - ASSERT_EQ(4, dex_registers0.GetConstant(1, 2, ci)); + DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm0, encoding, 2); + ASSERT_EQ(0, dex_registers0.GetStackOffsetInBytes(0, 2, ci, encoding)); + ASSERT_EQ(4, dex_registers0.GetConstant(1, 2, ci, encoding)); - InlineInfo if0 = ci.GetInlineInfoOf(sm0); + InlineInfo if0 = ci.GetInlineInfoOf(sm0, encoding); ASSERT_EQ(2u, if0.GetDepth()); ASSERT_EQ(2u, if0.GetDexPcAtDepth(0)); ASSERT_EQ(42u, if0.GetMethodIndexAtDepth(0)); @@ -597,24 +609,24 @@ TEST(StackMapTest, InlineTest) { ASSERT_EQ(82u, if0.GetMethodIndexAtDepth(1)); ASSERT_EQ(kStatic, if0.GetInvokeTypeAtDepth(1)); - DexRegisterMap dex_registers1 = ci.GetDexRegisterMapAtDepth(0, if0, 1); - ASSERT_EQ(8, dex_registers1.GetStackOffsetInBytes(0, 1, ci)); + DexRegisterMap dex_registers1 = ci.GetDexRegisterMapAtDepth(0, if0, encoding, 1); + ASSERT_EQ(8, dex_registers1.GetStackOffsetInBytes(0, 1, ci, encoding)); - DexRegisterMap dex_registers2 = ci.GetDexRegisterMapAtDepth(1, if0, 3); - ASSERT_EQ(16, dex_registers2.GetStackOffsetInBytes(0, 3, ci)); - ASSERT_EQ(20, dex_registers2.GetConstant(1, 3, ci)); - ASSERT_EQ(15, dex_registers2.GetMachineRegister(2, 3, ci)); + DexRegisterMap dex_registers2 = ci.GetDexRegisterMapAtDepth(1, if0, encoding, 3); + ASSERT_EQ(16, dex_registers2.GetStackOffsetInBytes(0, 3, ci, encoding)); + ASSERT_EQ(20, dex_registers2.GetConstant(1, 3, ci, encoding)); + ASSERT_EQ(15, dex_registers2.GetMachineRegister(2, 3, ci, encoding)); } { // Verify second stack map. - StackMap sm1 = ci.GetStackMapAt(1); + StackMap sm1 = ci.GetStackMapAt(1, encoding); - DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm1, 2); - ASSERT_EQ(56, dex_registers0.GetStackOffsetInBytes(0, 2, ci)); - ASSERT_EQ(0, dex_registers0.GetConstant(1, 2, ci)); + DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm1, encoding, 2); + ASSERT_EQ(56, dex_registers0.GetStackOffsetInBytes(0, 2, ci, encoding)); + ASSERT_EQ(0, dex_registers0.GetConstant(1, 2, ci, encoding)); - InlineInfo if1 = ci.GetInlineInfoOf(sm1); + InlineInfo if1 = ci.GetInlineInfoOf(sm1, encoding); ASSERT_EQ(3u, if1.GetDepth()); ASSERT_EQ(2u, if1.GetDexPcAtDepth(0)); ASSERT_EQ(42u, if1.GetMethodIndexAtDepth(0)); @@ -626,36 +638,36 @@ TEST(StackMapTest, InlineTest) { ASSERT_EQ(52u, if1.GetMethodIndexAtDepth(2)); ASSERT_EQ(kVirtual, if1.GetInvokeTypeAtDepth(2)); - DexRegisterMap dex_registers1 = ci.GetDexRegisterMapAtDepth(0, if1, 1); - ASSERT_EQ(12, dex_registers1.GetStackOffsetInBytes(0, 1, ci)); + DexRegisterMap dex_registers1 = ci.GetDexRegisterMapAtDepth(0, if1, encoding, 1); + ASSERT_EQ(12, dex_registers1.GetStackOffsetInBytes(0, 1, ci, encoding)); - DexRegisterMap dex_registers2 = ci.GetDexRegisterMapAtDepth(1, if1, 3); - ASSERT_EQ(80, dex_registers2.GetStackOffsetInBytes(0, 3, ci)); - ASSERT_EQ(10, dex_registers2.GetConstant(1, 3, ci)); - ASSERT_EQ(5, dex_registers2.GetMachineRegister(2, 3, ci)); + DexRegisterMap dex_registers2 = ci.GetDexRegisterMapAtDepth(1, if1, encoding, 3); + ASSERT_EQ(80, dex_registers2.GetStackOffsetInBytes(0, 3, ci, encoding)); + ASSERT_EQ(10, dex_registers2.GetConstant(1, 3, ci, encoding)); + ASSERT_EQ(5, dex_registers2.GetMachineRegister(2, 3, ci, encoding)); ASSERT_FALSE(if1.HasDexRegisterMapAtDepth(2)); } { // Verify third stack map. - StackMap sm2 = ci.GetStackMapAt(2); + StackMap sm2 = ci.GetStackMapAt(2, encoding); - DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm2, 2); + DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm2, encoding, 2); ASSERT_FALSE(dex_registers0.IsDexRegisterLive(0)); - ASSERT_EQ(4, dex_registers0.GetConstant(1, 2, ci)); - ASSERT_FALSE(sm2.HasInlineInfo(ci)); + ASSERT_EQ(4, dex_registers0.GetConstant(1, 2, ci, encoding)); + ASSERT_FALSE(sm2.HasInlineInfo(encoding)); } { // Verify fourth stack map. - StackMap sm3 = ci.GetStackMapAt(3); + StackMap sm3 = ci.GetStackMapAt(3, encoding); - DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm3, 2); - ASSERT_EQ(56, dex_registers0.GetStackOffsetInBytes(0, 2, ci)); - ASSERT_EQ(0, dex_registers0.GetConstant(1, 2, ci)); + DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm3, encoding, 2); + ASSERT_EQ(56, dex_registers0.GetStackOffsetInBytes(0, 2, ci, encoding)); + ASSERT_EQ(0, dex_registers0.GetConstant(1, 2, ci, encoding)); - InlineInfo if2 = ci.GetInlineInfoOf(sm3); + InlineInfo if2 = ci.GetInlineInfoOf(sm3, encoding); ASSERT_EQ(3u, if2.GetDepth()); ASSERT_EQ(2u, if2.GetDexPcAtDepth(0)); ASSERT_EQ(42u, if2.GetMethodIndexAtDepth(0)); @@ -669,12 +681,12 @@ TEST(StackMapTest, InlineTest) { ASSERT_FALSE(if2.HasDexRegisterMapAtDepth(0)); - DexRegisterMap dex_registers1 = ci.GetDexRegisterMapAtDepth(1, if2, 1); - ASSERT_EQ(2, dex_registers1.GetMachineRegister(0, 1, ci)); + DexRegisterMap dex_registers1 = ci.GetDexRegisterMapAtDepth(1, if2, encoding, 1); + ASSERT_EQ(2, dex_registers1.GetMachineRegister(0, 1, ci, encoding)); - DexRegisterMap dex_registers2 = ci.GetDexRegisterMapAtDepth(2, if2, 2); + DexRegisterMap dex_registers2 = ci.GetDexRegisterMapAtDepth(2, if2, encoding, 2); ASSERT_FALSE(dex_registers2.IsDexRegisterLive(0)); - ASSERT_EQ(3, dex_registers2.GetMachineRegister(1, 2, ci)); + ASSERT_EQ(3, dex_registers2.GetMachineRegister(1, 2, ci, encoding)); } } diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index e039d10583..96d5654d65 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -1346,9 +1346,11 @@ class OatDumper { const void* raw_code_info = oat_method.GetVmapTable(); if (raw_code_info != nullptr) { CodeInfo code_info(raw_code_info); - StackMap stack_map = code_info.GetStackMapForNativePcOffset(offset); + StackMapEncoding encoding = code_info.ExtractEncoding(); + StackMap stack_map = code_info.GetStackMapForNativePcOffset(offset, encoding); if (stack_map.IsValid()) { - stack_map.Dump(os, code_info, oat_method.GetCodeOffset(), code_item->registers_size_); + stack_map.Dump( + os, code_info, encoding, oat_method.GetCodeOffset(), code_item->registers_size_); } } } diff --git a/runtime/art_method.cc b/runtime/art_method.cc index 349013cfd0..fe26438eac 100644 --- a/runtime/art_method.cc +++ b/runtime/art_method.cc @@ -182,9 +182,10 @@ uint32_t ArtMethod::ToDexPc(const uintptr_t pc, bool abort_on_failure) { uint32_t sought_offset = pc - reinterpret_cast<uintptr_t>(entry_point); if (IsOptimized(sizeof(void*))) { CodeInfo code_info = GetOptimizedCodeInfo(); - StackMap stack_map = code_info.GetStackMapForNativePcOffset(sought_offset); + StackMapEncoding encoding = code_info.ExtractEncoding(); + StackMap stack_map = code_info.GetStackMapForNativePcOffset(sought_offset, encoding); if (stack_map.IsValid()) { - return stack_map.GetDexPc(code_info); + return stack_map.GetDexPc(encoding); } } else { MappingTable table(entry_point != nullptr ? diff --git a/runtime/asm_support.h b/runtime/asm_support.h index d7efe1c21e..10ed0f4dfa 100644 --- a/runtime/asm_support.h +++ b/runtime/asm_support.h @@ -104,11 +104,11 @@ ADD_TEST_EQ(THREAD_TOP_QUICK_FRAME_OFFSET, art::Thread::TopOfManagedStackOffset<__SIZEOF_POINTER__>().Int32Value()) // Offset of field Thread::tlsPtr_.managed_stack.top_quick_frame_. -#define THREAD_SELF_OFFSET (THREAD_CARD_TABLE_OFFSET + (8 * __SIZEOF_POINTER__)) +#define THREAD_SELF_OFFSET (THREAD_CARD_TABLE_OFFSET + (9 * __SIZEOF_POINTER__)) ADD_TEST_EQ(THREAD_SELF_OFFSET, art::Thread::SelfOffset<__SIZEOF_POINTER__>().Int32Value()) -#define THREAD_LOCAL_POS_OFFSET (THREAD_CARD_TABLE_OFFSET + 146 * __SIZEOF_POINTER__) +#define THREAD_LOCAL_POS_OFFSET (THREAD_CARD_TABLE_OFFSET + 147 * __SIZEOF_POINTER__) ADD_TEST_EQ(THREAD_LOCAL_POS_OFFSET, art::Thread::ThreadLocalPosOffset<__SIZEOF_POINTER__>().Int32Value()) #define THREAD_LOCAL_END_OFFSET (THREAD_LOCAL_POS_OFFSET + __SIZEOF_POINTER__) diff --git a/runtime/check_reference_map_visitor.h b/runtime/check_reference_map_visitor.h index d323379e4c..504b7536f6 100644 --- a/runtime/check_reference_map_visitor.h +++ b/runtime/check_reference_map_visitor.h @@ -65,17 +65,18 @@ class CheckReferenceMapVisitor : public StackVisitor { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { ArtMethod* m = GetMethod(); CodeInfo code_info = m->GetOptimizedCodeInfo(); - StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset); + StackMapEncoding encoding = code_info.ExtractEncoding(); + StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset, encoding); uint16_t number_of_dex_registers = m->GetCodeItem()->registers_size_; DexRegisterMap dex_register_map = - code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers); - MemoryRegion stack_mask = stack_map.GetStackMask(code_info); - uint32_t register_mask = stack_map.GetRegisterMask(code_info); + code_info.GetDexRegisterMapOf(stack_map, encoding, number_of_dex_registers); + MemoryRegion stack_mask = stack_map.GetStackMask(encoding); + uint32_t register_mask = stack_map.GetRegisterMask(encoding); for (int i = 0; i < number_of_references; ++i) { int reg = registers[i]; CHECK(reg < m->GetCodeItem()->registers_size_); - DexRegisterLocation location = - dex_register_map.GetDexRegisterLocation(reg, number_of_dex_registers, code_info); + DexRegisterLocation location = dex_register_map.GetDexRegisterLocation( + reg, number_of_dex_registers, code_info, encoding); switch (location.GetKind()) { case DexRegisterLocation::Kind::kNone: // Not set, should not be a reference. diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 431ef278ab..31140a83fc 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -277,9 +277,9 @@ ClassLinker::ClassLinker(InternTable* intern_table) quick_to_interpreter_bridge_trampoline_(nullptr), image_pointer_size_(sizeof(void*)) { CHECK(intern_table_ != nullptr); - for (auto& root : find_array_class_cache_) { - root = GcRoot<mirror::Class>(nullptr); - } + static_assert(kFindArrayCacheSize == arraysize(find_array_class_cache_), + "Array cache size wrong."); + std::fill_n(find_array_class_cache_, kFindArrayCacheSize, GcRoot<mirror::Class>(nullptr)); } void ClassLinker::InitWithoutImage(std::vector<std::unique_ptr<const DexFile>> boot_class_path) { @@ -5886,4 +5886,9 @@ ArtMethod* ClassLinker::CreateRuntimeMethod() { return method; } +void ClassLinker::DropFindArrayClassCache() { + std::fill_n(find_array_class_cache_, kFindArrayCacheSize, GcRoot<mirror::Class>(nullptr)); + find_array_class_cache_next_victim_ = 0; +} + } // namespace art diff --git a/runtime/class_linker.h b/runtime/class_linker.h index fa8b2e796b..d9935cbfda 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -480,6 +480,10 @@ class ClassLinker { ArtMethod* CreateRuntimeMethod(); + // Clear the ArrayClass cache. This is necessary when cleaning up for the image, as the cache + // entries are roots, but potentially not image classes. + void DropFindArrayClassCache() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + private: const OatFile::OatMethod FindOatMethodFor(ArtMethod* method, bool* found) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h index dbd24a276f..b0cbd02880 100644 --- a/runtime/entrypoints/entrypoint_utils-inl.h +++ b/runtime/entrypoints/entrypoint_utils-inl.h @@ -77,10 +77,11 @@ inline ArtMethod* GetCalleeSaveMethodCaller(ArtMethod** sp, (reinterpret_cast<uint8_t*>(sp) + callee_return_pc_offset)); uintptr_t native_pc_offset = outer_method->NativeQuickPcOffset(caller_pc); CodeInfo code_info = outer_method->GetOptimizedCodeInfo(); - StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset); + StackMapEncoding encoding = code_info.ExtractEncoding(); + StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset, encoding); DCHECK(stack_map.IsValid()); - if (stack_map.HasInlineInfo(code_info)) { - InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map); + if (stack_map.HasInlineInfo(encoding)) { + InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map, encoding); uint32_t method_index = inline_info.GetMethodIndexAtDepth(inline_info.GetDepth() - 1); InvokeType invoke_type = static_cast<InvokeType>( inline_info.GetInvokeTypeAtDepth(inline_info.GetDepth() - 1)); diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index 042c33f7ff..cc83db1e59 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -314,13 +314,14 @@ class QuickArgumentVisitor { if (outer_method->IsOptimized(sizeof(void*))) { CodeInfo code_info = outer_method->GetOptimizedCodeInfo(); - StackMap stack_map = code_info.GetStackMapForNativePcOffset(outer_pc_offset); + StackMapEncoding encoding = code_info.ExtractEncoding(); + StackMap stack_map = code_info.GetStackMapForNativePcOffset(outer_pc_offset, encoding); DCHECK(stack_map.IsValid()); - if (stack_map.HasInlineInfo(code_info)) { - InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map); + if (stack_map.HasInlineInfo(encoding)) { + InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map, encoding); return inline_info.GetDexPcAtDepth(inline_info.GetDepth() - 1); } else { - return stack_map.GetDexPc(code_info); + return stack_map.GetDexPc(encoding); } } else { return outer_method->ToDexPc(outer_pc); diff --git a/runtime/entrypoints_order_test.cc b/runtime/entrypoints_order_test.cc index 482f656fa6..963dd0265f 100644 --- a/runtime/entrypoints_order_test.cc +++ b/runtime/entrypoints_order_test.cc @@ -88,7 +88,8 @@ class EntrypointsOrderTest : public CommonRuntimeTest { EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, stack_end, managed_stack, sizeof(void*)); EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, managed_stack, suspend_trigger, sizeof(ManagedStack)); EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, suspend_trigger, jni_env, sizeof(void*)); - EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, jni_env, self, sizeof(void*)); + EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, jni_env, tmp_jni_env, sizeof(void*)); + EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, tmp_jni_env, self, sizeof(void*)); EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, self, opeer, sizeof(void*)); EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, opeer, jpeer, sizeof(void*)); EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, jpeer, stack_begin, sizeof(void*)); diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index 59d0259f2b..20e791d9f2 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -2253,6 +2253,7 @@ void Heap::PreZygoteFork() { // to large objects. mod_union_table->SetCards(); AddModUnionTable(mod_union_table); + large_object_space_->SetAllLargeObjectsAsZygoteObjects(self); if (collector::SemiSpace::kUseRememberedSet) { // Add a new remembered set for the post-zygote non-moving space. accounting::RememberedSet* post_zygote_non_moving_space_rem_set = diff --git a/runtime/gc/space/large_object_space.cc b/runtime/gc/space/large_object_space.cc index da4a930f15..52192e212c 100644 --- a/runtime/gc/space/large_object_space.cc +++ b/runtime/gc/space/large_object_space.cc @@ -41,13 +41,13 @@ class ValgrindLargeObjectMapSpace FINAL : public LargeObjectMapSpace { // Keep valgrind happy if there is any large objects such as dex cache arrays which aren't // freed since they are held live by the class linker. MutexLock mu(Thread::Current(), lock_); - for (auto& m : mem_maps_) { - delete m.second; + for (auto& m : large_objects_) { + delete m.second.mem_map; } } - virtual mirror::Object* Alloc(Thread* self, size_t num_bytes, size_t* bytes_allocated, - size_t* usable_size, size_t* bytes_tl_bulk_allocated) + mirror::Object* Alloc(Thread* self, size_t num_bytes, size_t* bytes_allocated, + size_t* usable_size, size_t* bytes_tl_bulk_allocated) OVERRIDE { mirror::Object* obj = LargeObjectMapSpace::Alloc(self, num_bytes + kValgrindRedZoneBytes * 2, bytes_allocated, @@ -63,26 +63,35 @@ class ValgrindLargeObjectMapSpace FINAL : public LargeObjectMapSpace { return object_without_rdz; } - virtual size_t AllocationSize(mirror::Object* obj, size_t* usable_size) OVERRIDE { - mirror::Object* object_with_rdz = reinterpret_cast<mirror::Object*>( - reinterpret_cast<uintptr_t>(obj) - kValgrindRedZoneBytes); - return LargeObjectMapSpace::AllocationSize(object_with_rdz, usable_size); + size_t AllocationSize(mirror::Object* obj, size_t* usable_size) OVERRIDE { + return LargeObjectMapSpace::AllocationSize(ObjectWithRedzone(obj), usable_size); } - virtual size_t Free(Thread* self, mirror::Object* obj) OVERRIDE { - mirror::Object* object_with_rdz = reinterpret_cast<mirror::Object*>( - reinterpret_cast<uintptr_t>(obj) - kValgrindRedZoneBytes); + bool IsZygoteLargeObject(Thread* self, mirror::Object* obj) const OVERRIDE { + return LargeObjectMapSpace::IsZygoteLargeObject(self, ObjectWithRedzone(obj)); + } + + size_t Free(Thread* self, mirror::Object* obj) OVERRIDE { + mirror::Object* object_with_rdz = ObjectWithRedzone(obj); VALGRIND_MAKE_MEM_UNDEFINED(object_with_rdz, AllocationSize(obj, nullptr)); return LargeObjectMapSpace::Free(self, object_with_rdz); } bool Contains(const mirror::Object* obj) const OVERRIDE { - mirror::Object* object_with_rdz = reinterpret_cast<mirror::Object*>( - reinterpret_cast<uintptr_t>(obj) - kValgrindRedZoneBytes); - return LargeObjectMapSpace::Contains(object_with_rdz); + return LargeObjectMapSpace::Contains(ObjectWithRedzone(obj)); } private: + static const mirror::Object* ObjectWithRedzone(const mirror::Object* obj) { + return reinterpret_cast<const mirror::Object*>( + reinterpret_cast<uintptr_t>(obj) - kValgrindRedZoneBytes); + } + + static mirror::Object* ObjectWithRedzone(mirror::Object* obj) { + return reinterpret_cast<mirror::Object*>( + reinterpret_cast<uintptr_t>(obj) - kValgrindRedZoneBytes); + } + static constexpr size_t kValgrindRedZoneBytes = kPageSize; }; @@ -139,8 +148,7 @@ mirror::Object* LargeObjectMapSpace::Alloc(Thread* self, size_t num_bytes, CHECK(space_bitmap == nullptr) << obj_end << " overlaps with bitmap " << *space_bitmap; } MutexLock mu(self, lock_); - large_objects_.push_back(obj); - mem_maps_.Put(obj, mem_map); + large_objects_.Put(obj, LargeObject {mem_map, false /* not zygote */}); const size_t allocation_size = mem_map->BaseSize(); DCHECK(bytes_allocated != nullptr); begin_ = std::min(begin_, reinterpret_cast<uint8_t*>(obj)); @@ -161,28 +169,43 @@ mirror::Object* LargeObjectMapSpace::Alloc(Thread* self, size_t num_bytes, return obj; } +bool LargeObjectMapSpace::IsZygoteLargeObject(Thread* self, mirror::Object* obj) const { + MutexLock mu(self, lock_); + auto it = large_objects_.find(obj); + CHECK(it != large_objects_.end()); + return it->second.is_zygote; +} + +void LargeObjectMapSpace::SetAllLargeObjectsAsZygoteObjects(Thread* self) { + MutexLock mu(self, lock_); + for (auto& pair : large_objects_) { + pair.second.is_zygote = true; + } +} + size_t LargeObjectMapSpace::Free(Thread* self, mirror::Object* ptr) { MutexLock mu(self, lock_); - MemMaps::iterator found = mem_maps_.find(ptr); - if (UNLIKELY(found == mem_maps_.end())) { - Runtime::Current()->GetHeap()->DumpSpaces(LOG(ERROR)); + auto it = large_objects_.find(ptr); + if (UNLIKELY(it == large_objects_.end())) { + Runtime::Current()->GetHeap()->DumpSpaces(LOG(INTERNAL_FATAL)); LOG(FATAL) << "Attempted to free large object " << ptr << " which was not live"; } - const size_t map_size = found->second->BaseSize(); + MemMap* mem_map = it->second.mem_map; + const size_t map_size = mem_map->BaseSize(); DCHECK_GE(num_bytes_allocated_, map_size); size_t allocation_size = map_size; num_bytes_allocated_ -= allocation_size; --num_objects_allocated_; - delete found->second; - mem_maps_.erase(found); + delete mem_map; + large_objects_.erase(it); return allocation_size; } size_t LargeObjectMapSpace::AllocationSize(mirror::Object* obj, size_t* usable_size) { MutexLock mu(Thread::Current(), lock_); - auto found = mem_maps_.find(obj); - CHECK(found != mem_maps_.end()) << "Attempted to get size of a large object which is not live"; - size_t alloc_size = found->second->BaseSize(); + auto it = large_objects_.find(obj); + CHECK(it != large_objects_.end()) << "Attempted to get size of a large object which is not live"; + size_t alloc_size = it->second.mem_map->BaseSize(); if (usable_size != nullptr) { *usable_size = alloc_size; } @@ -202,8 +225,8 @@ size_t LargeObjectSpace::FreeList(Thread* self, size_t num_ptrs, mirror::Object* void LargeObjectMapSpace::Walk(DlMallocSpace::WalkCallback callback, void* arg) { MutexLock mu(Thread::Current(), lock_); - for (auto it = mem_maps_.begin(); it != mem_maps_.end(); ++it) { - MemMap* mem_map = it->second; + for (auto& pair : large_objects_) { + MemMap* mem_map = pair.second.mem_map; callback(mem_map->Begin(), mem_map->End(), mem_map->Size(), arg); callback(nullptr, nullptr, 0, arg); } @@ -213,22 +236,25 @@ bool LargeObjectMapSpace::Contains(const mirror::Object* obj) const { Thread* self = Thread::Current(); if (lock_.IsExclusiveHeld(self)) { // We hold lock_ so do the check. - return mem_maps_.find(const_cast<mirror::Object*>(obj)) != mem_maps_.end(); + return large_objects_.find(const_cast<mirror::Object*>(obj)) != large_objects_.end(); } else { MutexLock mu(self, lock_); - return mem_maps_.find(const_cast<mirror::Object*>(obj)) != mem_maps_.end(); + return large_objects_.find(const_cast<mirror::Object*>(obj)) != large_objects_.end(); } } // Keeps track of allocation sizes + whether or not the previous allocation is free. -// Used to coalesce free blocks and find the best fit block for an allocation. +// Used to coalesce free blocks and find the best fit block for an allocation for best fit object +// allocation. Each allocation has an AllocationInfo which contains the size of the previous free +// block preceding it. Implemented in such a way that we can also find the iterator for any +// allocation info pointer. class AllocationInfo { public: AllocationInfo() : prev_free_(0), alloc_size_(0) { } // Return the number of pages that the allocation info covers. size_t AlignSize() const { - return alloc_size_ & ~kFlagFree; + return alloc_size_ & kFlagsMask; } // Returns the allocation size in bytes. size_t ByteSize() const { @@ -236,12 +262,23 @@ class AllocationInfo { } // Updates the allocation size and whether or not it is free. void SetByteSize(size_t size, bool free) { + DCHECK_EQ(size & ~kFlagsMask, 0u); DCHECK_ALIGNED(size, FreeListSpace::kAlignment); - alloc_size_ = (size / FreeListSpace::kAlignment) | (free ? kFlagFree : 0U); + alloc_size_ = (size / FreeListSpace::kAlignment) | (free ? kFlagFree : 0u); } + // Returns true if the block is free. bool IsFree() const { return (alloc_size_ & kFlagFree) != 0; } + // Return true if the large object is a zygote object. + bool IsZygoteObject() const { + return (alloc_size_ & kFlagZygote) != 0; + } + // Change the object to be a zygote object. + void SetZygoteObject() { + alloc_size_ |= kFlagZygote; + } + // Return true if this is a zygote large object. // Finds and returns the next non free allocation info after ourself. AllocationInfo* GetNextInfo() { return this + AlignSize(); @@ -275,10 +312,9 @@ class AllocationInfo { } private: - // Used to implement best fit object allocation. Each allocation has an AllocationInfo which - // contains the size of the previous free block preceding it. Implemented in such a way that we - // can also find the iterator for any allocation info pointer. - static constexpr uint32_t kFlagFree = 0x8000000; + static constexpr uint32_t kFlagFree = 0x80000000; // If block is free. + static constexpr uint32_t kFlagZygote = 0x40000000; // If the large object is a zygote object. + static constexpr uint32_t kFlagsMask = ~(kFlagFree | kFlagZygote); // Combined flags for masking. // Contains the size of the previous free block with kAlignment as the unit. If 0 then the // allocation before us is not free. // These variables are undefined in the middle of allocations / free blocks. @@ -493,7 +529,7 @@ mirror::Object* FreeListSpace::Alloc(Thread* self, size_t num_bytes, size_t* byt } void FreeListSpace::Dump(std::ostream& os) const { - MutexLock mu(Thread::Current(), const_cast<Mutex&>(lock_)); + MutexLock mu(Thread::Current(), lock_); os << GetName() << " -" << " begin: " << reinterpret_cast<void*>(Begin()) << " end: " << reinterpret_cast<void*>(End()) << "\n"; @@ -519,6 +555,24 @@ void FreeListSpace::Dump(std::ostream& os) const { } } +bool FreeListSpace::IsZygoteLargeObject(Thread* self ATTRIBUTE_UNUSED, mirror::Object* obj) const { + const AllocationInfo* info = GetAllocationInfoForAddress(reinterpret_cast<uintptr_t>(obj)); + DCHECK(info != nullptr); + return info->IsZygoteObject(); +} + +void FreeListSpace::SetAllLargeObjectsAsZygoteObjects(Thread* self) { + MutexLock mu(self, lock_); + uintptr_t free_end_start = reinterpret_cast<uintptr_t>(end_) - free_end_; + for (AllocationInfo* cur_info = GetAllocationInfoForAddress(reinterpret_cast<uintptr_t>(Begin())), + *end_info = GetAllocationInfoForAddress(free_end_start); cur_info < end_info; + cur_info = cur_info->GetNextInfo()) { + if (!cur_info->IsFree()) { + cur_info->SetZygoteObject(); + } + } +} + void LargeObjectSpace::SweepCallback(size_t num_ptrs, mirror::Object** ptrs, void* arg) { SweepCallbackContext* context = static_cast<SweepCallbackContext*>(arg); space::LargeObjectSpace* space = context->space->AsLargeObjectSpace(); diff --git a/runtime/gc/space/large_object_space.h b/runtime/gc/space/large_object_space.h index d1f9386d09..45ed0cd75f 100644 --- a/runtime/gc/space/large_object_space.h +++ b/runtime/gc/space/large_object_space.h @@ -98,6 +98,12 @@ class LargeObjectSpace : public DiscontinuousSpace, public AllocSpace { void LogFragmentationAllocFailure(std::ostream& os, size_t failed_alloc_bytes) OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Return true if the large object is a zygote large object. Potentially slow. + virtual bool IsZygoteLargeObject(Thread* self, mirror::Object* obj) const = 0; + // Called when we create the zygote space, mark all existing large objects as zygote large + // objects. + virtual void SetAllLargeObjectsAsZygoteObjects(Thread* self) = 0; + protected: explicit LargeObjectSpace(const std::string& name, uint8_t* begin, uint8_t* end); static void SweepCallback(size_t num_ptrs, mirror::Object** ptrs, void* arg); @@ -133,16 +139,20 @@ class LargeObjectMapSpace : public LargeObjectSpace { bool Contains(const mirror::Object* obj) const NO_THREAD_SAFETY_ANALYSIS; protected: + struct LargeObject { + MemMap* mem_map; + bool is_zygote; + }; explicit LargeObjectMapSpace(const std::string& name); virtual ~LargeObjectMapSpace() {} + bool IsZygoteLargeObject(Thread* self, mirror::Object* obj) const OVERRIDE LOCKS_EXCLUDED(lock_); + void SetAllLargeObjectsAsZygoteObjects(Thread* self) OVERRIDE LOCKS_EXCLUDED(lock_); + // Used to ensure mutual exclusion when the allocation spaces data structures are being modified. mutable Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; - std::vector<mirror::Object*, TrackingAllocator<mirror::Object*, kAllocatorTagLOS>> large_objects_ + AllocationTrackingSafeMap<mirror::Object*, LargeObject, kAllocatorTagLOSMaps> large_objects_ GUARDED_BY(lock_); - typedef SafeMap<mirror::Object*, MemMap*, std::less<mirror::Object*>, - TrackingAllocator<std::pair<mirror::Object*, MemMap*>, kAllocatorTagLOSMaps>> MemMaps; - MemMaps mem_maps_ GUARDED_BY(lock_); }; // A continuous large object space with a free-list to handle holes. @@ -177,6 +187,8 @@ class FreeListSpace FINAL : public LargeObjectSpace { } // Removes header from the free blocks set by finding the corresponding iterator and erasing it. void RemoveFreePrev(AllocationInfo* info) EXCLUSIVE_LOCKS_REQUIRED(lock_); + bool IsZygoteLargeObject(Thread* self, mirror::Object* obj) const OVERRIDE; + void SetAllLargeObjectsAsZygoteObjects(Thread* self) OVERRIDE; class SortByPrevFree { public: @@ -192,7 +204,7 @@ class FreeListSpace FINAL : public LargeObjectSpace { std::unique_ptr<MemMap> allocation_info_map_; AllocationInfo* allocation_info_; - Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; + mutable Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; // Free bytes at the end of the space. size_t free_end_ GUARDED_BY(lock_); FreeBlocks free_blocks_ GUARDED_BY(lock_); diff --git a/runtime/gc/space/large_object_space_test.cc b/runtime/gc/space/large_object_space_test.cc index f04a7ca4b2..05b484af07 100644 --- a/runtime/gc/space/large_object_space_test.cc +++ b/runtime/gc/space/large_object_space_test.cc @@ -34,6 +34,7 @@ class LargeObjectSpaceTest : public SpaceTest { void LargeObjectSpaceTest::LargeObjectTest() { size_t rand_seed = 0; + Thread* const self = Thread::Current(); for (size_t i = 0; i < 2; ++i) { LargeObjectSpace* los = nullptr; if (i == 0) { @@ -51,8 +52,8 @@ void LargeObjectSpaceTest::LargeObjectTest() { size_t request_size = test_rand(&rand_seed) % max_allocation_size; size_t allocation_size = 0; size_t bytes_tl_bulk_allocated; - mirror::Object* obj = los->Alloc(Thread::Current(), request_size, &allocation_size, - nullptr, &bytes_tl_bulk_allocated); + mirror::Object* obj = los->Alloc(self, request_size, &allocation_size, nullptr, + &bytes_tl_bulk_allocated); ASSERT_TRUE(obj != nullptr); ASSERT_EQ(allocation_size, los->AllocationSize(obj, nullptr)); ASSERT_GE(allocation_size, request_size); @@ -70,8 +71,21 @@ void LargeObjectSpaceTest::LargeObjectTest() { } } + // Check the zygote flag for the first phase. + if (phase == 0) { + for (const auto& pair : requests) { + mirror::Object* obj = pair.first; + ASSERT_FALSE(los->IsZygoteLargeObject(self, obj)); + } + los->SetAllLargeObjectsAsZygoteObjects(self); + for (const auto& pair : requests) { + mirror::Object* obj = pair.first; + ASSERT_TRUE(los->IsZygoteLargeObject(self, obj)); + } + } + // Free 1 / 2 the allocations the first phase, and all the second phase. - size_t limit = !phase ? requests.size() / 2 : 0; + size_t limit = phase == 0 ? requests.size() / 2 : 0; while (requests.size() > limit) { mirror::Object* obj = requests.back().first; size_t request_size = requests.back().second; @@ -88,7 +102,7 @@ void LargeObjectSpaceTest::LargeObjectTest() { size_t bytes_allocated = 0, bytes_tl_bulk_allocated; // Checks that the coalescing works. - mirror::Object* obj = los->Alloc(Thread::Current(), 100 * MB, &bytes_allocated, nullptr, + mirror::Object* obj = los->Alloc(self, 100 * MB, &bytes_allocated, nullptr, &bytes_tl_bulk_allocated); EXPECT_TRUE(obj != nullptr); los->Free(Thread::Current(), obj); diff --git a/runtime/hprof/hprof.cc b/runtime/hprof/hprof.cc index 917fe4360f..6e0e56e82a 100644 --- a/runtime/hprof/hprof.cc +++ b/runtime/hprof/hprof.cc @@ -891,8 +891,8 @@ void Hprof::DumpHeapObject(mirror::Object* obj) { return; } - gc::space::ContinuousSpace* space = - Runtime::Current()->GetHeap()->FindContinuousSpaceFromObject(obj, true); + gc::Heap* const heap = Runtime::Current()->GetHeap(); + const gc::space::ContinuousSpace* const space = heap->FindContinuousSpaceFromObject(obj, true); HprofHeapId heap_type = HPROF_HEAP_APP; if (space != nullptr) { if (space->IsZygoteSpace()) { @@ -900,6 +900,11 @@ void Hprof::DumpHeapObject(mirror::Object* obj) { } else if (space->IsImageSpace()) { heap_type = HPROF_HEAP_IMAGE; } + } else { + const auto* los = heap->GetLargeObjectsSpace(); + if (los->Contains(obj) && los->IsZygoteLargeObject(Thread::Current(), obj)) { + heap_type = HPROF_HEAP_ZYGOTE; + } } CheckHeapSegmentConstraints(); @@ -1040,7 +1045,7 @@ void Hprof::DumpHeapClass(mirror::Class* klass) { } // Instance fields for this class (no superclass fields) - int iFieldCount = klass->IsObjectClass() ? 0 : klass->NumInstanceFields(); + int iFieldCount = klass->NumInstanceFields(); if (klass->IsStringClass()) { __ AddU2((uint16_t)iFieldCount + 1); } else { @@ -1114,7 +1119,7 @@ void Hprof::DumpHeapInstanceObject(mirror::Object* obj, mirror::Class* klass) { // Write the instance data; fields for this class, followed by super class fields, // and so on. Don't write the klass or monitor fields of Object.class. mirror::Class* orig_klass = klass; - while (!klass->IsObjectClass()) { + do { int ifieldCount = klass->NumInstanceFields(); for (int i = 0; i < ifieldCount; ++i) { ArtField* f = klass->GetInstanceField(i); @@ -1146,7 +1151,7 @@ void Hprof::DumpHeapInstanceObject(mirror::Object* obj, mirror::Class* klass) { } klass = klass->GetSuperClass(); - } + } while (klass != nullptr); // Output native value character array for strings. if (orig_klass->IsStringClass()) { diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc index 6ab44551d0..cb36183977 100644 --- a/runtime/jni_internal.cc +++ b/runtime/jni_internal.cc @@ -406,7 +406,7 @@ class JNI { CHECK_NON_NULL_ARGUMENT(java_class); ScopedObjectAccess soa(env); mirror::Class* c = soa.Decode<mirror::Class*>(java_class); - return soa.AddLocalReference<jclass>(c->GetSuperClass()); + return soa.AddLocalReference<jclass>(c->IsInterface() ? nullptr : c->GetSuperClass()); } // Note: java_class1 should be safely castable to java_class2, and diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc index 99eb365094..2a0cb28f0c 100644 --- a/runtime/jni_internal_test.cc +++ b/runtime/jni_internal_test.cc @@ -1238,7 +1238,7 @@ TEST_F(JniInternalTest, GetSuperclass) { ASSERT_NE(runnable_interface, nullptr); ASSERT_TRUE(env_->IsSameObject(object_class, env_->GetSuperclass(string_class))); ASSERT_EQ(env_->GetSuperclass(object_class), nullptr); - ASSERT_TRUE(env_->IsSameObject(object_class, env_->GetSuperclass(runnable_interface))); + ASSERT_EQ(env_->GetSuperclass(runnable_interface), nullptr); // Null as class should fail. CheckJniAbortCatcher jni_abort_catcher; diff --git a/runtime/mem_map.cc b/runtime/mem_map.cc index cf4233c3d1..d8c1ec1508 100644 --- a/runtime/mem_map.cc +++ b/runtime/mem_map.cc @@ -221,7 +221,7 @@ static bool CheckMapRequest(uint8_t* expected_ptr, void* actual_ptr, size_t byte // We call this here so that we can try and generate a full error // message with the overlapping mapping. There's no guarantee that // that there will be an overlap though, since - // - The kernel is not *required* to honour expected_ptr unless MAP_FIXED is + // - The kernel is not *required* to honor expected_ptr unless MAP_FIXED is // true, even if there is no overlap // - There might have been an overlap at the point of mmap, but the // overlapping region has since been unmapped. diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h index ba8a693bdb..551e7e290a 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -1207,7 +1207,12 @@ class MANAGED Class FINAL : public Object { // Descriptor for the class such as "java.lang.Class" or "[C". Lazily initialized by ComputeName HeapReference<String> name_; - // The superclass, or null if this is java.lang.Object, an interface or primitive type. + // The superclass, or null if this is java.lang.Object or a primitive type. + // + // Note that interfaces have java.lang.Object as their + // superclass. This doesn't match the expectations in JNI + // GetSuperClass or java.lang.Class.getSuperClass() which need to + // check for interfaces and return null. HeapReference<Class> super_class_; // If class verify fails, we must return same error on subsequent tries. diff --git a/runtime/native/java_lang_VMClassLoader.cc b/runtime/native/java_lang_VMClassLoader.cc index 0c39f2b53e..15156301c8 100644 --- a/runtime/native/java_lang_VMClassLoader.cc +++ b/runtime/native/java_lang_VMClassLoader.cc @@ -55,60 +55,29 @@ static jclass VMClassLoader_findLoadedClass(JNIEnv* env, jclass, jobject javaLoa return nullptr; } -static jint VMClassLoader_getBootClassPathSize(JNIEnv*, jclass) { - return Runtime::Current()->GetClassLinker()->GetBootClassPath().size(); -} - /* - * Returns a string URL for a resource with the specified 'javaName' in - * entry 'index' of the boot class path. - * - * We return a newly-allocated String in the following form: - * - * jar:file://path!/name - * - * Where "path" is the bootstrap class path entry and "name" is the string - * passed into this method. "path" needs to be an absolute path (starting - * with '/'); if it's not we'd need to make it absolute as part of forming - * the URL string. + * Returns an array of entries from the boot classpath that could contain resources. */ -static jstring VMClassLoader_getBootClassPathResource(JNIEnv* env, jclass, jstring javaName, - jint index) { - ScopedUtfChars name(env, javaName); - if (name.c_str() == nullptr) { - return nullptr; - } - +static jobjectArray VMClassLoader_getBootClassPathEntries(JNIEnv* env, jclass) { const std::vector<const DexFile*>& path = Runtime::Current()->GetClassLinker()->GetBootClassPath(); - if (index < 0 || size_t(index) >= path.size()) { - return nullptr; - } - const DexFile* dex_file = path[index]; + jclass stringClass = env->FindClass("java/lang/String"); + jobjectArray array = env->NewObjectArray(path.size(), stringClass, nullptr); + for (size_t i = 0; i < path.size(); ++i) { + const DexFile* dex_file = path[i]; - // For multidex locations, e.g., x.jar:classes2.dex, we want to look into x.jar. - const std::string& location(dex_file->GetBaseLocation()); + // For multidex locations, e.g., x.jar:classes2.dex, we want to look into x.jar. + const std::string& location(dex_file->GetBaseLocation()); - std::string error_msg; - std::unique_ptr<ZipArchive> zip_archive(ZipArchive::Open(location.c_str(), &error_msg)); - if (zip_archive.get() == nullptr) { - LOG(WARNING) << "Failed to open zip archive '" << location << "': " << error_msg; - return nullptr; - } - std::unique_ptr<ZipEntry> zip_entry(zip_archive->Find(name.c_str(), &error_msg)); - if (zip_entry.get() == nullptr) { - return nullptr; + jstring javaPath = env->NewStringUTF(location.c_str()); + env->SetObjectArrayElement(array, i, javaPath); } - - std::string url; - StringAppendF(&url, "jar:file://%s!/%s", location.c_str(), name.c_str()); - return env->NewStringUTF(url.c_str()); + return array; } static JNINativeMethod gMethods[] = { NATIVE_METHOD(VMClassLoader, findLoadedClass, "!(Ljava/lang/ClassLoader;Ljava/lang/String;)Ljava/lang/Class;"), - NATIVE_METHOD(VMClassLoader, getBootClassPathResource, "(Ljava/lang/String;I)Ljava/lang/String;"), - NATIVE_METHOD(VMClassLoader, getBootClassPathSize, "!()I"), + NATIVE_METHOD(VMClassLoader, getBootClassPathEntries, "()[Ljava/lang/String;"), }; void register_java_lang_VMClassLoader(JNIEnv* env) { diff --git a/runtime/stack.cc b/runtime/stack.cc index 411088794a..5aeca98a88 100644 --- a/runtime/stack.cc +++ b/runtime/stack.cc @@ -113,9 +113,10 @@ InlineInfo StackVisitor::GetCurrentInlineInfo() const { ArtMethod* outer_method = *GetCurrentQuickFrame(); uint32_t native_pc_offset = outer_method->NativeQuickPcOffset(cur_quick_frame_pc_); CodeInfo code_info = outer_method->GetOptimizedCodeInfo(); - StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset); + StackMapEncoding encoding = code_info.ExtractEncoding(); + StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset, encoding); DCHECK(stack_map.IsValid()); - return code_info.GetInlineInfoOf(stack_map); + return code_info.GetInlineInfoOf(stack_map, encoding); } ArtMethod* StackVisitor::GetMethod() const { @@ -274,34 +275,40 @@ bool StackVisitor::GetVRegFromOptimizedCode(ArtMethod* m, uint16_t vreg, VRegKin const void* code_pointer = outer_method->GetQuickOatCodePointer(sizeof(void*)); DCHECK(code_pointer != nullptr); CodeInfo code_info = outer_method->GetOptimizedCodeInfo(); + StackMapEncoding encoding = code_info.ExtractEncoding(); uint32_t native_pc_offset = outer_method->NativeQuickPcOffset(cur_quick_frame_pc_); - StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset); + StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset, encoding); DCHECK(stack_map.IsValid()); size_t depth_in_stack_map = current_inlining_depth_ - 1; DexRegisterMap dex_register_map = IsInInlinedFrame() - ? code_info.GetDexRegisterMapAtDepth( - depth_in_stack_map, code_info.GetInlineInfoOf(stack_map), number_of_dex_registers) - : code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers); + ? code_info.GetDexRegisterMapAtDepth(depth_in_stack_map, + code_info.GetInlineInfoOf(stack_map, encoding), + encoding, + number_of_dex_registers) + : code_info.GetDexRegisterMapOf(stack_map, encoding, number_of_dex_registers); DexRegisterLocation::Kind location_kind = - dex_register_map.GetLocationKind(vreg, number_of_dex_registers, code_info); + dex_register_map.GetLocationKind(vreg, number_of_dex_registers, code_info, encoding); switch (location_kind) { case DexRegisterLocation::Kind::kInStack: { - const int32_t offset = - dex_register_map.GetStackOffsetInBytes(vreg, number_of_dex_registers, code_info); + const int32_t offset = dex_register_map.GetStackOffsetInBytes(vreg, + number_of_dex_registers, + code_info, + encoding); const uint8_t* addr = reinterpret_cast<const uint8_t*>(cur_quick_frame_) + offset; *val = *reinterpret_cast<const uint32_t*>(addr); return true; } case DexRegisterLocation::Kind::kInRegister: case DexRegisterLocation::Kind::kInFpuRegister: { - uint32_t reg = dex_register_map.GetMachineRegister(vreg, number_of_dex_registers, code_info); + uint32_t reg = + dex_register_map.GetMachineRegister(vreg, number_of_dex_registers, code_info, encoding); return GetRegisterIfAccessible(reg, kind, val); } case DexRegisterLocation::Kind::kConstant: - *val = dex_register_map.GetConstant(vreg, number_of_dex_registers, code_info); + *val = dex_register_map.GetConstant(vreg, number_of_dex_registers, code_info, encoding); return true; case DexRegisterLocation::Kind::kNone: return false; @@ -309,7 +316,10 @@ bool StackVisitor::GetVRegFromOptimizedCode(ArtMethod* m, uint16_t vreg, VRegKin LOG(FATAL) << "Unexpected location kind" << DexRegisterLocation::PrettyDescriptor( - dex_register_map.GetLocationInternalKind(vreg, number_of_dex_registers, code_info)); + dex_register_map.GetLocationInternalKind(vreg, + number_of_dex_registers, + code_info, + encoding)); UNREACHABLE(); } } @@ -782,10 +792,11 @@ void StackVisitor::WalkStack(bool include_transitions) { if ((walk_kind_ == StackWalkKind::kIncludeInlinedFrames) && method->IsOptimized(sizeof(void*))) { CodeInfo code_info = method->GetOptimizedCodeInfo(); + StackMapEncoding encoding = code_info.ExtractEncoding(); uint32_t native_pc_offset = method->NativeQuickPcOffset(cur_quick_frame_pc_); - StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset); - if (stack_map.IsValid() && stack_map.HasInlineInfo(code_info)) { - InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map); + StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset, encoding); + if (stack_map.IsValid() && stack_map.HasInlineInfo(encoding)) { + InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map, encoding); DCHECK_EQ(current_inlining_depth_, 0u); for (current_inlining_depth_ = inline_info.GetDepth(); current_inlining_depth_ != 0; diff --git a/runtime/stack_map.cc b/runtime/stack_map.cc index a64071ff05..f8fc2a9e90 100644 --- a/runtime/stack_map.cc +++ b/runtime/stack_map.cc @@ -28,9 +28,10 @@ constexpr uint32_t StackMap::kNoInlineInfo; DexRegisterLocation::Kind DexRegisterMap::GetLocationInternalKind(uint16_t dex_register_number, uint16_t number_of_dex_registers, - const CodeInfo& code_info) const { + const CodeInfo& code_info, + const StackMapEncoding& enc) const { DexRegisterLocationCatalog dex_register_location_catalog = - code_info.GetDexRegisterLocationCatalog(); + code_info.GetDexRegisterLocationCatalog(enc); size_t location_catalog_entry_index = GetLocationCatalogEntryIndex( dex_register_number, number_of_dex_registers, @@ -40,9 +41,10 @@ DexRegisterLocation::Kind DexRegisterMap::GetLocationInternalKind(uint16_t dex_r DexRegisterLocation DexRegisterMap::GetDexRegisterLocation(uint16_t dex_register_number, uint16_t number_of_dex_registers, - const CodeInfo& code_info) const { + const CodeInfo& code_info, + const StackMapEncoding& enc) const { DexRegisterLocationCatalog dex_register_location_catalog = - code_info.GetDexRegisterLocationCatalog(); + code_info.GetDexRegisterLocationCatalog(enc); size_t location_catalog_entry_index = GetLocationCatalogEntryIndex( dex_register_number, number_of_dex_registers, @@ -50,156 +52,43 @@ DexRegisterLocation DexRegisterMap::GetDexRegisterLocation(uint16_t dex_register return dex_register_location_catalog.GetDexRegisterLocation(location_catalog_entry_index); } -// Loads `number_of_bytes` at the given `offset` and assemble a uint32_t. If `check_max` is true, -// this method converts a maximum value of size `number_of_bytes` into a uint32_t 0xFFFFFFFF. -static uint32_t LoadAt(MemoryRegion region, - size_t number_of_bytes, - size_t offset, - bool check_max = false) { +uint32_t StackMap::LoadAt(size_t number_of_bytes, size_t offset, bool check_max) const { if (number_of_bytes == 0u) { DCHECK(!check_max); return 0; } else if (number_of_bytes == 1u) { - uint8_t value = region.LoadUnaligned<uint8_t>(offset); - if (check_max && value == 0xFF) { - return -1; - } else { - return value; - } + uint8_t value = region_.LoadUnaligned<uint8_t>(offset); + return (check_max && value == 0xFF) ? -1 : value; } else if (number_of_bytes == 2u) { - uint16_t value = region.LoadUnaligned<uint16_t>(offset); - if (check_max && value == 0xFFFF) { - return -1; - } else { - return value; - } + uint16_t value = region_.LoadUnaligned<uint16_t>(offset); + return (check_max && value == 0xFFFF) ? -1 : value; } else if (number_of_bytes == 3u) { - uint16_t low = region.LoadUnaligned<uint16_t>(offset); - uint16_t high = region.LoadUnaligned<uint8_t>(offset + sizeof(uint16_t)); + uint16_t low = region_.LoadUnaligned<uint16_t>(offset); + uint16_t high = region_.LoadUnaligned<uint8_t>(offset + sizeof(uint16_t)); uint32_t value = (high << 16) + low; - if (check_max && value == 0xFFFFFF) { - return -1; - } else { - return value; - } + return (check_max && value == 0xFFFFFF) ? -1 : value; } else { DCHECK_EQ(number_of_bytes, 4u); - return region.LoadUnaligned<uint32_t>(offset); + return region_.LoadUnaligned<uint32_t>(offset); } } -static void StoreAt(MemoryRegion region, size_t number_of_bytes, size_t offset, uint32_t value) { +void StackMap::StoreAt(size_t number_of_bytes, size_t offset, uint32_t value) const { if (number_of_bytes == 0u) { DCHECK_EQ(value, 0u); } else if (number_of_bytes == 1u) { - region.StoreUnaligned<uint8_t>(offset, value); + region_.StoreUnaligned<uint8_t>(offset, value); } else if (number_of_bytes == 2u) { - region.StoreUnaligned<uint16_t>(offset, value); + region_.StoreUnaligned<uint16_t>(offset, value); } else if (number_of_bytes == 3u) { - region.StoreUnaligned<uint16_t>(offset, Low16Bits(value)); - region.StoreUnaligned<uint8_t>(offset + sizeof(uint16_t), High16Bits(value)); + region_.StoreUnaligned<uint16_t>(offset, Low16Bits(value)); + region_.StoreUnaligned<uint8_t>(offset + sizeof(uint16_t), High16Bits(value)); } else { - region.StoreUnaligned<uint32_t>(offset, value); + region_.StoreUnaligned<uint32_t>(offset, value); DCHECK_EQ(number_of_bytes, 4u); } } -uint32_t StackMap::GetDexPc(const CodeInfo& info) const { - return LoadAt(region_, info.NumberOfBytesForDexPc(), info.ComputeStackMapDexPcOffset()); -} - -void StackMap::SetDexPc(const CodeInfo& info, uint32_t dex_pc) { - StoreAt(region_, info.NumberOfBytesForDexPc(), info.ComputeStackMapDexPcOffset(), dex_pc); -} - -uint32_t StackMap::GetNativePcOffset(const CodeInfo& info) const { - return LoadAt(region_, info.NumberOfBytesForNativePc(), info.ComputeStackMapNativePcOffset()); -} - -void StackMap::SetNativePcOffset(const CodeInfo& info, uint32_t native_pc_offset) { - StoreAt(region_, info.NumberOfBytesForNativePc(), info.ComputeStackMapNativePcOffset(), native_pc_offset); -} - -uint32_t StackMap::GetDexRegisterMapOffset(const CodeInfo& info) const { - return LoadAt(region_, - info.NumberOfBytesForDexRegisterMap(), - info.ComputeStackMapDexRegisterMapOffset(), - /* check_max */ true); -} - -void StackMap::SetDexRegisterMapOffset(const CodeInfo& info, uint32_t offset) { - StoreAt(region_, - info.NumberOfBytesForDexRegisterMap(), - info.ComputeStackMapDexRegisterMapOffset(), - offset); -} - -uint32_t StackMap::GetInlineDescriptorOffset(const CodeInfo& info) const { - if (!info.HasInlineInfo()) return kNoInlineInfo; - return LoadAt(region_, - info.NumberOfBytesForInlineInfo(), - info.ComputeStackMapInlineInfoOffset(), - /* check_max */ true); -} - -void StackMap::SetInlineDescriptorOffset(const CodeInfo& info, uint32_t offset) { - DCHECK(info.HasInlineInfo()); - StoreAt(region_, - info.NumberOfBytesForInlineInfo(), - info.ComputeStackMapInlineInfoOffset(), - offset); -} - -uint32_t StackMap::GetRegisterMask(const CodeInfo& info) const { - return LoadAt(region_, - info.NumberOfBytesForRegisterMask(), - info.ComputeStackMapRegisterMaskOffset()); -} - -void StackMap::SetRegisterMask(const CodeInfo& info, uint32_t mask) { - StoreAt(region_, - info.NumberOfBytesForRegisterMask(), - info.ComputeStackMapRegisterMaskOffset(), - mask); -} - -size_t StackMap::ComputeStackMapSizeInternal(size_t stack_mask_size, - size_t number_of_bytes_for_inline_info, - size_t number_of_bytes_for_dex_map, - size_t number_of_bytes_for_dex_pc, - size_t number_of_bytes_for_native_pc, - size_t number_of_bytes_for_register_mask) { - return stack_mask_size - + number_of_bytes_for_inline_info - + number_of_bytes_for_dex_map - + number_of_bytes_for_dex_pc - + number_of_bytes_for_native_pc - + number_of_bytes_for_register_mask; -} - -size_t StackMap::ComputeStackMapSize(size_t stack_mask_size, - size_t inline_info_size, - size_t dex_register_map_size, - size_t dex_pc_max, - size_t native_pc_max, - size_t register_mask_max) { - return ComputeStackMapSizeInternal( - stack_mask_size, - inline_info_size == 0 - ? 0 - // + 1 to also encode kNoInlineInfo. - : CodeInfo::EncodingSizeInBytes(inline_info_size + dex_register_map_size + 1), - // + 1 to also encode kNoDexRegisterMap. - CodeInfo::EncodingSizeInBytes(dex_register_map_size + 1), - CodeInfo::EncodingSizeInBytes(dex_pc_max), - CodeInfo::EncodingSizeInBytes(native_pc_max), - CodeInfo::EncodingSizeInBytes(register_mask_max)); -} - -MemoryRegion StackMap::GetStackMask(const CodeInfo& info) const { - return region_.Subregion(info.ComputeStackMapStackMaskOffset(), info.GetStackMaskSize()); -} - static void DumpRegisterMapping(std::ostream& os, size_t dex_register_num, DexRegisterLocation location, @@ -216,6 +105,7 @@ void CodeInfo::Dump(std::ostream& os, uint32_t code_offset, uint16_t number_of_dex_registers, bool dump_stack_maps) const { + StackMapEncoding encoding = ExtractEncoding(); uint32_t code_info_size = GetOverallSize(); size_t number_of_stack_maps = GetNumberOfStackMaps(); Indenter indent_filter(os.rdbuf(), kIndentChar, kIndentBy1Count); @@ -223,21 +113,26 @@ void CodeInfo::Dump(std::ostream& os, indented_os << "Optimized CodeInfo (size=" << code_info_size << ", number_of_dex_registers=" << number_of_dex_registers << ", number_of_stack_maps=" << number_of_stack_maps - << ", has_inline_info=" << HasInlineInfo() - << ", number_of_bytes_for_inline_info=" << NumberOfBytesForInlineInfo() - << ", number_of_bytes_for_dex_register_map=" << NumberOfBytesForDexRegisterMap() - << ", number_of_bytes_for_dex_pc=" << NumberOfBytesForDexPc() - << ", number_of_bytes_for_native_pc=" << NumberOfBytesForNativePc() - << ", number_of_bytes_for_register_mask=" << NumberOfBytesForRegisterMask() + << ", has_inline_info=" << encoding.HasInlineInfo() + << ", number_of_bytes_for_inline_info=" << encoding.NumberOfBytesForInlineInfo() + << ", number_of_bytes_for_dex_register_map=" + << encoding.NumberOfBytesForDexRegisterMap() + << ", number_of_bytes_for_dex_pc=" << encoding.NumberOfBytesForDexPc() + << ", number_of_bytes_for_native_pc=" << encoding.NumberOfBytesForNativePc() + << ", number_of_bytes_for_register_mask=" << encoding.NumberOfBytesForRegisterMask() << ")\n"; // Display the Dex register location catalog. - GetDexRegisterLocationCatalog().Dump(indented_os, *this); + GetDexRegisterLocationCatalog(encoding).Dump(indented_os, *this); // Display stack maps along with (live) Dex register maps. if (dump_stack_maps) { for (size_t i = 0; i < number_of_stack_maps; ++i) { - StackMap stack_map = GetStackMapAt(i); - stack_map.Dump( - indented_os, *this, code_offset, number_of_dex_registers, " " + std::to_string(i)); + StackMap stack_map = GetStackMapAt(i, encoding); + stack_map.Dump(indented_os, + *this, + encoding, + code_offset, + number_of_dex_registers, + " " + std::to_string(i)); } } // TODO: Dump the stack map's inline information? We need to know more from the caller: @@ -245,9 +140,10 @@ void CodeInfo::Dump(std::ostream& os, } void DexRegisterLocationCatalog::Dump(std::ostream& os, const CodeInfo& code_info) { + StackMapEncoding encoding = code_info.ExtractEncoding(); size_t number_of_location_catalog_entries = code_info.GetNumberOfDexRegisterLocationCatalogEntries(); - size_t location_catalog_size_in_bytes = code_info.GetDexRegisterLocationCatalogSize(); + size_t location_catalog_size_in_bytes = code_info.GetDexRegisterLocationCatalogSize(encoding); Indenter indent_filter(os.rdbuf(), kIndentChar, kIndentBy1Count); std::ostream indented_os(&indent_filter); indented_os @@ -262,6 +158,7 @@ void DexRegisterLocationCatalog::Dump(std::ostream& os, const CodeInfo& code_inf void DexRegisterMap::Dump(std::ostream& os, const CodeInfo& code_info, uint16_t number_of_dex_registers) const { + StackMapEncoding encoding = code_info.ExtractEncoding(); size_t number_of_location_catalog_entries = code_info.GetNumberOfDexRegisterLocationCatalogEntries(); Indenter indent_filter(os.rdbuf(), kIndentChar, kIndentBy1Count); @@ -271,7 +168,10 @@ void DexRegisterMap::Dump(std::ostream& os, if (IsDexRegisterLive(j)) { size_t location_catalog_entry_index = GetLocationCatalogEntryIndex( j, number_of_dex_registers, number_of_location_catalog_entries); - DexRegisterLocation location = GetDexRegisterLocation(j, number_of_dex_registers, code_info); + DexRegisterLocation location = GetDexRegisterLocation(j, + number_of_dex_registers, + code_info, + encoding); DumpRegisterMapping( indented_os, j, location, "v", "\t[entry " + std::to_string(static_cast<int>(location_catalog_entry_index)) + "]"); @@ -281,6 +181,7 @@ void DexRegisterMap::Dump(std::ostream& os, void StackMap::Dump(std::ostream& os, const CodeInfo& code_info, + const StackMapEncoding& encoding, uint32_t code_offset, uint16_t number_of_dex_registers, const std::string& header_suffix) const { @@ -288,21 +189,22 @@ void StackMap::Dump(std::ostream& os, std::ostream indented_os(&indent_filter); indented_os << "StackMap" << header_suffix << std::hex - << " [native_pc=0x" << code_offset + GetNativePcOffset(code_info) << "]" - << " (dex_pc=0x" << GetDexPc(code_info) - << ", native_pc_offset=0x" << GetNativePcOffset(code_info) - << ", dex_register_map_offset=0x" << GetDexRegisterMapOffset(code_info) - << ", inline_info_offset=0x" << GetInlineDescriptorOffset(code_info) - << ", register_mask=0x" << GetRegisterMask(code_info) + << " [native_pc=0x" << code_offset + GetNativePcOffset(encoding) << "]" + << " (dex_pc=0x" << GetDexPc(encoding) + << ", native_pc_offset=0x" << GetNativePcOffset(encoding) + << ", dex_register_map_offset=0x" << GetDexRegisterMapOffset(encoding) + << ", inline_info_offset=0x" << GetInlineDescriptorOffset(encoding) + << ", register_mask=0x" << GetRegisterMask(encoding) << std::dec << ", stack_mask=0b"; - MemoryRegion stack_mask = GetStackMask(code_info); + MemoryRegion stack_mask = GetStackMask(encoding); for (size_t i = 0, e = stack_mask.size_in_bits(); i < e; ++i) { indented_os << stack_mask.LoadBit(e - i - 1); } indented_os << ")\n"; - if (HasDexRegisterMap(code_info)) { - DexRegisterMap dex_register_map = code_info.GetDexRegisterMapOf(*this, number_of_dex_registers); + if (HasDexRegisterMap(encoding)) { + DexRegisterMap dex_register_map = code_info.GetDexRegisterMapOf( + *this, encoding, number_of_dex_registers); dex_register_map.Dump(os, code_info, number_of_dex_registers); } } @@ -321,8 +223,9 @@ void InlineInfo::Dump(std::ostream& os, << ", method_index=0x" << GetMethodIndexAtDepth(i) << ")\n"; if (HasDexRegisterMapAtDepth(i)) { + StackMapEncoding encoding = code_info.ExtractEncoding(); DexRegisterMap dex_register_map = - code_info.GetDexRegisterMapAtDepth(i, *this, number_of_dex_registers[i]); + code_info.GetDexRegisterMapAtDepth(i, *this, encoding, number_of_dex_registers[i]); dex_register_map.Dump(indented_os, code_info, number_of_dex_registers[i]); } } diff --git a/runtime/stack_map.h b/runtime/stack_map.h index 7f33e6d090..4e420084d1 100644 --- a/runtime/stack_map.h +++ b/runtime/stack_map.h @@ -32,6 +32,7 @@ static constexpr ssize_t kFrameSlotSize = 4; static constexpr size_t kVRegSize = 4; class CodeInfo; +class StackMapEncoding; /** * Classes in the following file are wrapper on stack map information backed @@ -437,26 +438,30 @@ class DexRegisterMap { // Get the surface kind of Dex register `dex_register_number`. DexRegisterLocation::Kind GetLocationKind(uint16_t dex_register_number, uint16_t number_of_dex_registers, - const CodeInfo& code_info) const { + const CodeInfo& code_info, + const StackMapEncoding& enc) const { return DexRegisterLocation::ConvertToSurfaceKind( - GetLocationInternalKind(dex_register_number, number_of_dex_registers, code_info)); + GetLocationInternalKind(dex_register_number, number_of_dex_registers, code_info, enc)); } // Get the internal kind of Dex register `dex_register_number`. DexRegisterLocation::Kind GetLocationInternalKind(uint16_t dex_register_number, uint16_t number_of_dex_registers, - const CodeInfo& code_info) const; + const CodeInfo& code_info, + const StackMapEncoding& enc) const; // Get the Dex register location `dex_register_number`. DexRegisterLocation GetDexRegisterLocation(uint16_t dex_register_number, uint16_t number_of_dex_registers, - const CodeInfo& code_info) const; + const CodeInfo& code_info, + const StackMapEncoding& enc) const; int32_t GetStackOffsetInBytes(uint16_t dex_register_number, uint16_t number_of_dex_registers, - const CodeInfo& code_info) const { + const CodeInfo& code_info, + const StackMapEncoding& enc) const { DexRegisterLocation location = - GetDexRegisterLocation(dex_register_number, number_of_dex_registers, code_info); + GetDexRegisterLocation(dex_register_number, number_of_dex_registers, code_info, enc); DCHECK(location.GetKind() == DexRegisterLocation::Kind::kInStack); // GetDexRegisterLocation returns the offset in bytes. return location.GetValue(); @@ -464,9 +469,10 @@ class DexRegisterMap { int32_t GetConstant(uint16_t dex_register_number, uint16_t number_of_dex_registers, - const CodeInfo& code_info) const { + const CodeInfo& code_info, + const StackMapEncoding& enc) const { DexRegisterLocation location = - GetDexRegisterLocation(dex_register_number, number_of_dex_registers, code_info); + GetDexRegisterLocation(dex_register_number, number_of_dex_registers, code_info, enc); DCHECK(location.GetKind() == DexRegisterLocation::Kind::kConstant) << DexRegisterLocation::PrettyDescriptor(location.GetKind()); return location.GetValue(); @@ -474,9 +480,10 @@ class DexRegisterMap { int32_t GetMachineRegister(uint16_t dex_register_number, uint16_t number_of_dex_registers, - const CodeInfo& code_info) const { + const CodeInfo& code_info, + const StackMapEncoding& enc) const { DexRegisterLocation location = - GetDexRegisterLocation(dex_register_number, number_of_dex_registers, code_info); + GetDexRegisterLocation(dex_register_number, number_of_dex_registers, code_info, enc); DCHECK(location.GetInternalKind() == DexRegisterLocation::Kind::kInRegister || location.GetInternalKind() == DexRegisterLocation::Kind::kInFpuRegister) << DexRegisterLocation::PrettyDescriptor(location.GetInternalKind()); @@ -628,6 +635,111 @@ class DexRegisterMap { friend class StackMapStream; }; +class StackMapEncoding { + public: + StackMapEncoding() {} + + StackMapEncoding(size_t stack_mask_size, + size_t bytes_for_inline_info, + size_t bytes_for_dex_register_map, + size_t bytes_for_dex_pc, + size_t bytes_for_native_pc, + size_t bytes_for_register_mask) + : bytes_for_stack_mask_(stack_mask_size), + bytes_for_inline_info_(bytes_for_inline_info), + bytes_for_dex_register_map_(bytes_for_dex_register_map), + bytes_for_dex_pc_(bytes_for_dex_pc), + bytes_for_native_pc_(bytes_for_native_pc), + bytes_for_register_mask_(bytes_for_register_mask) {} + + static StackMapEncoding CreateFromSizes(size_t stack_mask_size, + size_t inline_info_size, + size_t dex_register_map_size, + size_t dex_pc_max, + size_t native_pc_max, + size_t register_mask_max) { + return StackMapEncoding( + stack_mask_size, + // + 1 to also encode kNoInlineInfo: if an inline info offset + // is at 0xFF, we want to overflow to a larger encoding, because it will + // conflict with kNoInlineInfo. + // The offset is relative to the dex register map. TODO: Change this. + inline_info_size == 0 + ? 0 + : EncodingSizeInBytes(dex_register_map_size + inline_info_size + 1), + // + 1 to also encode kNoDexRegisterMap: if a dex register map offset + // is at 0xFF, we want to overflow to a larger encoding, because it will + // conflict with kNoDexRegisterMap. + EncodingSizeInBytes(dex_register_map_size + 1), + EncodingSizeInBytes(dex_pc_max), + EncodingSizeInBytes(native_pc_max), + EncodingSizeInBytes(register_mask_max)); + } + + // Get the size of one stack map of this CodeInfo object, in bytes. + // All stack maps of a CodeInfo have the same size. + size_t ComputeStackMapSize() const { + return bytes_for_register_mask_ + + bytes_for_stack_mask_ + + bytes_for_inline_info_ + + bytes_for_dex_register_map_ + + bytes_for_dex_pc_ + + bytes_for_native_pc_; + } + + bool HasInlineInfo() const { return bytes_for_inline_info_ > 0; } + + size_t NumberOfBytesForStackMask() const { return bytes_for_stack_mask_; } + size_t NumberOfBytesForInlineInfo() const { return bytes_for_inline_info_; } + size_t NumberOfBytesForDexRegisterMap() const { return bytes_for_dex_register_map_; } + size_t NumberOfBytesForDexPc() const { return bytes_for_dex_pc_; } + size_t NumberOfBytesForNativePc() const { return bytes_for_native_pc_; } + size_t NumberOfBytesForRegisterMask() const { return bytes_for_register_mask_; } + + size_t ComputeStackMapRegisterMaskOffset() const { + return kRegisterMaskOffset; + } + + size_t ComputeStackMapStackMaskOffset() const { + return ComputeStackMapRegisterMaskOffset() + bytes_for_register_mask_; + } + + size_t ComputeStackMapDexPcOffset() const { + return ComputeStackMapStackMaskOffset() + bytes_for_stack_mask_; + } + + size_t ComputeStackMapNativePcOffset() const { + return ComputeStackMapDexPcOffset() + bytes_for_dex_pc_; + } + + size_t ComputeStackMapDexRegisterMapOffset() const { + return ComputeStackMapNativePcOffset() + bytes_for_native_pc_; + } + + size_t ComputeStackMapInlineInfoOffset() const { + return ComputeStackMapDexRegisterMapOffset() + bytes_for_dex_register_map_; + } + + private: + static size_t EncodingSizeInBytes(size_t max_element) { + DCHECK(IsUint<32>(max_element)); + return (max_element == 0) ? 0 + : IsUint<8>(max_element) ? 1 + : IsUint<16>(max_element) ? 2 + : IsUint<24>(max_element) ? 3 + : 4; + } + + static constexpr int kRegisterMaskOffset = 0; + + size_t bytes_for_stack_mask_; + size_t bytes_for_inline_info_; + size_t bytes_for_dex_register_map_; + size_t bytes_for_dex_pc_; + size_t bytes_for_native_pc_; + size_t bytes_for_register_mask_; +}; + /** * A Stack Map holds compilation information for a specific PC necessary for: * - Mapping it to a dex PC, @@ -642,44 +754,82 @@ class DexRegisterMap { */ class StackMap { public: - explicit StackMap(MemoryRegion region) : region_(region) {} StackMap() {} + explicit StackMap(MemoryRegion region) : region_(region) {} bool IsValid() const { return region_.pointer() != nullptr; } - uint32_t GetDexPc(const CodeInfo& info) const; + uint32_t GetDexPc(const StackMapEncoding& encoding) const { + return LoadAt(encoding.NumberOfBytesForDexPc(), encoding.ComputeStackMapDexPcOffset()); + } - void SetDexPc(const CodeInfo& info, uint32_t dex_pc); + void SetDexPc(const StackMapEncoding& encoding, uint32_t dex_pc) { + StoreAt(encoding.NumberOfBytesForDexPc(), encoding.ComputeStackMapDexPcOffset(), dex_pc); + } - uint32_t GetNativePcOffset(const CodeInfo& info) const; + uint32_t GetNativePcOffset(const StackMapEncoding& encoding) const { + return LoadAt(encoding.NumberOfBytesForNativePc(), encoding.ComputeStackMapNativePcOffset()); + } - void SetNativePcOffset(const CodeInfo& info, uint32_t native_pc_offset); + void SetNativePcOffset(const StackMapEncoding& encoding, uint32_t native_pc_offset) { + StoreAt(encoding.NumberOfBytesForNativePc(), + encoding.ComputeStackMapNativePcOffset(), + native_pc_offset); + } - uint32_t GetDexRegisterMapOffset(const CodeInfo& info) const; + uint32_t GetDexRegisterMapOffset(const StackMapEncoding& encoding) const { + return LoadAt(encoding.NumberOfBytesForDexRegisterMap(), + encoding.ComputeStackMapDexRegisterMapOffset(), + /* check_max */ true); + } - void SetDexRegisterMapOffset(const CodeInfo& info, uint32_t offset); + void SetDexRegisterMapOffset(const StackMapEncoding& encoding, uint32_t offset) { + StoreAt(encoding.NumberOfBytesForDexRegisterMap(), + encoding.ComputeStackMapDexRegisterMapOffset(), + offset); + } - uint32_t GetInlineDescriptorOffset(const CodeInfo& info) const; + uint32_t GetInlineDescriptorOffset(const StackMapEncoding& encoding) const { + if (!encoding.HasInlineInfo()) return kNoInlineInfo; + return LoadAt(encoding.NumberOfBytesForInlineInfo(), + encoding.ComputeStackMapInlineInfoOffset(), + /* check_max */ true); + } - void SetInlineDescriptorOffset(const CodeInfo& info, uint32_t offset); + void SetInlineDescriptorOffset(const StackMapEncoding& encoding, uint32_t offset) { + DCHECK(encoding.HasInlineInfo()); + StoreAt(encoding.NumberOfBytesForInlineInfo(), + encoding.ComputeStackMapInlineInfoOffset(), + offset); + } - uint32_t GetRegisterMask(const CodeInfo& info) const; + uint32_t GetRegisterMask(const StackMapEncoding& encoding) const { + return LoadAt(encoding.NumberOfBytesForRegisterMask(), + encoding.ComputeStackMapRegisterMaskOffset()); + } - void SetRegisterMask(const CodeInfo& info, uint32_t mask); + void SetRegisterMask(const StackMapEncoding& encoding, uint32_t mask) { + StoreAt(encoding.NumberOfBytesForRegisterMask(), + encoding.ComputeStackMapRegisterMaskOffset(), + mask); + } - MemoryRegion GetStackMask(const CodeInfo& info) const; + MemoryRegion GetStackMask(const StackMapEncoding& encoding) const { + return region_.Subregion(encoding.ComputeStackMapStackMaskOffset(), + encoding.NumberOfBytesForStackMask()); + } - void SetStackMask(const CodeInfo& info, const BitVector& sp_map) { - MemoryRegion region = GetStackMask(info); + void SetStackMask(const StackMapEncoding& encoding, const BitVector& sp_map) { + MemoryRegion region = GetStackMask(encoding); sp_map.CopyTo(region.start(), region.size()); } - bool HasDexRegisterMap(const CodeInfo& info) const { - return GetDexRegisterMapOffset(info) != kNoDexRegisterMap; + bool HasDexRegisterMap(const StackMapEncoding& encoding) const { + return GetDexRegisterMapOffset(encoding) != kNoDexRegisterMap; } - bool HasInlineInfo(const CodeInfo& info) const { - return GetInlineDescriptorOffset(info) != kNoInlineInfo; + bool HasInlineInfo(const StackMapEncoding& encoding) const { + return GetInlineDescriptorOffset(encoding) != kNoInlineInfo; } bool Equals(const StackMap& other) const { @@ -687,15 +837,9 @@ class StackMap { && region_.size() == other.region_.size(); } - static size_t ComputeStackMapSize(size_t stack_mask_size, - size_t inline_info_size, - size_t dex_register_map_size, - size_t dex_pc_max, - size_t native_pc_max, - size_t register_mask_max); - void Dump(std::ostream& os, const CodeInfo& code_info, + const StackMapEncoding& encoding, uint32_t code_offset, uint16_t number_of_dex_registers, const std::string& header_suffix = "") const; @@ -709,21 +853,17 @@ class StackMap { static constexpr uint32_t kNoInlineInfo = -1; private: - static size_t ComputeStackMapSizeInternal(size_t stack_mask_size, - size_t number_of_bytes_for_inline_info, - size_t number_of_bytes_for_dex_map, - size_t number_of_bytes_for_dex_pc, - size_t number_of_bytes_for_native_pc, - size_t number_of_bytes_for_register_mask); - // TODO: Instead of plain types such as "uint32_t", introduce // typedefs (and document the memory layout of StackMap). - static constexpr int kRegisterMaskOffset = 0; static constexpr int kFixedSize = 0; + // Loads `number_of_bytes` at the given `offset` and assemble a uint32_t. If `check_max` is true, + // this method converts a maximum value of size `number_of_bytes` into a uint32_t 0xFFFFFFFF. + uint32_t LoadAt(size_t number_of_bytes, size_t offset, bool check_max = false) const; + void StoreAt(size_t number_of_bytes, size_t offset, uint32_t value) const; + MemoryRegion region_; - friend class CodeInfo; friend class StackMapStream; }; @@ -827,39 +967,23 @@ class CodeInfo { region_ = MemoryRegion(const_cast<void*>(data), size); } - static size_t EncodingSizeInBytes(size_t max_element) { - DCHECK(IsUint<32>(max_element)); - return (max_element == 0) ? 0 - : IsUint<8>(max_element) ? 1 - : IsUint<16>(max_element) ? 2 - : IsUint<24>(max_element) ? 3 - : 4; + StackMapEncoding ExtractEncoding() const { + return StackMapEncoding(region_.LoadUnaligned<uint32_t>(kStackMaskSizeOffset), + GetNumberOfBytesForEncoding(kInlineInfoBitOffset), + GetNumberOfBytesForEncoding(kDexRegisterMapBitOffset), + GetNumberOfBytesForEncoding(kDexPcBitOffset), + GetNumberOfBytesForEncoding(kNativePcBitOffset), + GetNumberOfBytesForEncoding(kRegisterMaskBitOffset)); } - void SetEncoding(size_t inline_info_size, - size_t dex_register_map_size, - size_t dex_pc_max, - size_t native_pc_max, - size_t register_mask_max) { - if (inline_info_size != 0) { - region_.StoreBit(kHasInlineInfoBitOffset, 1); - // + 1 to also encode kNoInlineInfo: if an inline info offset - // is at 0xFF, we want to overflow to a larger encoding, because it will - // conflict with kNoInlineInfo. - // The offset is relative to the dex register map. TODO: Change this. - SetEncodingAt(kInlineInfoBitOffset, - EncodingSizeInBytes(dex_register_map_size + inline_info_size + 1)); - } else { - region_.StoreBit(kHasInlineInfoBitOffset, 0); - SetEncodingAt(kInlineInfoBitOffset, 0); - } - // + 1 to also encode kNoDexRegisterMap: if a dex register map offset - // is at 0xFF, we want to overflow to a larger encoding, because it will - // conflict with kNoDexRegisterMap. - SetEncodingAt(kDexRegisterMapBitOffset, EncodingSizeInBytes(dex_register_map_size + 1)); - SetEncodingAt(kDexPcBitOffset, EncodingSizeInBytes(dex_pc_max)); - SetEncodingAt(kNativePcBitOffset, EncodingSizeInBytes(native_pc_max)); - SetEncodingAt(kRegisterMaskBitOffset, EncodingSizeInBytes(register_mask_max)); + void SetEncoding(const StackMapEncoding& encoding) { + region_.StoreUnaligned<uint32_t>(kStackMaskSizeOffset, encoding.NumberOfBytesForStackMask()); + region_.StoreBit(kHasInlineInfoBitOffset, encoding.NumberOfBytesForInlineInfo() != 0); + SetEncodingAt(kInlineInfoBitOffset, encoding.NumberOfBytesForInlineInfo()); + SetEncodingAt(kDexRegisterMapBitOffset, encoding.NumberOfBytesForDexRegisterMap()); + SetEncodingAt(kDexPcBitOffset, encoding.NumberOfBytesForDexPc()); + SetEncodingAt(kNativePcBitOffset, encoding.NumberOfBytesForNativePc()); + SetEncodingAt(kRegisterMaskBitOffset, encoding.NumberOfBytesForRegisterMask()); } void SetEncodingAt(size_t bit_offset, size_t number_of_bytes) { @@ -880,64 +1004,15 @@ class CodeInfo { return region_.LoadBit(kHasInlineInfoBitOffset); } - size_t NumberOfBytesForInlineInfo() const { - return GetNumberOfBytesForEncoding(kInlineInfoBitOffset); - } - - size_t NumberOfBytesForDexRegisterMap() const { - return GetNumberOfBytesForEncoding(kDexRegisterMapBitOffset); - } - - size_t NumberOfBytesForRegisterMask() const { - return GetNumberOfBytesForEncoding(kRegisterMaskBitOffset); - } - - size_t NumberOfBytesForNativePc() const { - return GetNumberOfBytesForEncoding(kNativePcBitOffset); - } - - size_t NumberOfBytesForDexPc() const { - return GetNumberOfBytesForEncoding(kDexPcBitOffset); - } - - size_t ComputeStackMapRegisterMaskOffset() const { - return StackMap::kRegisterMaskOffset; - } - - size_t ComputeStackMapStackMaskOffset() const { - return ComputeStackMapRegisterMaskOffset() - + (NumberOfBytesForRegisterMask() * sizeof(uint8_t)); - } - - size_t ComputeStackMapDexPcOffset() const { - return ComputeStackMapStackMaskOffset() + GetStackMaskSize(); - } - - size_t ComputeStackMapNativePcOffset() const { - return ComputeStackMapDexPcOffset() - + (NumberOfBytesForDexPc() * sizeof(uint8_t)); - } - - size_t ComputeStackMapDexRegisterMapOffset() const { - return ComputeStackMapNativePcOffset() - + (NumberOfBytesForNativePc() * sizeof(uint8_t)); - } - - size_t ComputeStackMapInlineInfoOffset() const { - CHECK(HasInlineInfo()); - return ComputeStackMapDexRegisterMapOffset() - + (NumberOfBytesForDexRegisterMap() * sizeof(uint8_t)); - } - - DexRegisterLocationCatalog GetDexRegisterLocationCatalog() const { + DexRegisterLocationCatalog GetDexRegisterLocationCatalog(const StackMapEncoding& encoding) const { return DexRegisterLocationCatalog(region_.Subregion( - GetDexRegisterLocationCatalogOffset(), - GetDexRegisterLocationCatalogSize())); + GetDexRegisterLocationCatalogOffset(encoding), + GetDexRegisterLocationCatalogSize(encoding))); } - StackMap GetStackMapAt(size_t i) const { - size_t size = StackMapSize(); - return StackMap(GetStackMaps().Subregion(i * size, size)); + StackMap GetStackMapAt(size_t i, const StackMapEncoding& encoding) const { + size_t stack_map_size = encoding.ComputeStackMapSize(); + return StackMap(GetStackMaps(encoding).Subregion(i * stack_map_size, stack_map_size)); } uint32_t GetOverallSize() const { @@ -956,19 +1031,11 @@ class CodeInfo { region_.StoreUnaligned<uint32_t>(kNumberOfDexRegisterLocationCatalogEntriesOffset, num_entries); } - uint32_t GetDexRegisterLocationCatalogSize() const { - return ComputeDexRegisterLocationCatalogSize(GetDexRegisterLocationCatalogOffset(), + uint32_t GetDexRegisterLocationCatalogSize(const StackMapEncoding& encoding) const { + return ComputeDexRegisterLocationCatalogSize(GetDexRegisterLocationCatalogOffset(encoding), GetNumberOfDexRegisterLocationCatalogEntries()); } - uint32_t GetStackMaskSize() const { - return region_.LoadUnaligned<uint32_t>(kStackMaskSizeOffset); - } - - void SetStackMaskSize(uint32_t size) { - region_.StoreUnaligned<uint32_t>(kStackMaskSizeOffset, size); - } - size_t GetNumberOfStackMaps() const { return region_.LoadUnaligned<uint32_t>(kNumberOfStackMapsOffset); } @@ -977,37 +1044,30 @@ class CodeInfo { region_.StoreUnaligned<uint32_t>(kNumberOfStackMapsOffset, number_of_stack_maps); } - // Get the size of one stack map of this CodeInfo object, in bytes. - // All stack maps of a CodeInfo have the same size. - size_t StackMapSize() const { - return StackMap::ComputeStackMapSizeInternal(GetStackMaskSize(), - NumberOfBytesForInlineInfo(), - NumberOfBytesForDexRegisterMap(), - NumberOfBytesForDexPc(), - NumberOfBytesForNativePc(), - NumberOfBytesForRegisterMask()); - } - // Get the size all the stack maps of this CodeInfo object, in bytes. - size_t GetStackMapsSize() const { - return StackMapSize() * GetNumberOfStackMaps(); + size_t GetStackMapsSize(const StackMapEncoding& encoding) const { + return encoding.ComputeStackMapSize() * GetNumberOfStackMaps(); } - uint32_t GetDexRegisterLocationCatalogOffset() const { - return GetStackMapsOffset() + GetStackMapsSize(); + uint32_t GetDexRegisterLocationCatalogOffset(const StackMapEncoding& encoding) const { + return GetStackMapsOffset() + GetStackMapsSize(encoding); } - size_t GetDexRegisterMapsOffset() const { - return GetDexRegisterLocationCatalogOffset() + GetDexRegisterLocationCatalogSize(); + size_t GetDexRegisterMapsOffset(const StackMapEncoding& encoding) const { + return GetDexRegisterLocationCatalogOffset(encoding) + + GetDexRegisterLocationCatalogSize(encoding); } uint32_t GetStackMapsOffset() const { return kFixedSize; } - DexRegisterMap GetDexRegisterMapOf(StackMap stack_map, uint32_t number_of_dex_registers) const { - DCHECK(stack_map.HasDexRegisterMap(*this)); - uint32_t offset = GetDexRegisterMapsOffset() + stack_map.GetDexRegisterMapOffset(*this); + DexRegisterMap GetDexRegisterMapOf(StackMap stack_map, + const StackMapEncoding& encoding, + uint32_t number_of_dex_registers) const { + DCHECK(stack_map.HasDexRegisterMap(encoding)); + uint32_t offset = GetDexRegisterMapsOffset(encoding) + + stack_map.GetDexRegisterMapOffset(encoding); size_t size = ComputeDexRegisterMapSizeOf(offset, number_of_dex_registers); return DexRegisterMap(region_.Subregion(offset, size)); } @@ -1015,37 +1075,40 @@ class CodeInfo { // Return the `DexRegisterMap` pointed by `inline_info` at depth `depth`. DexRegisterMap GetDexRegisterMapAtDepth(uint8_t depth, InlineInfo inline_info, + const StackMapEncoding& encoding, uint32_t number_of_dex_registers) const { DCHECK(inline_info.HasDexRegisterMapAtDepth(depth)); - uint32_t offset = - GetDexRegisterMapsOffset() + inline_info.GetDexRegisterMapOffsetAtDepth(depth); + uint32_t offset = GetDexRegisterMapsOffset(encoding) + + inline_info.GetDexRegisterMapOffsetAtDepth(depth); size_t size = ComputeDexRegisterMapSizeOf(offset, number_of_dex_registers); return DexRegisterMap(region_.Subregion(offset, size)); } - InlineInfo GetInlineInfoOf(StackMap stack_map) const { - DCHECK(stack_map.HasInlineInfo(*this)); - uint32_t offset = stack_map.GetInlineDescriptorOffset(*this) + GetDexRegisterMapsOffset(); + InlineInfo GetInlineInfoOf(StackMap stack_map, const StackMapEncoding& encoding) const { + DCHECK(stack_map.HasInlineInfo(encoding)); + uint32_t offset = stack_map.GetInlineDescriptorOffset(encoding) + + GetDexRegisterMapsOffset(encoding); uint8_t depth = region_.LoadUnaligned<uint8_t>(offset); return InlineInfo(region_.Subregion(offset, InlineInfo::kFixedSize + depth * InlineInfo::SingleEntrySize())); } - StackMap GetStackMapForDexPc(uint32_t dex_pc) const { + StackMap GetStackMapForDexPc(uint32_t dex_pc, const StackMapEncoding& encoding) const { for (size_t i = 0, e = GetNumberOfStackMaps(); i < e; ++i) { - StackMap stack_map = GetStackMapAt(i); - if (stack_map.GetDexPc(*this) == dex_pc) { + StackMap stack_map = GetStackMapAt(i, encoding); + if (stack_map.GetDexPc(encoding) == dex_pc) { return stack_map; } } return StackMap(); } - StackMap GetStackMapForNativePcOffset(uint32_t native_pc_offset) const { + StackMap GetStackMapForNativePcOffset(uint32_t native_pc_offset, + const StackMapEncoding& encoding) const { // TODO: stack maps are sorted by native pc, we can do a binary search. for (size_t i = 0, e = GetNumberOfStackMaps(); i < e; ++i) { - StackMap stack_map = GetStackMapAt(i); - if (stack_map.GetNativePcOffset(*this) == native_pc_offset) { + StackMap stack_map = GetStackMapAt(i, encoding); + if (stack_map.GetNativePcOffset(encoding) == native_pc_offset) { return stack_map; } } @@ -1081,10 +1144,10 @@ class CodeInfo { static constexpr int kNativePcBitOffset = kDexPcBitOffset + 3; static constexpr int kRegisterMaskBitOffset = kNativePcBitOffset + 3; - MemoryRegion GetStackMaps() const { + MemoryRegion GetStackMaps(const StackMapEncoding& encoding) const { return region_.size() == 0 ? MemoryRegion() - : region_.Subregion(GetStackMapsOffset(), GetStackMapsSize()); + : region_.Subregion(GetStackMapsOffset(), GetStackMapsSize(encoding)); } // Compute the size of the Dex register map associated to the stack map at diff --git a/runtime/thread.cc b/runtime/thread.cc index 65999f77b5..4203b96f24 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -196,7 +196,12 @@ void* Thread::CreateCallback(void* arg) { // Check that if we got here we cannot be shutting down (as shutdown should never have started // while threads are being born). CHECK(!runtime->IsShuttingDownLocked()); - CHECK(self->Init(runtime->GetThreadList(), runtime->GetJavaVM())); + // Note: given that the JNIEnv is created in the parent thread, the only failure point here is + // a mess in InitStackHwm. We do not have a reasonable way to recover from that, so abort + // the runtime in such a case. In case this ever changes, we need to make sure here to + // delete the tmp_jni_env, as we own it at this point. + CHECK(self->Init(runtime->GetThreadList(), runtime->GetJavaVM(), self->tlsPtr_.tmp_jni_env)); + self->tlsPtr_.tmp_jni_env = nullptr; Runtime::Current()->EndThreadBirth(); } { @@ -358,37 +363,59 @@ void Thread::CreateNativeThread(JNIEnv* env, jobject java_peer, size_t stack_siz env->SetLongField(java_peer, WellKnownClasses::java_lang_Thread_nativePeer, reinterpret_cast<jlong>(child_thread)); - pthread_t new_pthread; - pthread_attr_t attr; - CHECK_PTHREAD_CALL(pthread_attr_init, (&attr), "new thread"); - CHECK_PTHREAD_CALL(pthread_attr_setdetachstate, (&attr, PTHREAD_CREATE_DETACHED), "PTHREAD_CREATE_DETACHED"); - CHECK_PTHREAD_CALL(pthread_attr_setstacksize, (&attr, stack_size), stack_size); - int pthread_create_result = pthread_create(&new_pthread, &attr, Thread::CreateCallback, child_thread); - CHECK_PTHREAD_CALL(pthread_attr_destroy, (&attr), "new thread"); - - if (pthread_create_result != 0) { - // pthread_create(3) failed, so clean up. - { - MutexLock mu(self, *Locks::runtime_shutdown_lock_); - runtime->EndThreadBirth(); - } - // Manually delete the global reference since Thread::Init will not have been run. - env->DeleteGlobalRef(child_thread->tlsPtr_.jpeer); - child_thread->tlsPtr_.jpeer = nullptr; - delete child_thread; - child_thread = nullptr; - // TODO: remove from thread group? - env->SetLongField(java_peer, WellKnownClasses::java_lang_Thread_nativePeer, 0); - { - std::string msg(StringPrintf("pthread_create (%s stack) failed: %s", - PrettySize(stack_size).c_str(), strerror(pthread_create_result))); - ScopedObjectAccess soa(env); - soa.Self()->ThrowOutOfMemoryError(msg.c_str()); + // Try to allocate a JNIEnvExt for the thread. We do this here as we might be out of memory and + // do not have a good way to report this on the child's side. + std::unique_ptr<JNIEnvExt> child_jni_env_ext( + JNIEnvExt::Create(child_thread, Runtime::Current()->GetJavaVM())); + + int pthread_create_result = 0; + if (child_jni_env_ext.get() != nullptr) { + pthread_t new_pthread; + pthread_attr_t attr; + child_thread->tlsPtr_.tmp_jni_env = child_jni_env_ext.get(); + CHECK_PTHREAD_CALL(pthread_attr_init, (&attr), "new thread"); + CHECK_PTHREAD_CALL(pthread_attr_setdetachstate, (&attr, PTHREAD_CREATE_DETACHED), + "PTHREAD_CREATE_DETACHED"); + CHECK_PTHREAD_CALL(pthread_attr_setstacksize, (&attr, stack_size), stack_size); + pthread_create_result = pthread_create(&new_pthread, + &attr, + Thread::CreateCallback, + child_thread); + CHECK_PTHREAD_CALL(pthread_attr_destroy, (&attr), "new thread"); + + if (pthread_create_result == 0) { + // pthread_create started the new thread. The child is now responsible for managing the + // JNIEnvExt we created. + // Note: we can't check for tmp_jni_env == nullptr, as that would require synchronization + // between the threads. + child_jni_env_ext.release(); + return; } } + + // Either JNIEnvExt::Create or pthread_create(3) failed, so clean up. + { + MutexLock mu(self, *Locks::runtime_shutdown_lock_); + runtime->EndThreadBirth(); + } + // Manually delete the global reference since Thread::Init will not have been run. + env->DeleteGlobalRef(child_thread->tlsPtr_.jpeer); + child_thread->tlsPtr_.jpeer = nullptr; + delete child_thread; + child_thread = nullptr; + // TODO: remove from thread group? + env->SetLongField(java_peer, WellKnownClasses::java_lang_Thread_nativePeer, 0); + { + std::string msg(child_jni_env_ext.get() == nullptr ? + "Could not allocate JNI Env" : + StringPrintf("pthread_create (%s stack) failed: %s", + PrettySize(stack_size).c_str(), strerror(pthread_create_result))); + ScopedObjectAccess soa(env); + soa.Self()->ThrowOutOfMemoryError(msg.c_str()); + } } -bool Thread::Init(ThreadList* thread_list, JavaVMExt* java_vm) { +bool Thread::Init(ThreadList* thread_list, JavaVMExt* java_vm, JNIEnvExt* jni_env_ext) { // This function does all the initialization that must be run by the native thread it applies to. // (When we create a new thread from managed code, we allocate the Thread* in Thread::Create so // we can handshake with the corresponding native thread when it's ready.) Check this native @@ -415,9 +442,15 @@ bool Thread::Init(ThreadList* thread_list, JavaVMExt* java_vm) { tls32_.thin_lock_thread_id = thread_list->AllocThreadId(this); - tlsPtr_.jni_env = JNIEnvExt::Create(this, java_vm); - if (tlsPtr_.jni_env == nullptr) { - return false; + if (jni_env_ext != nullptr) { + DCHECK_EQ(jni_env_ext->vm, java_vm); + DCHECK_EQ(jni_env_ext->self, this); + tlsPtr_.jni_env = jni_env_ext; + } else { + tlsPtr_.jni_env = JNIEnvExt::Create(this, java_vm); + if (tlsPtr_.jni_env == nullptr) { + return false; + } } thread_list->Register(this); @@ -2240,9 +2273,10 @@ class ReferenceMapVisitor : public StackVisitor { const void* entry_point = runtime->GetInstrumentation()->GetQuickCodeFor(m, sizeof(void*)); uintptr_t native_pc_offset = m->NativeQuickPcOffset(GetCurrentQuickFramePc(), entry_point); CodeInfo code_info = m->GetOptimizedCodeInfo(); - StackMap map = code_info.GetStackMapForNativePcOffset(native_pc_offset); + StackMapEncoding encoding = code_info.ExtractEncoding(); + StackMap map = code_info.GetStackMapForNativePcOffset(native_pc_offset, encoding); DCHECK(map.IsValid()); - MemoryRegion mask = map.GetStackMask(code_info); + MemoryRegion mask = map.GetStackMask(encoding); // Visit stack entries that hold pointers. for (size_t i = 0; i < mask.size_in_bits(); ++i) { if (mask.LoadBit(i)) { @@ -2258,7 +2292,7 @@ class ReferenceMapVisitor : public StackVisitor { } } // Visit callee-save registers that hold pointers. - uint32_t register_mask = map.GetRegisterMask(code_info); + uint32_t register_mask = map.GetRegisterMask(encoding); for (size_t i = 0; i < BitSizeOf<uint32_t>(); ++i) { if (register_mask & (1 << i)) { mirror::Object** ref_addr = reinterpret_cast<mirror::Object**>(GetGPRAddress(i)); diff --git a/runtime/thread.h b/runtime/thread.h index 8c2e215ff8..3f0d0a59a9 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -975,7 +975,15 @@ class Thread { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void RemoveFromThreadGroup(ScopedObjectAccess& soa) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - bool Init(ThreadList*, JavaVMExt*) EXCLUSIVE_LOCKS_REQUIRED(Locks::runtime_shutdown_lock_); + // Initialize a thread. + // + // The third parameter is not mandatory. If given, the thread will use this JNIEnvExt. In case + // Init succeeds, this means the thread takes ownership of it. If Init fails, it is the caller's + // responsibility to destroy the given JNIEnvExt. If the parameter is null, Init will try to + // create a JNIEnvExt on its own (and potentially fail at that stage, indicated by a return value + // of false). + bool Init(ThreadList*, JavaVMExt*, JNIEnvExt* jni_env_ext = nullptr) + EXCLUSIVE_LOCKS_REQUIRED(Locks::runtime_shutdown_lock_); void InitCardTable(); void InitCpu(); void CleanupCpu(); @@ -1111,8 +1119,8 @@ class Thread { struct PACKED(4) tls_ptr_sized_values { tls_ptr_sized_values() : card_table(nullptr), exception(nullptr), stack_end(nullptr), - managed_stack(), suspend_trigger(nullptr), jni_env(nullptr), self(nullptr), opeer(nullptr), - jpeer(nullptr), stack_begin(nullptr), stack_size(0), + managed_stack(), suspend_trigger(nullptr), jni_env(nullptr), tmp_jni_env(nullptr), + self(nullptr), opeer(nullptr), jpeer(nullptr), stack_begin(nullptr), stack_size(0), stack_trace_sample(nullptr), wait_next(nullptr), monitor_enter_object(nullptr), top_handle_scope(nullptr), class_loader_override(nullptr), long_jump_context(nullptr), instrumentation_stack(nullptr), debug_invoke_req(nullptr), single_step_control(nullptr), @@ -1144,6 +1152,10 @@ class Thread { // Every thread may have an associated JNI environment JNIEnvExt* jni_env; + // Temporary storage to transfer a pre-allocated JNIEnvExt from the creating thread to the + // created thread. + JNIEnvExt* tmp_jni_env; + // Initialized to "this". On certain architectures (such as x86) reading off of Thread::Current // is easy but getting the address of Thread::Current is hard. This field can be read off of // Thread::Current to give the address. diff --git a/test/004-StackWalk/src/Main.java b/test/004-StackWalk/src/Main.java index 782f51d2d3..9a1d0ab9f2 100644 --- a/test/004-StackWalk/src/Main.java +++ b/test/004-StackWalk/src/Main.java @@ -2,7 +2,7 @@ public class Main { public Main() { } - int f() throws Exception { + int $noinline$f() throws Exception { g(1); g(2); @@ -93,6 +93,6 @@ public class Main { public static void main(String[] args) throws Exception { Main st = new Main(); - st.f(); + st.$noinline$f(); } } diff --git a/test/067-preemptive-unpark/src/Main.java b/test/067-preemptive-unpark/src/Main.java index 2c099b946e..beb3262a61 100644 --- a/test/067-preemptive-unpark/src/Main.java +++ b/test/067-preemptive-unpark/src/Main.java @@ -40,22 +40,24 @@ public class Main { /** * Set up {@link #UNSAFE}. */ - public static void setUp() { + public static void setUp() throws Exception{ /* * Subvert the access check to get the unique Unsafe instance. * We can do this because there's no security manager * installed when running the test. */ + Field field = null; try { - Field field = Unsafe.class.getDeclaredField("THE_ONE"); - field.setAccessible(true); - - UNSAFE = (Unsafe) field.get(null); - } catch (NoSuchFieldException ex) { - throw new RuntimeException(ex); - } catch (IllegalAccessException ex) { - throw new RuntimeException(ex); + field = Unsafe.class.getDeclaredField("THE_ONE"); + } catch (NoSuchFieldException e1) { + try { + field = Unsafe.class.getDeclaredField("theUnsafe"); + } catch (NoSuchFieldException e2) { + throw new RuntimeException("Failed to find THE_ONE or theUnsafe"); + } } + field.setAccessible(true); + UNSAFE = (Unsafe) field.get(null); } /** diff --git a/test/133-static-invoke-super/src/Main.java b/test/133-static-invoke-super/src/Main.java index 7cfd099a53..e69499865a 100644 --- a/test/133-static-invoke-super/src/Main.java +++ b/test/133-static-invoke-super/src/Main.java @@ -26,14 +26,14 @@ public class Main { run(timing); } - static int testBasis(int interations) { - (new SubClass()).testDirect(interations); - return interations; + static int testBasis(int iterations) { + (new SubClass()).testDirect(iterations); + return iterations; } - static int testStatic(int interations) { - (new SubClass()).testStatic(interations); - return interations; + static int testStatic(int iterations) { + (new SubClass()).testStatic(iterations); + return iterations; } static public void run(boolean timing) { diff --git a/test/422-type-conversion/src/Main.java b/test/422-type-conversion/src/Main.java index 447b9b8fd2..9f8f417cff 100644 --- a/test/422-type-conversion/src/Main.java +++ b/test/422-type-conversion/src/Main.java @@ -138,554 +138,554 @@ public class Main { } private static void byteToLong() { - assertLongEquals(1L, $opt$ByteToLong((byte)1)); - assertLongEquals(0L, $opt$ByteToLong((byte)0)); - assertLongEquals(-1L, $opt$ByteToLong((byte)-1)); - assertLongEquals(51L, $opt$ByteToLong((byte)51)); - assertLongEquals(-51L, $opt$ByteToLong((byte)-51)); - assertLongEquals(127L, $opt$ByteToLong((byte)127)); // 2^7 - 1 - assertLongEquals(-127L, $opt$ByteToLong((byte)-127)); // -(2^7 - 1) - assertLongEquals(-128L, $opt$ByteToLong((byte)-128)); // -(2^7) + assertLongEquals(1L, $opt$noinline$ByteToLong((byte)1)); + assertLongEquals(0L, $opt$noinline$ByteToLong((byte)0)); + assertLongEquals(-1L, $opt$noinline$ByteToLong((byte)-1)); + assertLongEquals(51L, $opt$noinline$ByteToLong((byte)51)); + assertLongEquals(-51L, $opt$noinline$ByteToLong((byte)-51)); + assertLongEquals(127L, $opt$noinline$ByteToLong((byte)127)); // 2^7 - 1 + assertLongEquals(-127L, $opt$noinline$ByteToLong((byte)-127)); // -(2^7 - 1) + assertLongEquals(-128L, $opt$noinline$ByteToLong((byte)-128)); // -(2^7) } private static void shortToLong() { - assertLongEquals(1L, $opt$ShortToLong((short)1)); - assertLongEquals(0L, $opt$ShortToLong((short)0)); - assertLongEquals(-1L, $opt$ShortToLong((short)-1)); - assertLongEquals(51L, $opt$ShortToLong((short)51)); - assertLongEquals(-51L, $opt$ShortToLong((short)-51)); - assertLongEquals(32767L, $opt$ShortToLong((short)32767)); // 2^15 - 1 - assertLongEquals(-32767L, $opt$ShortToLong((short)-32767)); // -(2^15 - 1) - assertLongEquals(-32768L, $opt$ShortToLong((short)-32768)); // -(2^15) + assertLongEquals(1L, $opt$noinline$ShortToLong((short)1)); + assertLongEquals(0L, $opt$noinline$ShortToLong((short)0)); + assertLongEquals(-1L, $opt$noinline$ShortToLong((short)-1)); + assertLongEquals(51L, $opt$noinline$ShortToLong((short)51)); + assertLongEquals(-51L, $opt$noinline$ShortToLong((short)-51)); + assertLongEquals(32767L, $opt$noinline$ShortToLong((short)32767)); // 2^15 - 1 + assertLongEquals(-32767L, $opt$noinline$ShortToLong((short)-32767)); // -(2^15 - 1) + assertLongEquals(-32768L, $opt$noinline$ShortToLong((short)-32768)); // -(2^15) } private static void intToLong() { - assertLongEquals(1L, $opt$IntToLong(1)); - assertLongEquals(0L, $opt$IntToLong(0)); - assertLongEquals(-1L, $opt$IntToLong(-1)); - assertLongEquals(51L, $opt$IntToLong(51)); - assertLongEquals(-51L, $opt$IntToLong(-51)); - assertLongEquals(2147483647L, $opt$IntToLong(2147483647)); // 2^31 - 1 - assertLongEquals(-2147483647L, $opt$IntToLong(-2147483647)); // -(2^31 - 1) - assertLongEquals(-2147483648L, $opt$IntToLong(-2147483648)); // -(2^31) + assertLongEquals(1L, $opt$noinline$IntToLong(1)); + assertLongEquals(0L, $opt$noinline$IntToLong(0)); + assertLongEquals(-1L, $opt$noinline$IntToLong(-1)); + assertLongEquals(51L, $opt$noinline$IntToLong(51)); + assertLongEquals(-51L, $opt$noinline$IntToLong(-51)); + assertLongEquals(2147483647L, $opt$noinline$IntToLong(2147483647)); // 2^31 - 1 + assertLongEquals(-2147483647L, $opt$noinline$IntToLong(-2147483647)); // -(2^31 - 1) + assertLongEquals(-2147483648L, $opt$noinline$IntToLong(-2147483648)); // -(2^31) } private static void charToLong() { - assertLongEquals(1L, $opt$CharToLong((char)1)); - assertLongEquals(0L, $opt$CharToLong((char)0)); - assertLongEquals(51L, $opt$CharToLong((char)51)); - assertLongEquals(32767L, $opt$CharToLong((char)32767)); // 2^15 - 1 - assertLongEquals(65535L, $opt$CharToLong((char)65535)); // 2^16 - 1 - assertLongEquals(65535L, $opt$CharToLong((char)-1)); - assertLongEquals(65485L, $opt$CharToLong((char)-51)); - assertLongEquals(32769L, $opt$CharToLong((char)-32767)); // -(2^15 - 1) - assertLongEquals(32768L, $opt$CharToLong((char)-32768)); // -(2^15) + assertLongEquals(1L, $opt$noinline$CharToLong((char)1)); + assertLongEquals(0L, $opt$noinline$CharToLong((char)0)); + assertLongEquals(51L, $opt$noinline$CharToLong((char)51)); + assertLongEquals(32767L, $opt$noinline$CharToLong((char)32767)); // 2^15 - 1 + assertLongEquals(65535L, $opt$noinline$CharToLong((char)65535)); // 2^16 - 1 + assertLongEquals(65535L, $opt$noinline$CharToLong((char)-1)); + assertLongEquals(65485L, $opt$noinline$CharToLong((char)-51)); + assertLongEquals(32769L, $opt$noinline$CharToLong((char)-32767)); // -(2^15 - 1) + assertLongEquals(32768L, $opt$noinline$CharToLong((char)-32768)); // -(2^15) } private static void byteToFloat() { - assertFloatEquals(1F, $opt$ByteToFloat((byte)1)); - assertFloatEquals(0F, $opt$ByteToFloat((byte)0)); - assertFloatEquals(-1F, $opt$ByteToFloat((byte)-1)); - assertFloatEquals(51F, $opt$ByteToFloat((byte)51)); - assertFloatEquals(-51F, $opt$ByteToFloat((byte)-51)); - assertFloatEquals(127F, $opt$ByteToFloat((byte)127)); // 2^7 - 1 - assertFloatEquals(-127F, $opt$ByteToFloat((byte)-127)); // -(2^7 - 1) - assertFloatEquals(-128F, $opt$ByteToFloat((byte)-128)); // -(2^7) + assertFloatEquals(1F, $opt$noinline$ByteToFloat((byte)1)); + assertFloatEquals(0F, $opt$noinline$ByteToFloat((byte)0)); + assertFloatEquals(-1F, $opt$noinline$ByteToFloat((byte)-1)); + assertFloatEquals(51F, $opt$noinline$ByteToFloat((byte)51)); + assertFloatEquals(-51F, $opt$noinline$ByteToFloat((byte)-51)); + assertFloatEquals(127F, $opt$noinline$ByteToFloat((byte)127)); // 2^7 - 1 + assertFloatEquals(-127F, $opt$noinline$ByteToFloat((byte)-127)); // -(2^7 - 1) + assertFloatEquals(-128F, $opt$noinline$ByteToFloat((byte)-128)); // -(2^7) } private static void shortToFloat() { - assertFloatEquals(1F, $opt$ShortToFloat((short)1)); - assertFloatEquals(0F, $opt$ShortToFloat((short)0)); - assertFloatEquals(-1F, $opt$ShortToFloat((short)-1)); - assertFloatEquals(51F, $opt$ShortToFloat((short)51)); - assertFloatEquals(-51F, $opt$ShortToFloat((short)-51)); - assertFloatEquals(32767F, $opt$ShortToFloat((short)32767)); // 2^15 - 1 - assertFloatEquals(-32767F, $opt$ShortToFloat((short)-32767)); // -(2^15 - 1) - assertFloatEquals(-32768F, $opt$ShortToFloat((short)-32768)); // -(2^15) + assertFloatEquals(1F, $opt$noinline$ShortToFloat((short)1)); + assertFloatEquals(0F, $opt$noinline$ShortToFloat((short)0)); + assertFloatEquals(-1F, $opt$noinline$ShortToFloat((short)-1)); + assertFloatEquals(51F, $opt$noinline$ShortToFloat((short)51)); + assertFloatEquals(-51F, $opt$noinline$ShortToFloat((short)-51)); + assertFloatEquals(32767F, $opt$noinline$ShortToFloat((short)32767)); // 2^15 - 1 + assertFloatEquals(-32767F, $opt$noinline$ShortToFloat((short)-32767)); // -(2^15 - 1) + assertFloatEquals(-32768F, $opt$noinline$ShortToFloat((short)-32768)); // -(2^15) } private static void intToFloat() { - assertFloatEquals(1F, $opt$IntToFloat(1)); - assertFloatEquals(0F, $opt$IntToFloat(0)); - assertFloatEquals(-1F, $opt$IntToFloat(-1)); - assertFloatEquals(51F, $opt$IntToFloat(51)); - assertFloatEquals(-51F, $opt$IntToFloat(-51)); - assertFloatEquals(16777215F, $opt$IntToFloat(16777215)); // 2^24 - 1 - assertFloatEquals(-16777215F, $opt$IntToFloat(-16777215)); // -(2^24 - 1) - assertFloatEquals(16777216F, $opt$IntToFloat(16777216)); // 2^24 - assertFloatEquals(-16777216F, $opt$IntToFloat(-16777216)); // -(2^24) - assertFloatEquals(2147483647F, $opt$IntToFloat(2147483647)); // 2^31 - 1 - assertFloatEquals(-2147483648F, $opt$IntToFloat(-2147483648)); // -(2^31) + assertFloatEquals(1F, $opt$noinline$IntToFloat(1)); + assertFloatEquals(0F, $opt$noinline$IntToFloat(0)); + assertFloatEquals(-1F, $opt$noinline$IntToFloat(-1)); + assertFloatEquals(51F, $opt$noinline$IntToFloat(51)); + assertFloatEquals(-51F, $opt$noinline$IntToFloat(-51)); + assertFloatEquals(16777215F, $opt$noinline$IntToFloat(16777215)); // 2^24 - 1 + assertFloatEquals(-16777215F, $opt$noinline$IntToFloat(-16777215)); // -(2^24 - 1) + assertFloatEquals(16777216F, $opt$noinline$IntToFloat(16777216)); // 2^24 + assertFloatEquals(-16777216F, $opt$noinline$IntToFloat(-16777216)); // -(2^24) + assertFloatEquals(2147483647F, $opt$noinline$IntToFloat(2147483647)); // 2^31 - 1 + assertFloatEquals(-2147483648F, $opt$noinline$IntToFloat(-2147483648)); // -(2^31) } private static void charToFloat() { - assertFloatEquals(1F, $opt$CharToFloat((char)1)); - assertFloatEquals(0F, $opt$CharToFloat((char)0)); - assertFloatEquals(51F, $opt$CharToFloat((char)51)); - assertFloatEquals(32767F, $opt$CharToFloat((char)32767)); // 2^15 - 1 - assertFloatEquals(65535F, $opt$CharToFloat((char)65535)); // 2^16 - 1 - assertFloatEquals(65535F, $opt$CharToFloat((char)-1)); - assertFloatEquals(65485F, $opt$CharToFloat((char)-51)); - assertFloatEquals(32769F, $opt$CharToFloat((char)-32767)); // -(2^15 - 1) - assertFloatEquals(32768F, $opt$CharToFloat((char)-32768)); // -(2^15) + assertFloatEquals(1F, $opt$noinline$CharToFloat((char)1)); + assertFloatEquals(0F, $opt$noinline$CharToFloat((char)0)); + assertFloatEquals(51F, $opt$noinline$CharToFloat((char)51)); + assertFloatEquals(32767F, $opt$noinline$CharToFloat((char)32767)); // 2^15 - 1 + assertFloatEquals(65535F, $opt$noinline$CharToFloat((char)65535)); // 2^16 - 1 + assertFloatEquals(65535F, $opt$noinline$CharToFloat((char)-1)); + assertFloatEquals(65485F, $opt$noinline$CharToFloat((char)-51)); + assertFloatEquals(32769F, $opt$noinline$CharToFloat((char)-32767)); // -(2^15 - 1) + assertFloatEquals(32768F, $opt$noinline$CharToFloat((char)-32768)); // -(2^15) } private static void byteToDouble() { - assertDoubleEquals(1D, $opt$ByteToDouble((byte)1)); - assertDoubleEquals(0D, $opt$ByteToDouble((byte)0)); - assertDoubleEquals(-1D, $opt$ByteToDouble((byte)-1)); - assertDoubleEquals(51D, $opt$ByteToDouble((byte)51)); - assertDoubleEquals(-51D, $opt$ByteToDouble((byte)-51)); - assertDoubleEquals(127D, $opt$ByteToDouble((byte)127)); // 2^7 - 1 - assertDoubleEquals(-127D, $opt$ByteToDouble((byte)-127)); // -(2^7 - 1) - assertDoubleEquals(-128D, $opt$ByteToDouble((byte)-128)); // -(2^7) + assertDoubleEquals(1D, $opt$noinline$ByteToDouble((byte)1)); + assertDoubleEquals(0D, $opt$noinline$ByteToDouble((byte)0)); + assertDoubleEquals(-1D, $opt$noinline$ByteToDouble((byte)-1)); + assertDoubleEquals(51D, $opt$noinline$ByteToDouble((byte)51)); + assertDoubleEquals(-51D, $opt$noinline$ByteToDouble((byte)-51)); + assertDoubleEquals(127D, $opt$noinline$ByteToDouble((byte)127)); // 2^7 - 1 + assertDoubleEquals(-127D, $opt$noinline$ByteToDouble((byte)-127)); // -(2^7 - 1) + assertDoubleEquals(-128D, $opt$noinline$ByteToDouble((byte)-128)); // -(2^7) } private static void shortToDouble() { - assertDoubleEquals(1D, $opt$ShortToDouble((short)1)); - assertDoubleEquals(0D, $opt$ShortToDouble((short)0)); - assertDoubleEquals(-1D, $opt$ShortToDouble((short)-1)); - assertDoubleEquals(51D, $opt$ShortToDouble((short)51)); - assertDoubleEquals(-51D, $opt$ShortToDouble((short)-51)); - assertDoubleEquals(32767D, $opt$ShortToDouble((short)32767)); // 2^15 - 1 - assertDoubleEquals(-32767D, $opt$ShortToDouble((short)-32767)); // -(2^15 - 1) - assertDoubleEquals(-32768D, $opt$ShortToDouble((short)-32768)); // -(2^15) + assertDoubleEquals(1D, $opt$noinline$ShortToDouble((short)1)); + assertDoubleEquals(0D, $opt$noinline$ShortToDouble((short)0)); + assertDoubleEquals(-1D, $opt$noinline$ShortToDouble((short)-1)); + assertDoubleEquals(51D, $opt$noinline$ShortToDouble((short)51)); + assertDoubleEquals(-51D, $opt$noinline$ShortToDouble((short)-51)); + assertDoubleEquals(32767D, $opt$noinline$ShortToDouble((short)32767)); // 2^15 - 1 + assertDoubleEquals(-32767D, $opt$noinline$ShortToDouble((short)-32767)); // -(2^15 - 1) + assertDoubleEquals(-32768D, $opt$noinline$ShortToDouble((short)-32768)); // -(2^15) } private static void intToDouble() { - assertDoubleEquals(1D, $opt$IntToDouble(1)); - assertDoubleEquals(0D, $opt$IntToDouble(0)); - assertDoubleEquals(-1D, $opt$IntToDouble(-1)); - assertDoubleEquals(51D, $opt$IntToDouble(51)); - assertDoubleEquals(-51D, $opt$IntToDouble(-51)); - assertDoubleEquals(16777216D, $opt$IntToDouble(16777216)); // 2^24 - assertDoubleEquals(-16777216D, $opt$IntToDouble(-16777216)); // -(2^24) - assertDoubleEquals(2147483647D, $opt$IntToDouble(2147483647)); // 2^31 - 1 - assertDoubleEquals(-2147483648D, $opt$IntToDouble(-2147483648)); // -(2^31) + assertDoubleEquals(1D, $opt$noinline$IntToDouble(1)); + assertDoubleEquals(0D, $opt$noinline$IntToDouble(0)); + assertDoubleEquals(-1D, $opt$noinline$IntToDouble(-1)); + assertDoubleEquals(51D, $opt$noinline$IntToDouble(51)); + assertDoubleEquals(-51D, $opt$noinline$IntToDouble(-51)); + assertDoubleEquals(16777216D, $opt$noinline$IntToDouble(16777216)); // 2^24 + assertDoubleEquals(-16777216D, $opt$noinline$IntToDouble(-16777216)); // -(2^24) + assertDoubleEquals(2147483647D, $opt$noinline$IntToDouble(2147483647)); // 2^31 - 1 + assertDoubleEquals(-2147483648D, $opt$noinline$IntToDouble(-2147483648)); // -(2^31) } private static void charToDouble() { - assertDoubleEquals(1D, $opt$CharToDouble((char)1)); - assertDoubleEquals(0D, $opt$CharToDouble((char)0)); - assertDoubleEquals(51D, $opt$CharToDouble((char)51)); - assertDoubleEquals(32767D, $opt$CharToDouble((char)32767)); // 2^15 - 1 - assertDoubleEquals(65535D, $opt$CharToDouble((char)65535)); // 2^16 - 1 - assertDoubleEquals(65535D, $opt$CharToDouble((char)-1)); - assertDoubleEquals(65485D, $opt$CharToDouble((char)-51)); - assertDoubleEquals(32769D, $opt$CharToDouble((char)-32767)); // -(2^15 - 1) - assertDoubleEquals(32768D, $opt$CharToDouble((char)-32768)); // -(2^15) + assertDoubleEquals(1D, $opt$noinline$CharToDouble((char)1)); + assertDoubleEquals(0D, $opt$noinline$CharToDouble((char)0)); + assertDoubleEquals(51D, $opt$noinline$CharToDouble((char)51)); + assertDoubleEquals(32767D, $opt$noinline$CharToDouble((char)32767)); // 2^15 - 1 + assertDoubleEquals(65535D, $opt$noinline$CharToDouble((char)65535)); // 2^16 - 1 + assertDoubleEquals(65535D, $opt$noinline$CharToDouble((char)-1)); + assertDoubleEquals(65485D, $opt$noinline$CharToDouble((char)-51)); + assertDoubleEquals(32769D, $opt$noinline$CharToDouble((char)-32767)); // -(2^15 - 1) + assertDoubleEquals(32768D, $opt$noinline$CharToDouble((char)-32768)); // -(2^15) } private static void longToInt() { - assertIntEquals(1, $opt$LongToInt(1L)); - assertIntEquals(0, $opt$LongToInt(0L)); - assertIntEquals(-1, $opt$LongToInt(-1L)); - assertIntEquals(51, $opt$LongToInt(51L)); - assertIntEquals(-51, $opt$LongToInt(-51L)); - assertIntEquals(2147483647, $opt$LongToInt(2147483647L)); // 2^31 - 1 - assertIntEquals(-2147483647, $opt$LongToInt(-2147483647L)); // -(2^31 - 1) - assertIntEquals(-2147483648, $opt$LongToInt(-2147483648L)); // -(2^31) - assertIntEquals(-2147483648, $opt$LongToInt(2147483648L)); // (2^31) - assertIntEquals(2147483647, $opt$LongToInt(-2147483649L)); // -(2^31 + 1) - assertIntEquals(-1, $opt$LongToInt(9223372036854775807L)); // 2^63 - 1 - assertIntEquals(1, $opt$LongToInt(-9223372036854775807L)); // -(2^63 - 1) - assertIntEquals(0, $opt$LongToInt(-9223372036854775808L)); // -(2^63) - - assertIntEquals(42, $opt$LongLiteralToInt()); + assertIntEquals(1, $opt$noinline$LongToInt(1L)); + assertIntEquals(0, $opt$noinline$LongToInt(0L)); + assertIntEquals(-1, $opt$noinline$LongToInt(-1L)); + assertIntEquals(51, $opt$noinline$LongToInt(51L)); + assertIntEquals(-51, $opt$noinline$LongToInt(-51L)); + assertIntEquals(2147483647, $opt$noinline$LongToInt(2147483647L)); // 2^31 - 1 + assertIntEquals(-2147483647, $opt$noinline$LongToInt(-2147483647L)); // -(2^31 - 1) + assertIntEquals(-2147483648, $opt$noinline$LongToInt(-2147483648L)); // -(2^31) + assertIntEquals(-2147483648, $opt$noinline$LongToInt(2147483648L)); // (2^31) + assertIntEquals(2147483647, $opt$noinline$LongToInt(-2147483649L)); // -(2^31 + 1) + assertIntEquals(-1, $opt$noinline$LongToInt(9223372036854775807L)); // 2^63 - 1 + assertIntEquals(1, $opt$noinline$LongToInt(-9223372036854775807L)); // -(2^63 - 1) + assertIntEquals(0, $opt$noinline$LongToInt(-9223372036854775808L)); // -(2^63) + + assertIntEquals(42, $opt$noinline$LongLiteralToInt()); // Ensure long-to-int conversions truncates values as expected. - assertLongEquals(1L, $opt$IntToLong($opt$LongToInt(4294967297L))); // 2^32 + 1 - assertLongEquals(0L, $opt$IntToLong($opt$LongToInt(4294967296L))); // 2^32 - assertLongEquals(-1L, $opt$IntToLong($opt$LongToInt(4294967295L))); // 2^32 - 1 - assertLongEquals(0L, $opt$IntToLong($opt$LongToInt(0L))); - assertLongEquals(1L, $opt$IntToLong($opt$LongToInt(-4294967295L))); // -(2^32 - 1) - assertLongEquals(0L, $opt$IntToLong($opt$LongToInt(-4294967296L))); // -(2^32) - assertLongEquals(-1, $opt$IntToLong($opt$LongToInt(-4294967297L))); // -(2^32 + 1) + assertLongEquals(1L, $opt$noinline$IntToLong($opt$noinline$LongToInt(4294967297L))); // 2^32 + 1 + assertLongEquals(0L, $opt$noinline$IntToLong($opt$noinline$LongToInt(4294967296L))); // 2^32 + assertLongEquals(-1L, $opt$noinline$IntToLong($opt$noinline$LongToInt(4294967295L))); // 2^32 - 1 + assertLongEquals(0L, $opt$noinline$IntToLong($opt$noinline$LongToInt(0L))); + assertLongEquals(1L, $opt$noinline$IntToLong($opt$noinline$LongToInt(-4294967295L))); // -(2^32 - 1) + assertLongEquals(0L, $opt$noinline$IntToLong($opt$noinline$LongToInt(-4294967296L))); // -(2^32) + assertLongEquals(-1, $opt$noinline$IntToLong($opt$noinline$LongToInt(-4294967297L))); // -(2^32 + 1) } private static void longToFloat() { - assertFloatEquals(1F, $opt$LongToFloat(1L)); - assertFloatEquals(0F, $opt$LongToFloat(0L)); - assertFloatEquals(-1F, $opt$LongToFloat(-1L)); - assertFloatEquals(51F, $opt$LongToFloat(51L)); - assertFloatEquals(-51F, $opt$LongToFloat(-51L)); - assertFloatEquals(2147483647F, $opt$LongToFloat(2147483647L)); // 2^31 - 1 - assertFloatEquals(-2147483647F, $opt$LongToFloat(-2147483647L)); // -(2^31 - 1) - assertFloatEquals(-2147483648F, $opt$LongToFloat(-2147483648L)); // -(2^31) - assertFloatEquals(2147483648F, $opt$LongToFloat(2147483648L)); // (2^31) - assertFloatEquals(-2147483649F, $opt$LongToFloat(-2147483649L)); // -(2^31 + 1) - assertFloatEquals(4294967296F, $opt$LongToFloat(4294967296L)); // (2^32) - assertFloatEquals(-4294967296F, $opt$LongToFloat(-4294967296L)); // -(2^32) - assertFloatEquals(140739635871745F, $opt$LongToFloat(140739635871745L)); // 1 + 2^15 + 2^31 + 2^47 - assertFloatEquals(-140739635871745F, $opt$LongToFloat(-140739635871745L)); // -(1 + 2^15 + 2^31 + 2^47) - assertFloatEquals(9223372036854775807F, $opt$LongToFloat(9223372036854775807L)); // 2^63 - 1 - assertFloatEquals(-9223372036854775807F, $opt$LongToFloat(-9223372036854775807L)); // -(2^63 - 1) - assertFloatEquals(-9223372036854775808F, $opt$LongToFloat(-9223372036854775808L)); // -(2^63) + assertFloatEquals(1F, $opt$noinline$LongToFloat(1L)); + assertFloatEquals(0F, $opt$noinline$LongToFloat(0L)); + assertFloatEquals(-1F, $opt$noinline$LongToFloat(-1L)); + assertFloatEquals(51F, $opt$noinline$LongToFloat(51L)); + assertFloatEquals(-51F, $opt$noinline$LongToFloat(-51L)); + assertFloatEquals(2147483647F, $opt$noinline$LongToFloat(2147483647L)); // 2^31 - 1 + assertFloatEquals(-2147483647F, $opt$noinline$LongToFloat(-2147483647L)); // -(2^31 - 1) + assertFloatEquals(-2147483648F, $opt$noinline$LongToFloat(-2147483648L)); // -(2^31) + assertFloatEquals(2147483648F, $opt$noinline$LongToFloat(2147483648L)); // (2^31) + assertFloatEquals(-2147483649F, $opt$noinline$LongToFloat(-2147483649L)); // -(2^31 + 1) + assertFloatEquals(4294967296F, $opt$noinline$LongToFloat(4294967296L)); // (2^32) + assertFloatEquals(-4294967296F, $opt$noinline$LongToFloat(-4294967296L)); // -(2^32) + assertFloatEquals(140739635871745F, $opt$noinline$LongToFloat(140739635871745L)); // 1 + 2^15 + 2^31 + 2^47 + assertFloatEquals(-140739635871745F, $opt$noinline$LongToFloat(-140739635871745L)); // -(1 + 2^15 + 2^31 + 2^47) + assertFloatEquals(9223372036854775807F, $opt$noinline$LongToFloat(9223372036854775807L)); // 2^63 - 1 + assertFloatEquals(-9223372036854775807F, $opt$noinline$LongToFloat(-9223372036854775807L)); // -(2^63 - 1) + assertFloatEquals(-9223372036854775808F, $opt$noinline$LongToFloat(-9223372036854775808L)); // -(2^63) } private static void longToDouble() { - assertDoubleEquals(1D, $opt$LongToDouble(1L)); - assertDoubleEquals(0D, $opt$LongToDouble(0L)); - assertDoubleEquals(-1D, $opt$LongToDouble(-1L)); - assertDoubleEquals(51D, $opt$LongToDouble(51L)); - assertDoubleEquals(-51D, $opt$LongToDouble(-51L)); - assertDoubleEquals(2147483647D, $opt$LongToDouble(2147483647L)); // 2^31 - 1 - assertDoubleEquals(-2147483647D, $opt$LongToDouble(-2147483647L)); // -(2^31 - 1) - assertDoubleEquals(-2147483648D, $opt$LongToDouble(-2147483648L)); // -(2^31) - assertDoubleEquals(2147483648D, $opt$LongToDouble(2147483648L)); // (2^31) - assertDoubleEquals(-2147483649D, $opt$LongToDouble(-2147483649L)); // -(2^31 + 1) - assertDoubleEquals(4294967296D, $opt$LongToDouble(4294967296L)); // (2^32) - assertDoubleEquals(-4294967296D, $opt$LongToDouble(-4294967296L)); // -(2^32) - assertDoubleEquals(140739635871745D, $opt$LongToDouble(140739635871745L)); // 1 + 2^15 + 2^31 + 2^47 - assertDoubleEquals(-140739635871745D, $opt$LongToDouble(-140739635871745L)); // -(1 + 2^15 + 2^31 + 2^47) - assertDoubleEquals(9223372036854775807D, $opt$LongToDouble(9223372036854775807L)); // 2^63 - 1 - assertDoubleEquals(-9223372036854775807D, $opt$LongToDouble(-9223372036854775807L)); // -(2^63 - 1) - assertDoubleEquals(-9223372036854775808D, $opt$LongToDouble(-9223372036854775808L)); // -(2^63) + assertDoubleEquals(1D, $opt$noinline$LongToDouble(1L)); + assertDoubleEquals(0D, $opt$noinline$LongToDouble(0L)); + assertDoubleEquals(-1D, $opt$noinline$LongToDouble(-1L)); + assertDoubleEquals(51D, $opt$noinline$LongToDouble(51L)); + assertDoubleEquals(-51D, $opt$noinline$LongToDouble(-51L)); + assertDoubleEquals(2147483647D, $opt$noinline$LongToDouble(2147483647L)); // 2^31 - 1 + assertDoubleEquals(-2147483647D, $opt$noinline$LongToDouble(-2147483647L)); // -(2^31 - 1) + assertDoubleEquals(-2147483648D, $opt$noinline$LongToDouble(-2147483648L)); // -(2^31) + assertDoubleEquals(2147483648D, $opt$noinline$LongToDouble(2147483648L)); // (2^31) + assertDoubleEquals(-2147483649D, $opt$noinline$LongToDouble(-2147483649L)); // -(2^31 + 1) + assertDoubleEquals(4294967296D, $opt$noinline$LongToDouble(4294967296L)); // (2^32) + assertDoubleEquals(-4294967296D, $opt$noinline$LongToDouble(-4294967296L)); // -(2^32) + assertDoubleEquals(140739635871745D, $opt$noinline$LongToDouble(140739635871745L)); // 1 + 2^15 + 2^31 + 2^47 + assertDoubleEquals(-140739635871745D, $opt$noinline$LongToDouble(-140739635871745L)); // -(1 + 2^15 + 2^31 + 2^47) + assertDoubleEquals(9223372036854775807D, $opt$noinline$LongToDouble(9223372036854775807L)); // 2^63 - 1 + assertDoubleEquals(-9223372036854775807D, $opt$noinline$LongToDouble(-9223372036854775807L)); // -(2^63 - 1) + assertDoubleEquals(-9223372036854775808D, $opt$noinline$LongToDouble(-9223372036854775808L)); // -(2^63) } private static void floatToInt() { - assertIntEquals(1, $opt$FloatToInt(1F)); - assertIntEquals(0, $opt$FloatToInt(0F)); - assertIntEquals(0, $opt$FloatToInt(-0F)); - assertIntEquals(-1, $opt$FloatToInt(-1F)); - assertIntEquals(51, $opt$FloatToInt(51F)); - assertIntEquals(-51, $opt$FloatToInt(-51F)); - assertIntEquals(0, $opt$FloatToInt(0.5F)); - assertIntEquals(0, $opt$FloatToInt(0.4999999F)); - assertIntEquals(0, $opt$FloatToInt(-0.4999999F)); - assertIntEquals(0, $opt$FloatToInt(-0.5F)); - assertIntEquals(42, $opt$FloatToInt(42.199F)); - assertIntEquals(-42, $opt$FloatToInt(-42.199F)); - assertIntEquals(2147483647, $opt$FloatToInt(2147483647F)); // 2^31 - 1 - assertIntEquals(-2147483648, $opt$FloatToInt(-2147483647F)); // -(2^31 - 1) - assertIntEquals(-2147483648, $opt$FloatToInt(-2147483648F)); // -(2^31) - assertIntEquals(2147483647, $opt$FloatToInt(2147483648F)); // (2^31) - assertIntEquals(-2147483648, $opt$FloatToInt(-2147483649F)); // -(2^31 + 1) - assertIntEquals(2147483647, $opt$FloatToInt(9223372036854775807F)); // 2^63 - 1 - assertIntEquals(-2147483648, $opt$FloatToInt(-9223372036854775807F)); // -(2^63 - 1) - assertIntEquals(-2147483648, $opt$FloatToInt(-9223372036854775808F)); // -(2^63) - assertIntEquals(0, $opt$FloatToInt(Float.NaN)); - assertIntEquals(2147483647, $opt$FloatToInt(Float.POSITIVE_INFINITY)); - assertIntEquals(-2147483648, $opt$FloatToInt(Float.NEGATIVE_INFINITY)); + assertIntEquals(1, $opt$noinline$FloatToInt(1F)); + assertIntEquals(0, $opt$noinline$FloatToInt(0F)); + assertIntEquals(0, $opt$noinline$FloatToInt(-0F)); + assertIntEquals(-1, $opt$noinline$FloatToInt(-1F)); + assertIntEquals(51, $opt$noinline$FloatToInt(51F)); + assertIntEquals(-51, $opt$noinline$FloatToInt(-51F)); + assertIntEquals(0, $opt$noinline$FloatToInt(0.5F)); + assertIntEquals(0, $opt$noinline$FloatToInt(0.4999999F)); + assertIntEquals(0, $opt$noinline$FloatToInt(-0.4999999F)); + assertIntEquals(0, $opt$noinline$FloatToInt(-0.5F)); + assertIntEquals(42, $opt$noinline$FloatToInt(42.199F)); + assertIntEquals(-42, $opt$noinline$FloatToInt(-42.199F)); + assertIntEquals(2147483647, $opt$noinline$FloatToInt(2147483647F)); // 2^31 - 1 + assertIntEquals(-2147483648, $opt$noinline$FloatToInt(-2147483647F)); // -(2^31 - 1) + assertIntEquals(-2147483648, $opt$noinline$FloatToInt(-2147483648F)); // -(2^31) + assertIntEquals(2147483647, $opt$noinline$FloatToInt(2147483648F)); // (2^31) + assertIntEquals(-2147483648, $opt$noinline$FloatToInt(-2147483649F)); // -(2^31 + 1) + assertIntEquals(2147483647, $opt$noinline$FloatToInt(9223372036854775807F)); // 2^63 - 1 + assertIntEquals(-2147483648, $opt$noinline$FloatToInt(-9223372036854775807F)); // -(2^63 - 1) + assertIntEquals(-2147483648, $opt$noinline$FloatToInt(-9223372036854775808F)); // -(2^63) + assertIntEquals(0, $opt$noinline$FloatToInt(Float.NaN)); + assertIntEquals(2147483647, $opt$noinline$FloatToInt(Float.POSITIVE_INFINITY)); + assertIntEquals(-2147483648, $opt$noinline$FloatToInt(Float.NEGATIVE_INFINITY)); } private static void floatToLong() { - assertLongEquals(1L, $opt$FloatToLong(1F)); - assertLongEquals(0L, $opt$FloatToLong(0F)); - assertLongEquals(0L, $opt$FloatToLong(-0F)); - assertLongEquals(-1L, $opt$FloatToLong(-1F)); - assertLongEquals(51L, $opt$FloatToLong(51F)); - assertLongEquals(-51L, $opt$FloatToLong(-51F)); - assertLongEquals(0L, $opt$FloatToLong(0.5F)); - assertLongEquals(0L, $opt$FloatToLong(0.4999999F)); - assertLongEquals(0L, $opt$FloatToLong(-0.4999999F)); - assertLongEquals(0L, $opt$FloatToLong(-0.5F)); - assertLongEquals(42L, $opt$FloatToLong(42.199F)); - assertLongEquals(-42L, $opt$FloatToLong(-42.199F)); - assertLongEquals(2147483648L, $opt$FloatToLong(2147483647F)); // 2^31 - 1 - assertLongEquals(-2147483648L, $opt$FloatToLong(-2147483647F)); // -(2^31 - 1) - assertLongEquals(-2147483648L, $opt$FloatToLong(-2147483648F)); // -(2^31) - assertLongEquals(2147483648L, $opt$FloatToLong(2147483648F)); // (2^31) - assertLongEquals(-2147483648L, $opt$FloatToLong(-2147483649F)); // -(2^31 + 1) - assertLongEquals(9223372036854775807L, $opt$FloatToLong(9223372036854775807F)); // 2^63 - 1 - assertLongEquals(-9223372036854775808L, $opt$FloatToLong(-9223372036854775807F)); // -(2^63 - 1) - assertLongEquals(-9223372036854775808L, $opt$FloatToLong(-9223372036854775808F)); // -(2^63) - assertLongEquals(0L, $opt$FloatToLong(Float.NaN)); - assertLongEquals(9223372036854775807L, $opt$FloatToLong(Float.POSITIVE_INFINITY)); - assertLongEquals(-9223372036854775808L, $opt$FloatToLong(Float.NEGATIVE_INFINITY)); + assertLongEquals(1L, $opt$noinline$FloatToLong(1F)); + assertLongEquals(0L, $opt$noinline$FloatToLong(0F)); + assertLongEquals(0L, $opt$noinline$FloatToLong(-0F)); + assertLongEquals(-1L, $opt$noinline$FloatToLong(-1F)); + assertLongEquals(51L, $opt$noinline$FloatToLong(51F)); + assertLongEquals(-51L, $opt$noinline$FloatToLong(-51F)); + assertLongEquals(0L, $opt$noinline$FloatToLong(0.5F)); + assertLongEquals(0L, $opt$noinline$FloatToLong(0.4999999F)); + assertLongEquals(0L, $opt$noinline$FloatToLong(-0.4999999F)); + assertLongEquals(0L, $opt$noinline$FloatToLong(-0.5F)); + assertLongEquals(42L, $opt$noinline$FloatToLong(42.199F)); + assertLongEquals(-42L, $opt$noinline$FloatToLong(-42.199F)); + assertLongEquals(2147483648L, $opt$noinline$FloatToLong(2147483647F)); // 2^31 - 1 + assertLongEquals(-2147483648L, $opt$noinline$FloatToLong(-2147483647F)); // -(2^31 - 1) + assertLongEquals(-2147483648L, $opt$noinline$FloatToLong(-2147483648F)); // -(2^31) + assertLongEquals(2147483648L, $opt$noinline$FloatToLong(2147483648F)); // (2^31) + assertLongEquals(-2147483648L, $opt$noinline$FloatToLong(-2147483649F)); // -(2^31 + 1) + assertLongEquals(9223372036854775807L, $opt$noinline$FloatToLong(9223372036854775807F)); // 2^63 - 1 + assertLongEquals(-9223372036854775808L, $opt$noinline$FloatToLong(-9223372036854775807F)); // -(2^63 - 1) + assertLongEquals(-9223372036854775808L, $opt$noinline$FloatToLong(-9223372036854775808F)); // -(2^63) + assertLongEquals(0L, $opt$noinline$FloatToLong(Float.NaN)); + assertLongEquals(9223372036854775807L, $opt$noinline$FloatToLong(Float.POSITIVE_INFINITY)); + assertLongEquals(-9223372036854775808L, $opt$noinline$FloatToLong(Float.NEGATIVE_INFINITY)); } private static void floatToDouble() { - assertDoubleEquals(1D, $opt$FloatToDouble(1F)); - assertDoubleEquals(0D, $opt$FloatToDouble(0F)); - assertDoubleEquals(0D, $opt$FloatToDouble(-0F)); - assertDoubleEquals(-1D, $opt$FloatToDouble(-1F)); - assertDoubleEquals(51D, $opt$FloatToDouble(51F)); - assertDoubleEquals(-51D, $opt$FloatToDouble(-51F)); - assertDoubleEquals(0.5D, $opt$FloatToDouble(0.5F)); - assertDoubleEquals(0.49999991059303284D, $opt$FloatToDouble(0.4999999F)); - assertDoubleEquals(-0.49999991059303284D, $opt$FloatToDouble(-0.4999999F)); - assertDoubleEquals(-0.5D, $opt$FloatToDouble(-0.5F)); - assertDoubleEquals(42.19900131225586D, $opt$FloatToDouble(42.199F)); - assertDoubleEquals(-42.19900131225586D, $opt$FloatToDouble(-42.199F)); - assertDoubleEquals(2147483648D, $opt$FloatToDouble(2147483647F)); // 2^31 - 1 - assertDoubleEquals(-2147483648D, $opt$FloatToDouble(-2147483647F)); // -(2^31 - 1) - assertDoubleEquals(-2147483648D, $opt$FloatToDouble(-2147483648F)); // -(2^31) - assertDoubleEquals(2147483648D, $opt$FloatToDouble(2147483648F)); // (2^31) - assertDoubleEquals(-2147483648D, $opt$FloatToDouble(-2147483649F)); // -(2^31 + 1) - assertDoubleEquals(9223372036854775807D, $opt$FloatToDouble(9223372036854775807F)); // 2^63 - 1 - assertDoubleEquals(-9223372036854775807D, $opt$FloatToDouble(-9223372036854775807F)); // -(2^63 - 1) - assertDoubleEquals(-9223372036854775808D, $opt$FloatToDouble(-9223372036854775808F)); // -(2^63) - assertDoubleIsNaN($opt$FloatToDouble(Float.NaN)); - assertDoubleEquals(Double.POSITIVE_INFINITY, $opt$FloatToDouble(Float.POSITIVE_INFINITY)); - assertDoubleEquals(Double.NEGATIVE_INFINITY, $opt$FloatToDouble(Float.NEGATIVE_INFINITY)); + assertDoubleEquals(1D, $opt$noinline$FloatToDouble(1F)); + assertDoubleEquals(0D, $opt$noinline$FloatToDouble(0F)); + assertDoubleEquals(0D, $opt$noinline$FloatToDouble(-0F)); + assertDoubleEquals(-1D, $opt$noinline$FloatToDouble(-1F)); + assertDoubleEquals(51D, $opt$noinline$FloatToDouble(51F)); + assertDoubleEquals(-51D, $opt$noinline$FloatToDouble(-51F)); + assertDoubleEquals(0.5D, $opt$noinline$FloatToDouble(0.5F)); + assertDoubleEquals(0.49999991059303284D, $opt$noinline$FloatToDouble(0.4999999F)); + assertDoubleEquals(-0.49999991059303284D, $opt$noinline$FloatToDouble(-0.4999999F)); + assertDoubleEquals(-0.5D, $opt$noinline$FloatToDouble(-0.5F)); + assertDoubleEquals(42.19900131225586D, $opt$noinline$FloatToDouble(42.199F)); + assertDoubleEquals(-42.19900131225586D, $opt$noinline$FloatToDouble(-42.199F)); + assertDoubleEquals(2147483648D, $opt$noinline$FloatToDouble(2147483647F)); // 2^31 - 1 + assertDoubleEquals(-2147483648D, $opt$noinline$FloatToDouble(-2147483647F)); // -(2^31 - 1) + assertDoubleEquals(-2147483648D, $opt$noinline$FloatToDouble(-2147483648F)); // -(2^31) + assertDoubleEquals(2147483648D, $opt$noinline$FloatToDouble(2147483648F)); // (2^31) + assertDoubleEquals(-2147483648D, $opt$noinline$FloatToDouble(-2147483649F)); // -(2^31 + 1) + assertDoubleEquals(9223372036854775807D, $opt$noinline$FloatToDouble(9223372036854775807F)); // 2^63 - 1 + assertDoubleEquals(-9223372036854775807D, $opt$noinline$FloatToDouble(-9223372036854775807F)); // -(2^63 - 1) + assertDoubleEquals(-9223372036854775808D, $opt$noinline$FloatToDouble(-9223372036854775808F)); // -(2^63) + assertDoubleIsNaN($opt$noinline$FloatToDouble(Float.NaN)); + assertDoubleEquals(Double.POSITIVE_INFINITY, $opt$noinline$FloatToDouble(Float.POSITIVE_INFINITY)); + assertDoubleEquals(Double.NEGATIVE_INFINITY, $opt$noinline$FloatToDouble(Float.NEGATIVE_INFINITY)); } private static void doubleToInt() { - assertIntEquals(1, $opt$DoubleToInt(1D)); - assertIntEquals(0, $opt$DoubleToInt(0D)); - assertIntEquals(0, $opt$DoubleToInt(-0D)); - assertIntEquals(-1, $opt$DoubleToInt(-1D)); - assertIntEquals(51, $opt$DoubleToInt(51D)); - assertIntEquals(-51, $opt$DoubleToInt(-51D)); - assertIntEquals(0, $opt$DoubleToInt(0.5D)); - assertIntEquals(0, $opt$DoubleToInt(0.4999999D)); - assertIntEquals(0, $opt$DoubleToInt(-0.4999999D)); - assertIntEquals(0, $opt$DoubleToInt(-0.5D)); - assertIntEquals(42, $opt$DoubleToInt(42.199D)); - assertIntEquals(-42, $opt$DoubleToInt(-42.199D)); - assertIntEquals(2147483647, $opt$DoubleToInt(2147483647D)); // 2^31 - 1 - assertIntEquals(-2147483647, $opt$DoubleToInt(-2147483647D)); // -(2^31 - 1) - assertIntEquals(-2147483648, $opt$DoubleToInt(-2147483648D)); // -(2^31) - assertIntEquals(2147483647, $opt$DoubleToInt(2147483648D)); // (2^31) - assertIntEquals(-2147483648, $opt$DoubleToInt(-2147483649D)); // -(2^31 + 1) - assertIntEquals(2147483647, $opt$DoubleToInt(9223372036854775807D)); // 2^63 - 1 - assertIntEquals(-2147483648, $opt$DoubleToInt(-9223372036854775807D)); // -(2^63 - 1) - assertIntEquals(-2147483648, $opt$DoubleToInt(-9223372036854775808D)); // -(2^63) - assertIntEquals(0, $opt$DoubleToInt(Double.NaN)); - assertIntEquals(2147483647, $opt$DoubleToInt(Double.POSITIVE_INFINITY)); - assertIntEquals(-2147483648, $opt$DoubleToInt(Double.NEGATIVE_INFINITY)); + assertIntEquals(1, $opt$noinline$DoubleToInt(1D)); + assertIntEquals(0, $opt$noinline$DoubleToInt(0D)); + assertIntEquals(0, $opt$noinline$DoubleToInt(-0D)); + assertIntEquals(-1, $opt$noinline$DoubleToInt(-1D)); + assertIntEquals(51, $opt$noinline$DoubleToInt(51D)); + assertIntEquals(-51, $opt$noinline$DoubleToInt(-51D)); + assertIntEquals(0, $opt$noinline$DoubleToInt(0.5D)); + assertIntEquals(0, $opt$noinline$DoubleToInt(0.4999999D)); + assertIntEquals(0, $opt$noinline$DoubleToInt(-0.4999999D)); + assertIntEquals(0, $opt$noinline$DoubleToInt(-0.5D)); + assertIntEquals(42, $opt$noinline$DoubleToInt(42.199D)); + assertIntEquals(-42, $opt$noinline$DoubleToInt(-42.199D)); + assertIntEquals(2147483647, $opt$noinline$DoubleToInt(2147483647D)); // 2^31 - 1 + assertIntEquals(-2147483647, $opt$noinline$DoubleToInt(-2147483647D)); // -(2^31 - 1) + assertIntEquals(-2147483648, $opt$noinline$DoubleToInt(-2147483648D)); // -(2^31) + assertIntEquals(2147483647, $opt$noinline$DoubleToInt(2147483648D)); // (2^31) + assertIntEquals(-2147483648, $opt$noinline$DoubleToInt(-2147483649D)); // -(2^31 + 1) + assertIntEquals(2147483647, $opt$noinline$DoubleToInt(9223372036854775807D)); // 2^63 - 1 + assertIntEquals(-2147483648, $opt$noinline$DoubleToInt(-9223372036854775807D)); // -(2^63 - 1) + assertIntEquals(-2147483648, $opt$noinline$DoubleToInt(-9223372036854775808D)); // -(2^63) + assertIntEquals(0, $opt$noinline$DoubleToInt(Double.NaN)); + assertIntEquals(2147483647, $opt$noinline$DoubleToInt(Double.POSITIVE_INFINITY)); + assertIntEquals(-2147483648, $opt$noinline$DoubleToInt(Double.NEGATIVE_INFINITY)); } private static void doubleToLong() { - assertLongEquals(1L, $opt$DoubleToLong(1D)); - assertLongEquals(0L, $opt$DoubleToLong(0D)); - assertLongEquals(0L, $opt$DoubleToLong(-0D)); - assertLongEquals(-1L, $opt$DoubleToLong(-1D)); - assertLongEquals(51L, $opt$DoubleToLong(51D)); - assertLongEquals(-51L, $opt$DoubleToLong(-51D)); - assertLongEquals(0L, $opt$DoubleToLong(0.5D)); - assertLongEquals(0L, $opt$DoubleToLong(0.4999999D)); - assertLongEquals(0L, $opt$DoubleToLong(-0.4999999D)); - assertLongEquals(0L, $opt$DoubleToLong(-0.5D)); - assertLongEquals(42L, $opt$DoubleToLong(42.199D)); - assertLongEquals(-42L, $opt$DoubleToLong(-42.199D)); - assertLongEquals(2147483647L, $opt$DoubleToLong(2147483647D)); // 2^31 - 1 - assertLongEquals(-2147483647L, $opt$DoubleToLong(-2147483647D)); // -(2^31 - 1) - assertLongEquals(-2147483648L, $opt$DoubleToLong(-2147483648D)); // -(2^31) - assertLongEquals(2147483648L, $opt$DoubleToLong(2147483648D)); // (2^31) - assertLongEquals(-2147483649L, $opt$DoubleToLong(-2147483649D)); // -(2^31 + 1) - assertLongEquals(9223372036854775807L, $opt$DoubleToLong(9223372036854775807D)); // 2^63 - 1 - assertLongEquals(-9223372036854775808L, $opt$DoubleToLong(-9223372036854775807D)); // -(2^63 - 1) - assertLongEquals(-9223372036854775808L, $opt$DoubleToLong(-9223372036854775808D)); // -(2^63) - assertLongEquals(0L, $opt$DoubleToLong(Double.NaN)); - assertLongEquals(9223372036854775807L, $opt$DoubleToLong(Double.POSITIVE_INFINITY)); - assertLongEquals(-9223372036854775808L, $opt$DoubleToLong(Double.NEGATIVE_INFINITY)); + assertLongEquals(1L, $opt$noinline$DoubleToLong(1D)); + assertLongEquals(0L, $opt$noinline$DoubleToLong(0D)); + assertLongEquals(0L, $opt$noinline$DoubleToLong(-0D)); + assertLongEquals(-1L, $opt$noinline$DoubleToLong(-1D)); + assertLongEquals(51L, $opt$noinline$DoubleToLong(51D)); + assertLongEquals(-51L, $opt$noinline$DoubleToLong(-51D)); + assertLongEquals(0L, $opt$noinline$DoubleToLong(0.5D)); + assertLongEquals(0L, $opt$noinline$DoubleToLong(0.4999999D)); + assertLongEquals(0L, $opt$noinline$DoubleToLong(-0.4999999D)); + assertLongEquals(0L, $opt$noinline$DoubleToLong(-0.5D)); + assertLongEquals(42L, $opt$noinline$DoubleToLong(42.199D)); + assertLongEquals(-42L, $opt$noinline$DoubleToLong(-42.199D)); + assertLongEquals(2147483647L, $opt$noinline$DoubleToLong(2147483647D)); // 2^31 - 1 + assertLongEquals(-2147483647L, $opt$noinline$DoubleToLong(-2147483647D)); // -(2^31 - 1) + assertLongEquals(-2147483648L, $opt$noinline$DoubleToLong(-2147483648D)); // -(2^31) + assertLongEquals(2147483648L, $opt$noinline$DoubleToLong(2147483648D)); // (2^31) + assertLongEquals(-2147483649L, $opt$noinline$DoubleToLong(-2147483649D)); // -(2^31 + 1) + assertLongEquals(9223372036854775807L, $opt$noinline$DoubleToLong(9223372036854775807D)); // 2^63 - 1 + assertLongEquals(-9223372036854775808L, $opt$noinline$DoubleToLong(-9223372036854775807D)); // -(2^63 - 1) + assertLongEquals(-9223372036854775808L, $opt$noinline$DoubleToLong(-9223372036854775808D)); // -(2^63) + assertLongEquals(0L, $opt$noinline$DoubleToLong(Double.NaN)); + assertLongEquals(9223372036854775807L, $opt$noinline$DoubleToLong(Double.POSITIVE_INFINITY)); + assertLongEquals(-9223372036854775808L, $opt$noinline$DoubleToLong(Double.NEGATIVE_INFINITY)); } private static void doubleToFloat() { - assertFloatEquals(1F, $opt$DoubleToFloat(1D)); - assertFloatEquals(0F, $opt$DoubleToFloat(0D)); - assertFloatEquals(0F, $opt$DoubleToFloat(-0D)); - assertFloatEquals(-1F, $opt$DoubleToFloat(-1D)); - assertFloatEquals(51F, $opt$DoubleToFloat(51D)); - assertFloatEquals(-51F, $opt$DoubleToFloat(-51D)); - assertFloatEquals(0.5F, $opt$DoubleToFloat(0.5D)); - assertFloatEquals(0.4999999F, $opt$DoubleToFloat(0.4999999D)); - assertFloatEquals(-0.4999999F, $opt$DoubleToFloat(-0.4999999D)); - assertFloatEquals(-0.5F, $opt$DoubleToFloat(-0.5D)); - assertFloatEquals(42.199F, $opt$DoubleToFloat(42.199D)); - assertFloatEquals(-42.199F, $opt$DoubleToFloat(-42.199D)); - assertFloatEquals(2147483648F, $opt$DoubleToFloat(2147483647D)); // 2^31 - 1 - assertFloatEquals(-2147483648F, $opt$DoubleToFloat(-2147483647D)); // -(2^31 - 1) - assertFloatEquals(-2147483648F, $opt$DoubleToFloat(-2147483648D)); // -(2^31) - assertFloatEquals(2147483648F, $opt$DoubleToFloat(2147483648D)); // (2^31) - assertFloatEquals(-2147483648F, $opt$DoubleToFloat(-2147483649D)); // -(2^31 + 1) - assertFloatEquals(9223372036854775807F, $opt$DoubleToFloat(9223372036854775807D)); // 2^63 - 1 - assertFloatEquals(-9223372036854775807F, $opt$DoubleToFloat(-9223372036854775807D)); // -(2^63 - 1) - assertFloatEquals(-9223372036854775808F, $opt$DoubleToFloat(-9223372036854775808D)); // -(2^63) - assertFloatIsNaN($opt$DoubleToFloat(Float.NaN)); - assertFloatEquals(Float.POSITIVE_INFINITY, $opt$DoubleToFloat(Double.POSITIVE_INFINITY)); - assertFloatEquals(Float.NEGATIVE_INFINITY, $opt$DoubleToFloat(Double.NEGATIVE_INFINITY)); + assertFloatEquals(1F, $opt$noinline$DoubleToFloat(1D)); + assertFloatEquals(0F, $opt$noinline$DoubleToFloat(0D)); + assertFloatEquals(0F, $opt$noinline$DoubleToFloat(-0D)); + assertFloatEquals(-1F, $opt$noinline$DoubleToFloat(-1D)); + assertFloatEquals(51F, $opt$noinline$DoubleToFloat(51D)); + assertFloatEquals(-51F, $opt$noinline$DoubleToFloat(-51D)); + assertFloatEquals(0.5F, $opt$noinline$DoubleToFloat(0.5D)); + assertFloatEquals(0.4999999F, $opt$noinline$DoubleToFloat(0.4999999D)); + assertFloatEquals(-0.4999999F, $opt$noinline$DoubleToFloat(-0.4999999D)); + assertFloatEquals(-0.5F, $opt$noinline$DoubleToFloat(-0.5D)); + assertFloatEquals(42.199F, $opt$noinline$DoubleToFloat(42.199D)); + assertFloatEquals(-42.199F, $opt$noinline$DoubleToFloat(-42.199D)); + assertFloatEquals(2147483648F, $opt$noinline$DoubleToFloat(2147483647D)); // 2^31 - 1 + assertFloatEquals(-2147483648F, $opt$noinline$DoubleToFloat(-2147483647D)); // -(2^31 - 1) + assertFloatEquals(-2147483648F, $opt$noinline$DoubleToFloat(-2147483648D)); // -(2^31) + assertFloatEquals(2147483648F, $opt$noinline$DoubleToFloat(2147483648D)); // (2^31) + assertFloatEquals(-2147483648F, $opt$noinline$DoubleToFloat(-2147483649D)); // -(2^31 + 1) + assertFloatEquals(9223372036854775807F, $opt$noinline$DoubleToFloat(9223372036854775807D)); // 2^63 - 1 + assertFloatEquals(-9223372036854775807F, $opt$noinline$DoubleToFloat(-9223372036854775807D)); // -(2^63 - 1) + assertFloatEquals(-9223372036854775808F, $opt$noinline$DoubleToFloat(-9223372036854775808D)); // -(2^63) + assertFloatIsNaN($opt$noinline$DoubleToFloat(Float.NaN)); + assertFloatEquals(Float.POSITIVE_INFINITY, $opt$noinline$DoubleToFloat(Double.POSITIVE_INFINITY)); + assertFloatEquals(Float.NEGATIVE_INFINITY, $opt$noinline$DoubleToFloat(Double.NEGATIVE_INFINITY)); } private static void shortToByte() { - assertByteEquals((byte)1, $opt$ShortToByte((short)1)); - assertByteEquals((byte)0, $opt$ShortToByte((short)0)); - assertByteEquals((byte)-1, $opt$ShortToByte((short)-1)); - assertByteEquals((byte)51, $opt$ShortToByte((short)51)); - assertByteEquals((byte)-51, $opt$ShortToByte((short)-51)); - assertByteEquals((byte)127, $opt$ShortToByte((short)127)); // 2^7 - 1 - assertByteEquals((byte)-127, $opt$ShortToByte((short)-127)); // -(2^7 - 1) - assertByteEquals((byte)-128, $opt$ShortToByte((short)-128)); // -(2^7) - assertByteEquals((byte)-128, $opt$ShortToByte((short)128)); // 2^7 - assertByteEquals((byte)127, $opt$ShortToByte((short)-129)); // -(2^7 + 1) - assertByteEquals((byte)-1, $opt$ShortToByte((short)32767)); // 2^15 - 1 - assertByteEquals((byte)0, $opt$ShortToByte((short)-32768)); // -(2^15) + assertByteEquals((byte)1, $opt$noinline$ShortToByte((short)1)); + assertByteEquals((byte)0, $opt$noinline$ShortToByte((short)0)); + assertByteEquals((byte)-1, $opt$noinline$ShortToByte((short)-1)); + assertByteEquals((byte)51, $opt$noinline$ShortToByte((short)51)); + assertByteEquals((byte)-51, $opt$noinline$ShortToByte((short)-51)); + assertByteEquals((byte)127, $opt$noinline$ShortToByte((short)127)); // 2^7 - 1 + assertByteEquals((byte)-127, $opt$noinline$ShortToByte((short)-127)); // -(2^7 - 1) + assertByteEquals((byte)-128, $opt$noinline$ShortToByte((short)-128)); // -(2^7) + assertByteEquals((byte)-128, $opt$noinline$ShortToByte((short)128)); // 2^7 + assertByteEquals((byte)127, $opt$noinline$ShortToByte((short)-129)); // -(2^7 + 1) + assertByteEquals((byte)-1, $opt$noinline$ShortToByte((short)32767)); // 2^15 - 1 + assertByteEquals((byte)0, $opt$noinline$ShortToByte((short)-32768)); // -(2^15) } private static void intToByte() { - assertByteEquals((byte)1, $opt$IntToByte(1)); - assertByteEquals((byte)0, $opt$IntToByte(0)); - assertByteEquals((byte)-1, $opt$IntToByte(-1)); - assertByteEquals((byte)51, $opt$IntToByte(51)); - assertByteEquals((byte)-51, $opt$IntToByte(-51)); - assertByteEquals((byte)127, $opt$IntToByte(127)); // 2^7 - 1 - assertByteEquals((byte)-127, $opt$IntToByte(-127)); // -(2^7 - 1) - assertByteEquals((byte)-128, $opt$IntToByte(-128)); // -(2^7) - assertByteEquals((byte)-128, $opt$IntToByte(128)); // 2^7 - assertByteEquals((byte)127, $opt$IntToByte(-129)); // -(2^7 + 1) - assertByteEquals((byte)-1, $opt$IntToByte(2147483647)); // 2^31 - 1 - assertByteEquals((byte)0, $opt$IntToByte(-2147483648)); // -(2^31) + assertByteEquals((byte)1, $opt$noinline$IntToByte(1)); + assertByteEquals((byte)0, $opt$noinline$IntToByte(0)); + assertByteEquals((byte)-1, $opt$noinline$IntToByte(-1)); + assertByteEquals((byte)51, $opt$noinline$IntToByte(51)); + assertByteEquals((byte)-51, $opt$noinline$IntToByte(-51)); + assertByteEquals((byte)127, $opt$noinline$IntToByte(127)); // 2^7 - 1 + assertByteEquals((byte)-127, $opt$noinline$IntToByte(-127)); // -(2^7 - 1) + assertByteEquals((byte)-128, $opt$noinline$IntToByte(-128)); // -(2^7) + assertByteEquals((byte)-128, $opt$noinline$IntToByte(128)); // 2^7 + assertByteEquals((byte)127, $opt$noinline$IntToByte(-129)); // -(2^7 + 1) + assertByteEquals((byte)-1, $opt$noinline$IntToByte(2147483647)); // 2^31 - 1 + assertByteEquals((byte)0, $opt$noinline$IntToByte(-2147483648)); // -(2^31) } private static void charToByte() { - assertByteEquals((byte)1, $opt$CharToByte((char)1)); - assertByteEquals((byte)0, $opt$CharToByte((char)0)); - assertByteEquals((byte)51, $opt$CharToByte((char)51)); - assertByteEquals((byte)127, $opt$CharToByte((char)127)); // 2^7 - 1 - assertByteEquals((byte)-128, $opt$CharToByte((char)128)); // 2^7 - assertByteEquals((byte)-1, $opt$CharToByte((char)32767)); // 2^15 - 1 - assertByteEquals((byte)-1, $opt$CharToByte((char)65535)); // 2^16 - 1 - assertByteEquals((byte)-1, $opt$CharToByte((char)-1)); - assertByteEquals((byte)-51, $opt$CharToByte((char)-51)); - assertByteEquals((byte)-127, $opt$CharToByte((char)-127)); // -(2^7 - 1) - assertByteEquals((byte)-128, $opt$CharToByte((char)-128)); // -(2^7) - assertByteEquals((byte)127, $opt$CharToByte((char)-129)); // -(2^7 + 1) + assertByteEquals((byte)1, $opt$noinline$CharToByte((char)1)); + assertByteEquals((byte)0, $opt$noinline$CharToByte((char)0)); + assertByteEquals((byte)51, $opt$noinline$CharToByte((char)51)); + assertByteEquals((byte)127, $opt$noinline$CharToByte((char)127)); // 2^7 - 1 + assertByteEquals((byte)-128, $opt$noinline$CharToByte((char)128)); // 2^7 + assertByteEquals((byte)-1, $opt$noinline$CharToByte((char)32767)); // 2^15 - 1 + assertByteEquals((byte)-1, $opt$noinline$CharToByte((char)65535)); // 2^16 - 1 + assertByteEquals((byte)-1, $opt$noinline$CharToByte((char)-1)); + assertByteEquals((byte)-51, $opt$noinline$CharToByte((char)-51)); + assertByteEquals((byte)-127, $opt$noinline$CharToByte((char)-127)); // -(2^7 - 1) + assertByteEquals((byte)-128, $opt$noinline$CharToByte((char)-128)); // -(2^7) + assertByteEquals((byte)127, $opt$noinline$CharToByte((char)-129)); // -(2^7 + 1) } private static void byteToShort() { - assertShortEquals((short)1, $opt$ByteToShort((byte)1)); - assertShortEquals((short)0, $opt$ByteToShort((byte)0)); - assertShortEquals((short)-1, $opt$ByteToShort((byte)-1)); - assertShortEquals((short)51, $opt$ByteToShort((byte)51)); - assertShortEquals((short)-51, $opt$ByteToShort((byte)-51)); - assertShortEquals((short)127, $opt$ByteToShort((byte)127)); // 2^7 - 1 - assertShortEquals((short)-127, $opt$ByteToShort((byte)-127)); // -(2^7 - 1) - assertShortEquals((short)-128, $opt$ByteToShort((byte)-128)); // -(2^7) + assertShortEquals((short)1, $opt$noinline$ByteToShort((byte)1)); + assertShortEquals((short)0, $opt$noinline$ByteToShort((byte)0)); + assertShortEquals((short)-1, $opt$noinline$ByteToShort((byte)-1)); + assertShortEquals((short)51, $opt$noinline$ByteToShort((byte)51)); + assertShortEquals((short)-51, $opt$noinline$ByteToShort((byte)-51)); + assertShortEquals((short)127, $opt$noinline$ByteToShort((byte)127)); // 2^7 - 1 + assertShortEquals((short)-127, $opt$noinline$ByteToShort((byte)-127)); // -(2^7 - 1) + assertShortEquals((short)-128, $opt$noinline$ByteToShort((byte)-128)); // -(2^7) } private static void intToShort() { - assertShortEquals((short)1, $opt$IntToShort(1)); - assertShortEquals((short)0, $opt$IntToShort(0)); - assertShortEquals((short)-1, $opt$IntToShort(-1)); - assertShortEquals((short)51, $opt$IntToShort(51)); - assertShortEquals((short)-51, $opt$IntToShort(-51)); - assertShortEquals((short)32767, $opt$IntToShort(32767)); // 2^15 - 1 - assertShortEquals((short)-32767, $opt$IntToShort(-32767)); // -(2^15 - 1) - assertShortEquals((short)-32768, $opt$IntToShort(-32768)); // -(2^15) - assertShortEquals((short)-32768, $opt$IntToShort(32768)); // 2^15 - assertShortEquals((short)32767, $opt$IntToShort(-32769)); // -(2^15 + 1) - assertShortEquals((short)-1, $opt$IntToShort(2147483647)); // 2^31 - 1 - assertShortEquals((short)0, $opt$IntToShort(-2147483648)); // -(2^31) + assertShortEquals((short)1, $opt$noinline$IntToShort(1)); + assertShortEquals((short)0, $opt$noinline$IntToShort(0)); + assertShortEquals((short)-1, $opt$noinline$IntToShort(-1)); + assertShortEquals((short)51, $opt$noinline$IntToShort(51)); + assertShortEquals((short)-51, $opt$noinline$IntToShort(-51)); + assertShortEquals((short)32767, $opt$noinline$IntToShort(32767)); // 2^15 - 1 + assertShortEquals((short)-32767, $opt$noinline$IntToShort(-32767)); // -(2^15 - 1) + assertShortEquals((short)-32768, $opt$noinline$IntToShort(-32768)); // -(2^15) + assertShortEquals((short)-32768, $opt$noinline$IntToShort(32768)); // 2^15 + assertShortEquals((short)32767, $opt$noinline$IntToShort(-32769)); // -(2^15 + 1) + assertShortEquals((short)-1, $opt$noinline$IntToShort(2147483647)); // 2^31 - 1 + assertShortEquals((short)0, $opt$noinline$IntToShort(-2147483648)); // -(2^31) } private static void charToShort() { - assertShortEquals((short)1, $opt$CharToShort((char)1)); - assertShortEquals((short)0, $opt$CharToShort((char)0)); - assertShortEquals((short)51, $opt$CharToShort((char)51)); - assertShortEquals((short)32767, $opt$CharToShort((char)32767)); // 2^15 - 1 - assertShortEquals((short)-32768, $opt$CharToShort((char)32768)); // 2^15 - assertShortEquals((short)-32767, $opt$CharToShort((char)32769)); // 2^15 - assertShortEquals((short)-1, $opt$CharToShort((char)65535)); // 2^16 - 1 - assertShortEquals((short)-1, $opt$CharToShort((char)-1)); - assertShortEquals((short)-51, $opt$CharToShort((char)-51)); - assertShortEquals((short)-32767, $opt$CharToShort((char)-32767)); // -(2^15 - 1) - assertShortEquals((short)-32768, $opt$CharToShort((char)-32768)); // -(2^15) - assertShortEquals((short)32767, $opt$CharToShort((char)-32769)); // -(2^15 + 1) + assertShortEquals((short)1, $opt$noinline$CharToShort((char)1)); + assertShortEquals((short)0, $opt$noinline$CharToShort((char)0)); + assertShortEquals((short)51, $opt$noinline$CharToShort((char)51)); + assertShortEquals((short)32767, $opt$noinline$CharToShort((char)32767)); // 2^15 - 1 + assertShortEquals((short)-32768, $opt$noinline$CharToShort((char)32768)); // 2^15 + assertShortEquals((short)-32767, $opt$noinline$CharToShort((char)32769)); // 2^15 + assertShortEquals((short)-1, $opt$noinline$CharToShort((char)65535)); // 2^16 - 1 + assertShortEquals((short)-1, $opt$noinline$CharToShort((char)-1)); + assertShortEquals((short)-51, $opt$noinline$CharToShort((char)-51)); + assertShortEquals((short)-32767, $opt$noinline$CharToShort((char)-32767)); // -(2^15 - 1) + assertShortEquals((short)-32768, $opt$noinline$CharToShort((char)-32768)); // -(2^15) + assertShortEquals((short)32767, $opt$noinline$CharToShort((char)-32769)); // -(2^15 + 1) } private static void byteToChar() { - assertCharEquals((char)1, $opt$ByteToChar((byte)1)); - assertCharEquals((char)0, $opt$ByteToChar((byte)0)); - assertCharEquals((char)65535, $opt$ByteToChar((byte)-1)); - assertCharEquals((char)51, $opt$ByteToChar((byte)51)); - assertCharEquals((char)65485, $opt$ByteToChar((byte)-51)); - assertCharEquals((char)127, $opt$ByteToChar((byte)127)); // 2^7 - 1 - assertCharEquals((char)65409, $opt$ByteToChar((byte)-127)); // -(2^7 - 1) - assertCharEquals((char)65408, $opt$ByteToChar((byte)-128)); // -(2^7) + assertCharEquals((char)1, $opt$noinline$ByteToChar((byte)1)); + assertCharEquals((char)0, $opt$noinline$ByteToChar((byte)0)); + assertCharEquals((char)65535, $opt$noinline$ByteToChar((byte)-1)); + assertCharEquals((char)51, $opt$noinline$ByteToChar((byte)51)); + assertCharEquals((char)65485, $opt$noinline$ByteToChar((byte)-51)); + assertCharEquals((char)127, $opt$noinline$ByteToChar((byte)127)); // 2^7 - 1 + assertCharEquals((char)65409, $opt$noinline$ByteToChar((byte)-127)); // -(2^7 - 1) + assertCharEquals((char)65408, $opt$noinline$ByteToChar((byte)-128)); // -(2^7) } private static void shortToChar() { - assertCharEquals((char)1, $opt$ShortToChar((short)1)); - assertCharEquals((char)0, $opt$ShortToChar((short)0)); - assertCharEquals((char)65535, $opt$ShortToChar((short)-1)); - assertCharEquals((char)51, $opt$ShortToChar((short)51)); - assertCharEquals((char)65485, $opt$ShortToChar((short)-51)); - assertCharEquals((char)32767, $opt$ShortToChar((short)32767)); // 2^15 - 1 - assertCharEquals((char)32769, $opt$ShortToChar((short)-32767)); // -(2^15 - 1) - assertCharEquals((char)32768, $opt$ShortToChar((short)-32768)); // -(2^15) + assertCharEquals((char)1, $opt$noinline$ShortToChar((short)1)); + assertCharEquals((char)0, $opt$noinline$ShortToChar((short)0)); + assertCharEquals((char)65535, $opt$noinline$ShortToChar((short)-1)); + assertCharEquals((char)51, $opt$noinline$ShortToChar((short)51)); + assertCharEquals((char)65485, $opt$noinline$ShortToChar((short)-51)); + assertCharEquals((char)32767, $opt$noinline$ShortToChar((short)32767)); // 2^15 - 1 + assertCharEquals((char)32769, $opt$noinline$ShortToChar((short)-32767)); // -(2^15 - 1) + assertCharEquals((char)32768, $opt$noinline$ShortToChar((short)-32768)); // -(2^15) } private static void intToChar() { - assertCharEquals((char)1, $opt$IntToChar(1)); - assertCharEquals((char)0, $opt$IntToChar(0)); - assertCharEquals((char)65535, $opt$IntToChar(-1)); - assertCharEquals((char)51, $opt$IntToChar(51)); - assertCharEquals((char)65485, $opt$IntToChar(-51)); - assertCharEquals((char)32767, $opt$IntToChar(32767)); // 2^15 - 1 - assertCharEquals((char)32769, $opt$IntToChar(-32767)); // -(2^15 - 1) - assertCharEquals((char)32768, $opt$IntToChar(32768)); // 2^15 - assertCharEquals((char)32768, $opt$IntToChar(-32768)); // -(2^15) - assertCharEquals((char)65535, $opt$IntToChar(65535)); // 2^16 - 1 - assertCharEquals((char)1, $opt$IntToChar(-65535)); // -(2^16 - 1) - assertCharEquals((char)0, $opt$IntToChar(65536)); // 2^16 - assertCharEquals((char)0, $opt$IntToChar(-65536)); // -(2^16) - assertCharEquals((char)65535, $opt$IntToChar(2147483647)); // 2^31 - 1 - assertCharEquals((char)0, $opt$IntToChar(-2147483648)); // -(2^31) + assertCharEquals((char)1, $opt$noinline$IntToChar(1)); + assertCharEquals((char)0, $opt$noinline$IntToChar(0)); + assertCharEquals((char)65535, $opt$noinline$IntToChar(-1)); + assertCharEquals((char)51, $opt$noinline$IntToChar(51)); + assertCharEquals((char)65485, $opt$noinline$IntToChar(-51)); + assertCharEquals((char)32767, $opt$noinline$IntToChar(32767)); // 2^15 - 1 + assertCharEquals((char)32769, $opt$noinline$IntToChar(-32767)); // -(2^15 - 1) + assertCharEquals((char)32768, $opt$noinline$IntToChar(32768)); // 2^15 + assertCharEquals((char)32768, $opt$noinline$IntToChar(-32768)); // -(2^15) + assertCharEquals((char)65535, $opt$noinline$IntToChar(65535)); // 2^16 - 1 + assertCharEquals((char)1, $opt$noinline$IntToChar(-65535)); // -(2^16 - 1) + assertCharEquals((char)0, $opt$noinline$IntToChar(65536)); // 2^16 + assertCharEquals((char)0, $opt$noinline$IntToChar(-65536)); // -(2^16) + assertCharEquals((char)65535, $opt$noinline$IntToChar(2147483647)); // 2^31 - 1 + assertCharEquals((char)0, $opt$noinline$IntToChar(-2147483648)); // -(2^31) } // A dummy value to defeat inlining of these routines. static boolean doThrow = false; // These methods produce int-to-long Dex instructions. - static long $opt$ByteToLong(byte a) { if (doThrow) throw new Error(); return (long)a; } - static long $opt$ShortToLong(short a) { if (doThrow) throw new Error(); return (long)a; } - static long $opt$IntToLong(int a) { if (doThrow) throw new Error(); return (long)a; } - static long $opt$CharToLong(int a) { if (doThrow) throw new Error(); return (long)a; } + static long $opt$noinline$ByteToLong(byte a) { if (doThrow) throw new Error(); return (long)a; } + static long $opt$noinline$ShortToLong(short a) { if (doThrow) throw new Error(); return (long)a; } + static long $opt$noinline$IntToLong(int a) { if (doThrow) throw new Error(); return (long)a; } + static long $opt$noinline$CharToLong(int a) { if (doThrow) throw new Error(); return (long)a; } // These methods produce int-to-float Dex instructions. - static float $opt$ByteToFloat(byte a) { if (doThrow) throw new Error(); return (float)a; } - static float $opt$ShortToFloat(short a) { if (doThrow) throw new Error(); return (float)a; } - static float $opt$IntToFloat(int a) { if (doThrow) throw new Error(); return (float)a; } - static float $opt$CharToFloat(char a) { if (doThrow) throw new Error(); return (float)a; } + static float $opt$noinline$ByteToFloat(byte a) { if (doThrow) throw new Error(); return (float)a; } + static float $opt$noinline$ShortToFloat(short a) { if (doThrow) throw new Error(); return (float)a; } + static float $opt$noinline$IntToFloat(int a) { if (doThrow) throw new Error(); return (float)a; } + static float $opt$noinline$CharToFloat(char a) { if (doThrow) throw new Error(); return (float)a; } // These methods produce int-to-double Dex instructions. - static double $opt$ByteToDouble(byte a) { if (doThrow) throw new Error(); return (double)a; } - static double $opt$ShortToDouble(short a) { if (doThrow) throw new Error(); return (double)a; } - static double $opt$IntToDouble(int a) { if (doThrow) throw new Error(); return (double)a; } - static double $opt$CharToDouble(int a) { if (doThrow) throw new Error(); return (double)a; } + static double $opt$noinline$ByteToDouble(byte a) { if (doThrow) throw new Error(); return (double)a; } + static double $opt$noinline$ShortToDouble(short a) { if (doThrow) throw new Error(); return (double)a; } + static double $opt$noinline$IntToDouble(int a) { if (doThrow) throw new Error(); return (double)a; } + static double $opt$noinline$CharToDouble(int a) { if (doThrow) throw new Error(); return (double)a; } // These methods produce long-to-int Dex instructions. - static int $opt$LongToInt(long a) { if (doThrow) throw new Error(); return (int)a; } - static int $opt$LongLiteralToInt() { if (doThrow) throw new Error(); return (int)42L; } + static int $opt$noinline$LongToInt(long a) { if (doThrow) throw new Error(); return (int)a; } + static int $opt$noinline$LongLiteralToInt() { if (doThrow) throw new Error(); return (int)42L; } // This method produces a long-to-float Dex instruction. - static float $opt$LongToFloat(long a) { if (doThrow) throw new Error(); return (float)a; } + static float $opt$noinline$LongToFloat(long a) { if (doThrow) throw new Error(); return (float)a; } // This method produces a long-to-double Dex instruction. - static double $opt$LongToDouble(long a) { if (doThrow) throw new Error(); return (double)a; } + static double $opt$noinline$LongToDouble(long a) { if (doThrow) throw new Error(); return (double)a; } // This method produces a float-to-int Dex instruction. - static int $opt$FloatToInt(float a) { if (doThrow) throw new Error(); return (int)a; } + static int $opt$noinline$FloatToInt(float a) { if (doThrow) throw new Error(); return (int)a; } // This method produces a float-to-long Dex instruction. - static long $opt$FloatToLong(float a){ if (doThrow) throw new Error(); return (long)a; } + static long $opt$noinline$FloatToLong(float a){ if (doThrow) throw new Error(); return (long)a; } // This method produces a float-to-double Dex instruction. - static double $opt$FloatToDouble(float a) { if (doThrow) throw new Error(); return (double)a; } + static double $opt$noinline$FloatToDouble(float a) { if (doThrow) throw new Error(); return (double)a; } // This method produces a double-to-int Dex instruction. - static int $opt$DoubleToInt(double a){ if (doThrow) throw new Error(); return (int)a; } + static int $opt$noinline$DoubleToInt(double a){ if (doThrow) throw new Error(); return (int)a; } // This method produces a double-to-long Dex instruction. - static long $opt$DoubleToLong(double a){ if (doThrow) throw new Error(); return (long)a; } + static long $opt$noinline$DoubleToLong(double a){ if (doThrow) throw new Error(); return (long)a; } // This method produces a double-to-float Dex instruction. - static float $opt$DoubleToFloat(double a) { if (doThrow) throw new Error(); return (float)a; } + static float $opt$noinline$DoubleToFloat(double a) { if (doThrow) throw new Error(); return (float)a; } // These methods produce int-to-byte Dex instructions. - static byte $opt$ShortToByte(short a) { if (doThrow) throw new Error(); return (byte)a; } - static byte $opt$IntToByte(int a) { if (doThrow) throw new Error(); return (byte)a; } - static byte $opt$CharToByte(char a) { if (doThrow) throw new Error(); return (byte)a; } + static byte $opt$noinline$ShortToByte(short a) { if (doThrow) throw new Error(); return (byte)a; } + static byte $opt$noinline$IntToByte(int a) { if (doThrow) throw new Error(); return (byte)a; } + static byte $opt$noinline$CharToByte(char a) { if (doThrow) throw new Error(); return (byte)a; } // These methods produce int-to-short Dex instructions. - static short $opt$ByteToShort(byte a) { if (doThrow) throw new Error(); return (short)a; } - static short $opt$IntToShort(int a) { if (doThrow) throw new Error(); return (short)a; } - static short $opt$CharToShort(char a) { if (doThrow) throw new Error(); return (short)a; } + static short $opt$noinline$ByteToShort(byte a) { if (doThrow) throw new Error(); return (short)a; } + static short $opt$noinline$IntToShort(int a) { if (doThrow) throw new Error(); return (short)a; } + static short $opt$noinline$CharToShort(char a) { if (doThrow) throw new Error(); return (short)a; } // These methods produce int-to-char Dex instructions. - static char $opt$ByteToChar(byte a) { if (doThrow) throw new Error(); return (char)a; } - static char $opt$ShortToChar(short a) { if (doThrow) throw new Error(); return (char)a; } - static char $opt$IntToChar(int a) { if (doThrow) throw new Error(); return (char)a; } + static char $opt$noinline$ByteToChar(byte a) { if (doThrow) throw new Error(); return (char)a; } + static char $opt$noinline$ShortToChar(short a) { if (doThrow) throw new Error(); return (char)a; } + static char $opt$noinline$IntToChar(int a) { if (doThrow) throw new Error(); return (char)a; } } diff --git a/test/444-checker-nce/src/Main.java b/test/444-checker-nce/src/Main.java index 6ac0cad7e8..32122e4dcd 100644 --- a/test/444-checker-nce/src/Main.java +++ b/test/444-checker-nce/src/Main.java @@ -27,37 +27,37 @@ public class Main { return m.g(); } - /// CHECK-START: Main Main.thisTest() instruction_simplifier (before) + /// CHECK-START: Main Main.thisTest() ssa_builder (after) /// CHECK: NullCheck /// CHECK: InvokeStaticOrDirect - /// CHECK-START: Main Main.thisTest() instruction_simplifier (after) + /// CHECK-START: Main Main.thisTest() instruction_simplifier_after_types (after) /// CHECK-NOT: NullCheck /// CHECK: InvokeStaticOrDirect public Main thisTest() { return g(); } - /// CHECK-START: Main Main.newInstanceRemoveTest() instruction_simplifier (before) + /// CHECK-START: Main Main.newInstanceRemoveTest() ssa_builder (after) /// CHECK: NewInstance /// CHECK: NullCheck /// CHECK: InvokeStaticOrDirect /// CHECK: NullCheck /// CHECK: InvokeStaticOrDirect - /// CHECK-START: Main Main.newInstanceRemoveTest() instruction_simplifier (after) + /// CHECK-START: Main Main.newInstanceRemoveTest() instruction_simplifier_after_types (after) /// CHECK-NOT: NullCheck public Main newInstanceRemoveTest() { Main m = new Main(); return m.g(); } - /// CHECK-START: Main Main.newArrayRemoveTest() instruction_simplifier (before) + /// CHECK-START: Main Main.newArrayRemoveTest() ssa_builder (after) /// CHECK: NewArray /// CHECK: NullCheck /// CHECK: ArrayGet - /// CHECK-START: Main Main.newArrayRemoveTest() instruction_simplifier (after) + /// CHECK-START: Main Main.newArrayRemoveTest() instruction_simplifier_after_types (after) /// CHECK: NewArray /// CHECK-NOT: NullCheck /// CHECK: ArrayGet @@ -178,10 +178,10 @@ public class Main { return n.g(); } - /// CHECK-START: Main Main.scopeRemoveTest(int, Main) instruction_simplifier (before) + /// CHECK-START: Main Main.scopeRemoveTest(int, Main) ssa_builder (after) /// CHECK: NullCheck - /// CHECK-START: Main Main.scopeRemoveTest(int, Main) instruction_simplifier (after) + /// CHECK-START: Main Main.scopeRemoveTest(int, Main) instruction_simplifier_after_types (after) /// CHECK-NOT: NullCheck public Main scopeRemoveTest(int count, Main a) { Main m = null; diff --git a/test/450-checker-types/src/Main.java b/test/450-checker-types/src/Main.java index 9bf7cdf496..4056275d3d 100644 --- a/test/450-checker-types/src/Main.java +++ b/test/450-checker-types/src/Main.java @@ -16,25 +16,25 @@ interface Interface { - void f(); + void $noinline$f(); } class Super implements Interface { - public void f() { + public void $noinline$f() { throw new RuntimeException(); } } class SubclassA extends Super { - public void f() { + public void $noinline$f() { throw new RuntimeException(); } - public String h() { + public String $noinline$h() { throw new RuntimeException(); } - void g() { + void $noinline$g() { throw new RuntimeException(); } } @@ -43,11 +43,11 @@ class SubclassC extends SubclassA { } class SubclassB extends Super { - public void f() { + public void $noinline$f() { throw new RuntimeException(); } - void g() { + void $noinline$g() { throw new RuntimeException(); } } @@ -61,7 +61,7 @@ public class Main { /// CHECK-NOT: CheckCast public void testSimpleRemove() { Super s = new SubclassA(); - ((SubclassA)s).g(); + ((SubclassA)s).$noinline$g(); } /// CHECK-START: void Main.testSimpleKeep(Super) instruction_simplifier_after_types (before) @@ -70,7 +70,7 @@ public class Main { /// CHECK-START: void Main.testSimpleKeep(Super) instruction_simplifier_after_types (after) /// CHECK: CheckCast public void testSimpleKeep(Super s) { - ((SubclassA)s).f(); + ((SubclassA)s).$noinline$f(); } /// CHECK-START: java.lang.String Main.testClassRemove() instruction_simplifier_after_types (before) @@ -90,7 +90,7 @@ public class Main { /// CHECK: CheckCast public String testClassKeep() { Object s = SubclassA.class; - return ((SubclassA)s).h(); + return ((SubclassA)s).$noinline$h(); } /// CHECK-START: void Main.testIfRemove(int) instruction_simplifier_after_types (before) @@ -105,7 +105,7 @@ public class Main { } else { s = new SubclassC(); } - ((SubclassA)s).g(); + ((SubclassA)s).$noinline$g(); } /// CHECK-START: void Main.testIfKeep(int) instruction_simplifier_after_types (before) @@ -120,7 +120,7 @@ public class Main { } else { s = new SubclassB(); } - ((SubclassA)s).g(); + ((SubclassA)s).$noinline$g(); } /// CHECK-START: void Main.testForRemove(int) instruction_simplifier_after_types (before) @@ -135,7 +135,7 @@ public class Main { s = new SubclassC(); } } - ((SubclassA)s).g(); + ((SubclassA)s).$noinline$g(); } /// CHECK-START: void Main.testForKeep(int) instruction_simplifier_after_types (before) @@ -150,7 +150,7 @@ public class Main { s = new SubclassC(); } } - ((SubclassC)s).g(); + ((SubclassC)s).$noinline$g(); } /// CHECK-START: void Main.testPhiFromCall(int) instruction_simplifier_after_types (before) @@ -165,7 +165,7 @@ public class Main { } else { x = newObject(); // this one will have an unknown type. } - ((SubclassC)x).g(); + ((SubclassC)x).$noinline$g(); } /// CHECK-START: void Main.testInstanceOf(java.lang.Object) instruction_simplifier_after_types (before) @@ -176,10 +176,10 @@ public class Main { /// CHECK-NOT: CheckCast public void testInstanceOf(Object o) { if (o instanceof SubclassC) { - ((SubclassC)o).g(); + ((SubclassC)o).$noinline$g(); } if (o instanceof SubclassB) { - ((SubclassB)o).g(); + ((SubclassB)o).$noinline$g(); } } @@ -192,10 +192,10 @@ public class Main { /// CHECK: CheckCast public void testInstanceOfKeep(Object o) { if (o instanceof SubclassC) { - ((SubclassB)o).g(); + ((SubclassB)o).$noinline$g(); } if (o instanceof SubclassB) { - ((SubclassA)o).g(); + ((SubclassA)o).$noinline$g(); } } @@ -208,9 +208,9 @@ public class Main { public void testInstanceOfNested(Object o) { if (o instanceof SubclassC) { if (o instanceof SubclassB) { - ((SubclassB)o).g(); + ((SubclassB)o).$noinline$g(); } else { - ((SubclassC)o).g(); + ((SubclassC)o).$noinline$g(); } } } @@ -229,7 +229,7 @@ public class Main { } if (o instanceof SubclassB) { - ((SubclassB)o).g(); + ((SubclassB)o).$noinline$g(); } } @@ -245,7 +245,7 @@ public class Main { o = new SubclassB(); } if (o instanceof SubclassB) { - ((SubclassB)o).g(); + ((SubclassB)o).$noinline$g(); } } } @@ -258,7 +258,7 @@ public class Main { public void testInstanceOfSubclass() { Object o = new SubclassA(); if (o instanceof Super) { - ((SubclassA)o).g(); + ((SubclassA)o).$noinline$g(); } } @@ -276,7 +276,7 @@ public class Main { } if (o instanceof Super) { - ((SubclassA)o).g(); + ((SubclassA)o).$noinline$g(); } } @@ -294,7 +294,7 @@ public class Main { } if (o instanceof Super) { - ((Super)o).f(); + ((Super)o).$noinline$f(); } } @@ -307,7 +307,7 @@ public class Main { Object o = new SubclassA(); for (int i = 0; i < n; i++) { if (o instanceof Super) { - ((SubclassA)o).g(); + ((SubclassA)o).$noinline$g(); } if (i / 2 == 0) { o = new SubclassC(); @@ -324,7 +324,7 @@ public class Main { Object o = new SubclassA(); for (int i = 0; i < n; i++) { if (o instanceof Super) { - ((Super)o).f(); + ((Super)o).$noinline$f(); } if (i / 2 == 0) { o = new Object(); @@ -351,7 +351,7 @@ public class Main { public void testInstanceFieldGetSimpleRemove() { Main m = new Main(); Super a = m.a; - ((SubclassA)a).g(); + ((SubclassA)a).$noinline$g(); } /// CHECK-START: void Main.testStaticFieldGetSimpleRemove() instruction_simplifier_after_types (before) @@ -361,7 +361,7 @@ public class Main { /// CHECK-NOT: CheckCast public void testStaticFieldGetSimpleRemove() { Super b = Main.b; - ((SubclassA)b).g(); + ((SubclassA)b).$noinline$g(); } public static void main(String[] args) { diff --git a/test/451-spill-splot/src/Main.java b/test/451-spill-splot/src/Main.java index f631ebdd6d..b2f39f389d 100644 --- a/test/451-spill-splot/src/Main.java +++ b/test/451-spill-splot/src/Main.java @@ -48,37 +48,37 @@ public class Main { for (int count = 0; count < 2; count++) { System.out.println(aa + bb + cc + dd + ee + ff + gg + hh + ii + jj + kk + ll + mm + nn); System.out.println(a + b + c + d + e + f + g + h + i + j); - a = computeDouble(); - b = computeDouble(); - c = computeDouble(); - d = computeDouble(); - e = computeDouble(); - f = computeDouble(); - g = computeDouble(); - h = computeDouble(); - i = computeDouble(); - j = computeDouble(); + a = $noinline$computeDouble(); + b = $noinline$computeDouble(); + c = $noinline$computeDouble(); + d = $noinline$computeDouble(); + e = $noinline$computeDouble(); + f = $noinline$computeDouble(); + g = $noinline$computeDouble(); + h = $noinline$computeDouble(); + i = $noinline$computeDouble(); + j = $noinline$computeDouble(); System.out.println(a + b + c + d + e + f + g + h + i + j); - aa = computeFloat(); - bb = computeFloat(); - cc = computeFloat(); - dd = computeFloat(); - ee = computeFloat(); - ff = computeFloat(); - gg = computeFloat(); - hh = computeFloat(); - ii = computeFloat(); - jj = computeFloat(); - kk = computeFloat(); - ll = computeFloat(); - mm = computeFloat(); - nn = computeFloat(); + aa = $noinline$computeFloat(); + bb = $noinline$computeFloat(); + cc = $noinline$computeFloat(); + dd = $noinline$computeFloat(); + ee = $noinline$computeFloat(); + ff = $noinline$computeFloat(); + gg = $noinline$computeFloat(); + hh = $noinline$computeFloat(); + ii = $noinline$computeFloat(); + jj = $noinline$computeFloat(); + kk = $noinline$computeFloat(); + ll = $noinline$computeFloat(); + mm = $noinline$computeFloat(); + nn = $noinline$computeFloat(); } } static boolean doThrow = false; - public static double computeDouble() { + public static double $noinline$computeDouble() { if (doThrow) { // Try defeating inlining. throw new Error(); @@ -86,7 +86,7 @@ public class Main { return 2.0; } - public static float computeFloat() { + public static float $noinline$computeFloat() { if (doThrow) { // Try defeating inlining. throw new Error(); diff --git a/test/458-checker-instruction-simplification/src/Main.java b/test/458-checker-instruction-simplification/src/Main.java index ad5fc8ef93..ef18f64a37 100644 --- a/test/458-checker-instruction-simplification/src/Main.java +++ b/test/458-checker-instruction-simplification/src/Main.java @@ -933,18 +933,18 @@ public class Main { * remove the second. */ - /// CHECK-START: boolean Main.NotNotBool(boolean) instruction_simplifier_after_types (before) + /// CHECK-START: boolean Main.NotNotBool(boolean) last_instruction_simplifier (before) /// CHECK-DAG: <<Arg:z\d+>> ParameterValue /// CHECK-DAG: <<NotArg:z\d+>> BooleanNot [<<Arg>>] /// CHECK-DAG: <<NotNotArg:z\d+>> BooleanNot [<<NotArg>>] /// CHECK-DAG: Return [<<NotNotArg>>] - /// CHECK-START: boolean Main.NotNotBool(boolean) instruction_simplifier_after_types (after) + /// CHECK-START: boolean Main.NotNotBool(boolean) last_instruction_simplifier (after) /// CHECK-DAG: <<Arg:z\d+>> ParameterValue /// CHECK-DAG: BooleanNot [<<Arg>>] /// CHECK-DAG: Return [<<Arg>>] - /// CHECK-START: boolean Main.NotNotBool(boolean) instruction_simplifier_after_types (after) + /// CHECK-START: boolean Main.NotNotBool(boolean) last_instruction_simplifier (after) /// CHECK: BooleanNot /// CHECK-NOT: BooleanNot diff --git a/test/478-checker-clinit-check-pruning/src/Main.java b/test/478-checker-clinit-check-pruning/src/Main.java index 51be912b96..a2c98c9363 100644 --- a/test/478-checker-clinit-check-pruning/src/Main.java +++ b/test/478-checker-clinit-check-pruning/src/Main.java @@ -90,7 +90,7 @@ public class Main { /// CHECK-NOT: ClinitCheck static void invokeStaticNotInlined() { - ClassWithClinit2.staticMethod(); + ClassWithClinit2.$noinline$staticMethod(); } static class ClassWithClinit2 { @@ -100,7 +100,7 @@ public class Main { static boolean doThrow = false; - static void staticMethod() { + static void $noinline$staticMethod() { if (doThrow) { // Try defeating inlining. throw new Error(); @@ -169,7 +169,7 @@ public class Main { // initialization of ClassWithClinit4, meaning that the // call to staticMethod below does not need a clinit // check. - staticMethod(); + $noinline$staticMethod(); } static { @@ -178,7 +178,7 @@ public class Main { static boolean doThrow = false; - static void staticMethod() { + static void $noinline$staticMethod() { if (doThrow) { // Try defeating inlining. throw new Error(); @@ -242,7 +242,7 @@ public class Main { static class ClassWithClinit6 { static boolean doThrow = false; - static void staticMethod() { + static void $noinline$staticMethod() { if (doThrow) { // Try defeating inlining. throw new Error(); @@ -256,7 +256,7 @@ public class Main { static class SubClassOfClassWithClinit6 extends ClassWithClinit6 { static void invokeStaticNotInlined() { - ClassWithClinit6.staticMethod(); + ClassWithClinit6.$noinline$staticMethod(); } } @@ -276,7 +276,7 @@ public class Main { /// CHECK-NOT: ClinitCheck static void noClinitBecauseOfInvokeStatic() { - ClassWithClinit2.staticMethod(); + ClassWithClinit2.$noinline$staticMethod(); ClassWithClinit2.doThrow = false; } @@ -295,7 +295,7 @@ public class Main { /// CHECK-NOT: ClinitCheck static void clinitBecauseOfFieldAccess() { ClassWithClinit2.doThrow = false; - ClassWithClinit2.staticMethod(); + ClassWithClinit2.$noinline$staticMethod(); } // TODO: Add a test for the case of a static method whose declaring diff --git a/test/486-checker-must-do-null-check/src/Main.java b/test/486-checker-must-do-null-check/src/Main.java index ea72718ae0..e8ff6a4799 100644 --- a/test/486-checker-must-do-null-check/src/Main.java +++ b/test/486-checker-must-do-null-check/src/Main.java @@ -36,16 +36,16 @@ public class Main { /// CHECK: CheckCast must_do_null_check:false public void CheckCastPreChecked(Object o) { o.toString(); - ((Main)o).Bar(); + ((Main)o).$noinline$Bar(); } /// CHECK-START: void Main.CheckCast(java.lang.Object) instruction_simplifier (after) /// CHECK: CheckCast must_do_null_check:true public void CheckCast(Object o) { - ((Main)o).Bar(); + ((Main)o).$noinline$Bar(); } - void Bar() {throw new RuntimeException();} + void $noinline$Bar() {throw new RuntimeException();} public static void main(String[] sa) { Main t = new Main(); diff --git a/test/490-checker-inline/expected.txt b/test/490-checker-inline/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/490-checker-inline/expected.txt diff --git a/test/490-checker-inline/info.txt b/test/490-checker-inline/info.txt new file mode 100644 index 0000000000..0e42d771fe --- /dev/null +++ b/test/490-checker-inline/info.txt @@ -0,0 +1 @@ +Check that we inline virtual and interface calls. diff --git a/test/490-checker-inline/src/Main.java b/test/490-checker-inline/src/Main.java new file mode 100644 index 0000000000..21a01897e2 --- /dev/null +++ b/test/490-checker-inline/src/Main.java @@ -0,0 +1,52 @@ +/* + * 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. + */ + +interface Itf { + public void invokeInterface(); +} + +public class Main implements Itf { + + public void invokeInterface () { + } + + public void invokeVirtual() { + } + + public static Main createMain() { + return new Main(); + } + + public static Itf createItf() { + return new Main(); + } + + /// CHECK-START: void Main.testMethod() inliner (before) + /// CHECK-DAG: InvokeVirtual + /// CHECK-DAG: InvokeInterface + + /// CHECK-START: void Main.testMethod() inliner (after) + /// CHECK-NOT: Invoke{{.*}} + + public static void testMethod() { + createMain().invokeVirtual(); + createItf().invokeInterface(); + } + + public static void main(String[] args) { + testMethod(); + } +} |