From 7cd8fda37efa5a6b6590c6feb94d66f2ce7a0700 Mon Sep 17 00:00:00 2001 From: Tess Strickland Date: Wed, 27 May 2026 08:18:27 -0700 Subject: [PATCH] [vm] Use Uint32Arrays for coverage arrays. Also load/store canonical hashes in the heap for non-empty TypedData instances in the same manner as canonical hashes for Arrays. TEST=ci (refactoring only) Cq-Include-Trybots: luci.dart.try:vm-dyn-linux-debug-x64-try Change-Id: I54274b558fa9f0c8e304198b18cb3f0e9c3e0dfb Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/504600 Commit-Queue: Tess Strickland Reviewed-by: Ryan Macnak --- runtime/vm/compiler/backend/flow_graph.h | 6 +-- runtime/vm/compiler/backend/il.cc | 11 +++-- runtime/vm/compiler/backend/il.h | 4 +- runtime/vm/compiler/backend/il_serializer.cc | 30 ++++++++++-- runtime/vm/compiler/backend/il_serializer.h | 1 + runtime/vm/compiler/backend/il_test.cc | 5 +- .../frontend/base_flow_graph_builder.cc | 21 ++++---- .../frontend/base_flow_graph_builder.h | 10 ++-- runtime/vm/hash.h | 6 ++- runtime/vm/interpreter.cc | 17 +++---- runtime/vm/object.cc | 48 ++++++++++++------- runtime/vm/object.h | 11 +++-- runtime/vm/raw_object.h | 5 +- runtime/vm/roots.h | 3 +- runtime/vm/runtime_entry.cc | 2 +- runtime/vm/source_report.cc | 6 +-- runtime/vm/token_position.cc | 7 ++- 17 files changed, 123 insertions(+), 70 deletions(-) diff --git a/runtime/vm/compiler/backend/flow_graph.h b/runtime/vm/compiler/backend/flow_graph.h index b03babb21df..75adcd7bd01 100644 --- a/runtime/vm/compiler/backend/flow_graph.h +++ b/runtime/vm/compiler/backend/flow_graph.h @@ -601,8 +601,8 @@ class FlowGraph : public ZoneObject { void CreateCommonConstants(); - const Array& coverage_array() const { return *coverage_array_; } - void set_coverage_array(const Array& array) { coverage_array_ = &array; } + const TypedData& coverage_array() const { return *coverage_array_; } + void set_coverage_array(const TypedData& array) { coverage_array_ = &array; } // Renumbers SSA values and basic blocks to make numbering dense. // Preserves order among block ids. @@ -806,7 +806,7 @@ class FlowGraph : public ZoneObject { intptr_t max_argument_slot_count_ = -1; - const Array* coverage_array_ = &Array::empty_array(); + const TypedData* coverage_array_ = &TypedData::empty_coverage_array(); }; class LivenessAnalysis : public ValueObject { diff --git a/runtime/vm/compiler/backend/il.cc b/runtime/vm/compiler/backend/il.cc index e79a7327c72..5d1d3504535 100644 --- a/runtime/vm/compiler/backend/il.cc +++ b/runtime/vm/compiler/backend/il.cc @@ -3159,7 +3159,8 @@ Instruction* DebugStepCheckInstr::Canonicalize(FlowGraph* flow_graph) { Instruction* RecordCoverageInstr::Canonicalize(FlowGraph* flow_graph) { ASSERT(!coverage_array_.IsNull()); - return coverage_array_.At(coverage_index_) != Smi::New(0) ? nullptr : this; + return coverage_array_.GetUint32(coverage_index_ * kInt32Size) != 0 ? nullptr + : this; } Definition* BoxInstr::Canonicalize(FlowGraph* flow_graph) { @@ -8436,10 +8437,10 @@ void RecordCoverageInstr::EmitNativeCode(FlowGraphCompiler* compiler) { __ LoadObject(array_temp, coverage_array_); __ LoadImmediate(value_temp, Smi::RawValue(1)); - __ StoreFieldToOffset( - value_temp, array_temp, - compiler::target::Array::element_offset(coverage_index_), - compiler::kObjectBytes); + __ StoreFieldToOffset(value_temp, array_temp, + compiler::target::TypedData::payload_offset() + + (coverage_index_ * kInt32Size), + compiler::kFourBytes); } #undef Z diff --git a/runtime/vm/compiler/backend/il.h b/runtime/vm/compiler/backend/il.h index 532a222a95a..ca0263939f8 100644 --- a/runtime/vm/compiler/backend/il.h +++ b/runtime/vm/compiler/backend/il.h @@ -7253,7 +7253,7 @@ class StoreIndexedInstr : public TemplateInstruction<3, NoThrow> { class RecordCoverageInstr : public TemplateInstruction<0, NoThrow> { public: - RecordCoverageInstr(const Array& coverage_array, + RecordCoverageInstr(const TypedData& coverage_array, intptr_t coverage_index, const InstructionSource& source) : TemplateInstruction(source), @@ -7270,7 +7270,7 @@ class RecordCoverageInstr : public TemplateInstruction<0, NoThrow> { virtual Instruction* Canonicalize(FlowGraph* flow_graph); #define FIELD_LIST(F) \ - F(const Array&, coverage_array_) \ + F(const TypedData&, coverage_array_) \ F(const intptr_t, coverage_index_) \ F(const TokenPosition, token_pos_) diff --git a/runtime/vm/compiler/backend/il_serializer.cc b/runtime/vm/compiler/backend/il_serializer.cc index 9912a47c181..07608a8f032 100644 --- a/runtime/vm/compiler/backend/il_serializer.cc +++ b/runtime/vm/compiler/backend/il_serializer.cc @@ -679,7 +679,7 @@ void FlowGraphSerializer::WriteFlowGraph( Write(flow_graph.current_ssa_temp_index()); Write(flow_graph.max_block_id()); - Write(flow_graph.coverage_array()); + Write(flow_graph.coverage_array()); PrologueInfo prologue_info = flow_graph.prologue_info(); Write(prologue_info.min_block_id); @@ -734,7 +734,7 @@ void FlowGraphSerializer::WriteFlowGraph( FlowGraph* FlowGraphDeserializer::ReadFlowGraph() { const intptr_t current_ssa_temp_index = Read(); const intptr_t max_block_id = Read(); - const Array& coverage_array = Read(); + const TypedData& coverage_array = Read(); const PrologueInfo prologue_info(Read(), Read()); definitions_.EnsureLength(current_ssa_temp_index, nullptr); @@ -1579,6 +1579,16 @@ void FlowGraphSerializer::WriteObjectImpl(const Object& x, } break; } + case kTypedDataUint32ArrayCid: { + const auto& array = TypedData::Cast(x); + const intptr_t len = array.Length(); + Write(len); + for (intptr_t i = 0; i < len; ++i) { + uint32_t elem = array.GetUint32(i * kInt32Size); + Write(elem); + } + break; + } case kBoolCid: Write(Bool::Cast(x).value()); break; @@ -1876,6 +1886,19 @@ const Object& FlowGraphDeserializer::ReadObjectImpl(intptr_t cid, } return array; } + case kTypedDataUint32ArrayCid: { + const intptr_t len = Read(); + if (len == 0) { + // Currently only used for coverage arrays. + return Object::empty_coverage_array(); + } + auto& array = TypedData::ZoneHandle( + Z, TypedData::New(kTypedDataUint32ArrayCid, len, Heap::kOld)); + for (intptr_t i = 0; i < len; ++i) { + array.SetUint32(i * kInt32Size, Read()); + } + return array; + } case kBoolCid: return Bool::Get(Read()); case kClosureCid: { @@ -2161,7 +2184,8 @@ const Object& FlowGraphDeserializer::ReadObjectImpl(intptr_t cid, V(Instance, Object::null_instance()) \ V(String, Object::null_string()) \ V(TypeArguments, Object::null_type_arguments()) \ - V(TypeParameters, TypeParameters::Handle(d->zone())) + V(TypeParameters, TypeParameters::Handle(d->zone())) \ + V(TypedData, TypedData::Handle(d->zone())) #define SERIALIZE_HANDLE_AS_OBJECT(handle, null_handle) \ template <> \ diff --git a/runtime/vm/compiler/backend/il_serializer.h b/runtime/vm/compiler/backend/il_serializer.h index 62c3b6a1f97..18a61b10938 100644 --- a/runtime/vm/compiler/backend/il_serializer.h +++ b/runtime/vm/compiler/backend/il_serializer.h @@ -117,6 +117,7 @@ class NativeCallingConvention; V(TokenPosition) \ V(const TypeArguments&) \ V(const TypeParameters&) \ + V(const TypedData&) \ V(uint8_t) \ V(uint16_t) \ V(uint32_t) \ diff --git a/runtime/vm/compiler/backend/il_test.cc b/runtime/vm/compiler/backend/il_test.cc index 7a783e7cfd2..3df688225fb 100644 --- a/runtime/vm/compiler/backend/il_test.cc +++ b/runtime/vm/compiler/backend/il_test.cc @@ -1966,8 +1966,9 @@ ISOLATE_UNIT_TEST_CASE(IL_RecordCoverageSurvivesOptimizations) { { BlockBuilder builder(H.flow_graph(), H.flow_graph()->graph_entry()->normal_entry()); - const auto& coverage_array = Array::Handle(Array::New(1)); - coverage_array.SetAt(0, Smi::Handle(Smi::New(0))); + const auto& coverage_array = + TypedData::Handle(TypedData::New(kTypedDataUint32ArrayCid, 1)); + coverage_array.SetUint32(0, 0); builder.AddInstruction( new RecordCoverageInstr(coverage_array, 0, InstructionSource())); builder.AddReturn(new Value(H.flow_graph()->constant_null())); diff --git a/runtime/vm/compiler/frontend/base_flow_graph_builder.cc b/runtime/vm/compiler/frontend/base_flow_graph_builder.cc index b01c135ab1d..e766c139161 100644 --- a/runtime/vm/compiler/frontend/base_flow_graph_builder.cc +++ b/runtime/vm/compiler/frontend/base_flow_graph_builder.cc @@ -1391,7 +1391,7 @@ intptr_t BaseFlowGraphBuilder::GetCoverageIndexFor(intptr_t encoded_position) { // TODO(jensj): If Length is small enough it's probably better to just do // the linear search. for (intptr_t i = 0; i < coverage_array_.Length(); i += 2) { - intptr_t key = Smi::Value(static_cast(coverage_array_.At(i))); + intptr_t key = coverage_array_.GetUint32(i * kInt32Size); intptr_t value = i + 1; coverage_state_index_for_position_.Insert(key, value); } @@ -1415,23 +1415,24 @@ void BaseFlowGraphBuilder::FinalizeCoverageArray() { } if (coverage_state_index_for_position_.IsEmpty()) { - coverage_array_ = Array::empty_array().ptr(); + coverage_array_ = TypedData::empty_coverage_array().ptr(); return; } - coverage_array_ = - Array::New(coverage_state_index_for_position_.Length() * 2, Heap::kOld); + coverage_array_ = TypedData::New( + kTypedDataUint32ArrayCid, coverage_state_index_for_position_.Length() * 2, + Heap::kOld); - Smi& value = Smi::Handle(); auto it = coverage_state_index_for_position_.GetIterator(); for (auto* p = it.Next(); p != nullptr; p = it.Next()) { - value = Smi::New(p->key); + intptr_t value = p->key; // p->value is the index at which coverage state is stored, the // full coverage entry begins at the previous index. - const intptr_t coverage_entry_index = p->value - 1; - coverage_array_.SetAt(coverage_entry_index, value); - value = Smi::New(0); // no coverage recorded. - coverage_array_.SetAt(p->value, value); + const intptr_t coverage_entry_byte_index = (p->value - 1) * kInt32Size; + coverage_array_.SetUint32(coverage_entry_byte_index, value); + // no coverage recorded. + const intptr_t coverage_state_byte_index = p->value * kInt32Size; + coverage_array_.SetUint32(coverage_state_byte_index, 0); } } diff --git a/runtime/vm/compiler/frontend/base_flow_graph_builder.h b/runtime/vm/compiler/frontend/base_flow_graph_builder.h index 8d23695a8ce..c2681fa9a5e 100644 --- a/runtime/vm/compiler/frontend/base_flow_graph_builder.h +++ b/runtime/vm/compiler/frontend/base_flow_graph_builder.h @@ -174,11 +174,11 @@ class BaseFlowGraphBuilder { has_saved_args_desc_array() ? Array::ZoneHandle(zone_, function_.saved_args_desc()) : Object::null_array()), - coverage_array_( - Array::ZoneHandle(parsed_function->function().GetCoverageArray())) { - } + coverage_array_(TypedData::ZoneHandle( + zone_, + parsed_function->function().GetCoverageArray())) {} - const Array& coverage_array() const { return coverage_array_; } + const TypedData& coverage_array() const { return coverage_array_; } void FinalizeCoverageArray(); @@ -565,7 +565,7 @@ class BaseFlowGraphBuilder { // Mapping from token position to the index in the coverage array at which // coverage state is stored. IntMap coverage_state_index_for_position_; - Array& coverage_array_; + TypedData& coverage_array_; friend class StreamingFlowGraphBuilder; diff --git a/runtime/vm/hash.h b/runtime/vm/hash.h index 745a9bdaccb..ceae2b7180c 100644 --- a/runtime/vm/hash.h +++ b/runtime/vm/hash.h @@ -28,11 +28,15 @@ inline uint32_t FinalizeHash(uint32_t hash, intptr_t hashbits = kBitsPerInt32) { return (hash == 0) ? 1 : hash; } +// The value returned by HashBytes when the length is 0. Used to avoid storing +// canonical hashes to and loading from the heap for empty container Instances. +static constexpr uint32_t kEmptyContainerHash = 1; + inline uint32_t HashBytes(const void* bytes, intptr_t len, intptr_t hashbits = kBitsPerInt32) { if (len == 0) { - return 1; + return kEmptyContainerHash; } uint32_t hash = len; const intptr_t chunks = len / kInt32Size; diff --git a/runtime/vm/interpreter.cc b/runtime/vm/interpreter.cc index 5c812af7eaa..2ff17e415bd 100644 --- a/runtime/vm/interpreter.cc +++ b/runtime/vm/interpreter.cc @@ -3569,21 +3569,23 @@ SwitchDispatchNoSingleStep: : thread->isolate_group()->coverage(); if (coverage_enabled) { - ArrayPtr coverage_array = + TypedDataPtr coverage_array = Function::GetBytecode(FrameFunction(FP))->untag()->coverage_array(); - if (coverage_array == Array::null()) [[unlikely]] { + if (coverage_array == TypedData::null()) [[unlikely]] { SP[1] = Object::null(); // Allocate stack space for result. SP[2] = Function::GetBytecode(FrameFunction(FP)); Exit(thread, FP, SP + 3, pc); INVOKE_RUNTIME(DRT_AllocateBytecodeCoverageArray, NativeArguments(thread, 1, SP + 2, SP + 1)); ASSERT(Bytecode::RawCast(SP[2])->untag()->coverage_array() == - Array::RawCast(SP[1])); + TypedData::RawCast(SP[1])); - coverage_array = Array::RawCast(SP[1]); + coverage_array = TypedData::RawCast(SP[1]); } - ASSERT(coverage_array != Array::null()); + ASSERT(coverage_array != TypedData::null()); + auto* const entries = + reinterpret_cast(coverage_array->untag()->data()); // The index in rE is a logical index into the (position, count) pairs. ASSERT(Smi::Value(coverage_array->untag()->length()) % 2 == 0); @@ -3594,15 +3596,14 @@ SwitchDispatchNoSingleStep: // Double-check that the coverage type in the instruction is a branch // target iff the encoded position is a branch target. bool is_encoded_branch = false; - const intptr_t encoded = Smi::Value( - Smi::RawCast(coverage_array->untag()->element(position_index))); + const intptr_t encoded = entries[position_index]; TokenPosition::DecodeCoveragePosition(encoded, &is_encoded_branch); ASSERT_EQUAL(is_branch, is_encoded_branch); #else USE(position_index); #endif - coverage_array->untag()->set_element(count_index, Smi::New(1)); + entries[count_index] = 1; } #endif // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME) diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc index ef1045b126b..20e6d04c883 100644 --- a/runtime/vm/object.cc +++ b/runtime/vm/object.cc @@ -1209,6 +1209,8 @@ void Object::Init(IsolateGroup* isolate_group) { TypedData::New(kTypedDataUint32ArrayCid, LinkedHashBase::kUninitializedIndexSize, Heap::kOld)); Roots::uninitialized_data().initRO(Array::New(0, Heap::kOld)); + Roots::empty_coverage_array().initRO( + TypedData::New(kTypedDataUint32ArrayCid, 0, Heap::kOld)); // Some thread fields need to be reinitialized as null constants have not been // initialized until now. @@ -1317,6 +1319,8 @@ void Object::Init(IsolateGroup* isolate_group) { ASSERT(Roots::uninitialized_index().IsTypedData()); ASSERT(!Roots::uninitialized_data().IsSmi()); ASSERT(Roots::uninitialized_data().IsArray()); + ASSERT(!Roots::empty_coverage_array().IsSmi()); + ASSERT(Roots::empty_coverage_array().IsTypedData()); } void Object::FinishInit(IsolateGroup* isolate_group) { @@ -11467,7 +11471,7 @@ int32_t Function::SourceFingerprint() const { void Function::SaveICDataMap( const ZoneGrowableArray& deopt_id_to_ic_data, const Array& edge_counters_array, - const Array& coverage_array) const { + const TypedData& coverage_array) const { #if !defined(DART_PRECOMPILED_RUNTIME) // Already installed nothing to do. if (ic_data_array() != Array::null()) { @@ -11546,22 +11550,22 @@ void Function::RestoreICDataMap( #endif // DART_PRECOMPILED_RUNTIME } -ArrayPtr Function::GetCoverageArray() const { +TypedDataPtr Function::GetCoverageArray() const { #if defined(DART_DYNAMIC_MODULES) if (HasBytecode()) { #if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME) const auto& bytecode = Bytecode::Handle(GetBytecode()); return bytecode.coverage_array(); #else - return Array::null(); + return TypedData::null(); #endif } #endif const Array& arr = Array::Handle(ic_data_array()); if (arr.IsNull()) { - return Array::null(); + return TypedData::null(); } - return Array::RawCast(arr.At(ICDataArrayIndices::kCoverageData)); + return TypedData::RawCast(arr.At(ICDataArrayIndices::kCoverageData)); } void Function::set_ic_data_array(const Array& value) const { @@ -19090,26 +19094,24 @@ LocalVarDescriptorsPtr Bytecode::GetLocalVarDescriptors() const { #endif } -ArrayPtr Bytecode::EnsureCoverageArray(Thread* thread) const { +TypedDataPtr Bytecode::EnsureCoverageArray(Thread* thread) const { #if defined(DART_DYNAMIC_MODULES) // Should only be called for bytecode with RecordCoverage instructions. ASSERT(HasRecordedCoverage()); - if (coverage_array() == Array::null()) { + if (coverage_array() == TypedData::null()) { Zone* const zone = thread->zone(); bytecode::BytecodeRecordedCoverageIterator it(zone, *this); - const auto& array = - Array::Handle(zone, Array::New(2 * it.NumEntries(), Heap::kOld)); - auto& smi = Smi::Handle(zone); + const auto& array = TypedData::Handle( + zone, TypedData::New(kTypedDataUint32ArrayCid, 2 * it.NumEntries(), + Heap::kOld)); // The coverage array has two consecutive entries for each logical // index: the encoded coverage position and the hit count. for (intptr_t i = 0; it.MoveNext(); i += 2) { - smi = Smi::New(it.EncodedCoveragePosition()); - array.SetAt(i, smi); - smi = Smi::New(0); - array.SetAt(i + 1, smi); + array.SetUint32(i * kInt32Size, it.EncodedCoveragePosition()); + array.SetUint32((i + 1) * kInt32Size, 0); } SafepointWriteRwLocker ml(thread, thread->isolate_group()->program_lock()); - if (coverage_array() == Array::null()) { + if (coverage_array() == TypedData::null()) { untag()->set_coverage_array(array.ptr()); } } @@ -26184,8 +26186,20 @@ bool TypedData::CanonicalizeEquals(const Instance& other) const { } uint32_t TypedData::CanonicalizeHash() const { - NoSafepointScope no_safepoint; - return HashBytes(DataAddr(0), LengthInBytes(), kHashBits); + uint32_t hash = kEmptyContainerHash; + const intptr_t len = LengthInBytes(); + if (len != 0) { + auto* const thread = Thread::Current(); + hash = thread->heap()->GetCanonicalHash(ptr()); + if (hash == 0) { + { + NoSafepointScope no_safepoint; + hash = HashBytes(DataAddr(0), len, kHashBits); + } + thread->heap()->SetCanonicalHash(ptr(), hash); + } + } + return hash; } TypedDataPtr TypedData::New(intptr_t class_id, diff --git a/runtime/vm/object.h b/runtime/vm/object.h index ff7cdb6b696..66e9a7e402c 100644 --- a/runtime/vm/object.h +++ b/runtime/vm/object.h @@ -573,7 +573,8 @@ class Object { V(Type, void_type) \ V(AbstractType, null_abstract_type) \ V(TypedData, uninitialized_index) \ - V(Array, uninitialized_data) + V(Array, uninitialized_data) \ + V(TypedData, empty_coverage_array) #define DEFINE_SHARED_READONLY_HANDLE_GETTER(Type, name) \ static const Type& name() { return Roots::name(); } @@ -4065,7 +4066,7 @@ class Function : public Object { void SaveICDataMap( const ZoneGrowableArray& deopt_id_to_ic_data, const Array& edge_counters_array, - const Array& coverage_array) const; + const TypedData& coverage_array) const; // Uses 'ic_data_array' to populate the table 'deopt_id_to_ic_data'. Clone // ic_data (array and descriptor) if 'clone_ic_data' is true. void RestoreICDataMap(ZoneGrowableArray* deopt_id_to_ic_data, @@ -4087,7 +4088,7 @@ class Function : public Object { // Coverage data array is a list of pairs: // element 2 * i + 0 is token position // element 2 * i + 1 is coverage hit (zero meaning code was not hit) - ArrayPtr GetCoverageArray() const; + TypedDataPtr GetCoverageArray() const; // Outputs this function's service ID to the provided JSON object. void AddFunctionServiceId(const JSONObject& obj) const; @@ -7627,8 +7628,8 @@ class Bytecode : public Object { StoreNonPointer(&untag()->recorded_coverage_binary_offset_, value); } - ArrayPtr coverage_array() const { return untag()->coverage_array(); } - ArrayPtr EnsureCoverageArray(Thread* thread) const; + TypedDataPtr coverage_array() const { return untag()->coverage_array(); } + TypedDataPtr EnsureCoverageArray(Thread* thread) const; #endif // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME) bool HasLocalVariablesInfo() const { diff --git a/runtime/vm/raw_object.h b/runtime/vm/raw_object.h index 11d1e8d757e..1bcad00381f 100644 --- a/runtime/vm/raw_object.h +++ b/runtime/vm/raw_object.h @@ -2119,7 +2119,7 @@ class UntaggedBytecode : public UntaggedObject { COMPRESSED_POINTER_FIELD(ExceptionHandlersPtr, exception_handlers); COMPRESSED_POINTER_FIELD(PcDescriptorsPtr, pc_descriptors); #if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME) - COMPRESSED_POINTER_FIELD(ArrayPtr, coverage_array); + COMPRESSED_POINTER_FIELD(TypedDataPtr, coverage_array); COMPRESSED_POINTER_FIELD(LocalVarDescriptorsPtr, var_descriptors); VISIT_TO(var_descriptors); #else @@ -3401,8 +3401,9 @@ class UntaggedTypedData : public UntaggedTypedDataBase { } friend class Api; - friend class Instance; friend class DeltaEncodedTypedDataDeserializationCluster; + friend class Instance; + friend class Interpreter; friend class NativeEntryData; friend class Object; friend class ObjectPool; diff --git a/runtime/vm/roots.h b/runtime/vm/roots.h index 5f5002f8df6..bede5f8dba9 100644 --- a/runtime/vm/roots.h +++ b/runtime/vm/roots.h @@ -82,7 +82,8 @@ namespace dart { V(Type, void_type) \ V(AbstractType, null_abstract_type) \ V(TypedData, uninitialized_index) \ - V(Array, uninitialized_data) + V(Array, uninitialized_data) \ + V(TypedData, empty_coverage_array) #define API_HANDLE_ROOTS_LIST(V) \ V(true_api_handle) \ diff --git a/runtime/vm/runtime_entry.cc b/runtime/vm/runtime_entry.cc index 426992ccb0f..6fd1c1f16b8 100644 --- a/runtime/vm/runtime_entry.cc +++ b/runtime/vm/runtime_entry.cc @@ -4943,7 +4943,7 @@ DEFINE_RUNTIME_ENTRY(AllocateBytecodeCoverageArray, 1) { !defined(DART_PRECOMPILED_RUNTIME) const auto& bytecode = Bytecode::CheckedHandle(zone, arguments.ArgAt(0)); const auto& coverage_array = - Array::Handle(zone, bytecode.EnsureCoverageArray(thread)); + TypedData::Handle(zone, bytecode.EnsureCoverageArray(thread)); arguments.SetReturn(coverage_array); #else UNREACHABLE(); diff --git a/runtime/vm/source_report.cc b/runtime/vm/source_report.cc index 96b87bf45b0..8dcad28e23d 100644 --- a/runtime/vm/source_report.cc +++ b/runtime/vm/source_report.cc @@ -393,15 +393,15 @@ void SourceReport::PrintCoverageData(JSONObject* jsobj, }; // Merge the coverage from coverage_array attached to the function. - const Array& coverage_array = Array::Handle(function.GetCoverageArray()); + const auto& coverage_array = TypedData::Handle(function.GetCoverageArray()); if (!coverage_array.IsNull()) { for (intptr_t i = 0; i < coverage_array.Length(); i += 2) { bool is_branch_coverage; const TokenPosition token_pos = TokenPosition::DecodeCoveragePosition( - Smi::Value(Smi::RawCast(coverage_array.At(i))), &is_branch_coverage); + coverage_array.GetUint32(i * kInt32Size), &is_branch_coverage); if (is_branch_coverage == report_branch_coverage) { const bool was_executed = - Smi::Value(Smi::RawCast(coverage_array.At(i + 1))) != 0; + coverage_array.GetUint32((i + 1) * kInt32Size) != 0; update_coverage(token_pos, was_executed || const_constructor_hit); } } diff --git a/runtime/vm/token_position.cc b/runtime/vm/token_position.cc index 5dd2ca98acf..4cbe9ea1871 100644 --- a/runtime/vm/token_position.cc +++ b/runtime/vm/token_position.cc @@ -24,8 +24,11 @@ int32_t TokenPosition::Serialize() const { intptr_t TokenPosition::EncodeCoveragePosition(bool is_branch_coverage) { // Normal coverage positions are encoded as 2 * pos, and branch coverage are // encoded as 2 * pos + 1. - intptr_t encoded_position = 2 * static_cast(value_); - return is_branch_coverage ? encoded_position + 1 : encoded_position; + intptr_t encoded_position = + 2 * static_cast(value_) + (is_branch_coverage ? 1 : 0); + // Coverage arrays are Uint32Arrays, so ensure the result fits. + ASSERT(Utils::IsUint(32, encoded_position)); + return encoded_position; } TokenPosition TokenPosition::DecodeCoveragePosition(intptr_t encoded_position,