[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 <sstrickl@google.com> Reviewed-by: Ryan Macnak <rmacnak@google.com>
This commit is contained in:
committed by
dart-scoped@luci-project-accounts.iam.gserviceaccount.com
parent
da40aec0bb
commit
7cd8fda37e
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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_)
|
||||
|
||||
|
||||
@@ -679,7 +679,7 @@ void FlowGraphSerializer::WriteFlowGraph(
|
||||
|
||||
Write<intptr_t>(flow_graph.current_ssa_temp_index());
|
||||
Write<intptr_t>(flow_graph.max_block_id());
|
||||
Write<const Array&>(flow_graph.coverage_array());
|
||||
Write<const TypedData&>(flow_graph.coverage_array());
|
||||
|
||||
PrologueInfo prologue_info = flow_graph.prologue_info();
|
||||
Write<intptr_t>(prologue_info.min_block_id);
|
||||
@@ -734,7 +734,7 @@ void FlowGraphSerializer::WriteFlowGraph(
|
||||
FlowGraph* FlowGraphDeserializer::ReadFlowGraph() {
|
||||
const intptr_t current_ssa_temp_index = Read<intptr_t>();
|
||||
const intptr_t max_block_id = Read<intptr_t>();
|
||||
const Array& coverage_array = Read<const Array&>();
|
||||
const TypedData& coverage_array = Read<const TypedData&>();
|
||||
const PrologueInfo prologue_info(Read<intptr_t>(), Read<intptr_t>());
|
||||
|
||||
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<intptr_t>(len);
|
||||
for (intptr_t i = 0; i < len; ++i) {
|
||||
uint32_t elem = array.GetUint32(i * kInt32Size);
|
||||
Write<uint32_t>(elem);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case kBoolCid:
|
||||
Write<bool>(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<intptr_t>();
|
||||
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<uint32_t>());
|
||||
}
|
||||
return array;
|
||||
}
|
||||
case kBoolCid:
|
||||
return Bool::Get(Read<bool>());
|
||||
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 <> \
|
||||
|
||||
@@ -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) \
|
||||
|
||||
@@ -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()));
|
||||
|
||||
@@ -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<SmiPtr>(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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<intptr_t> coverage_state_index_for_position_;
|
||||
Array& coverage_array_;
|
||||
TypedData& coverage_array_;
|
||||
|
||||
friend class StreamingFlowGraphBuilder;
|
||||
|
||||
|
||||
+5
-1
@@ -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;
|
||||
|
||||
@@ -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<uint32_t*>(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)
|
||||
|
||||
|
||||
+31
-17
@@ -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<const ICData*>& 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,
|
||||
|
||||
+6
-5
@@ -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<const ICData*>& 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<const ICData*>* 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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
+2
-1
@@ -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) \
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<intptr_t>(value_);
|
||||
return is_branch_coverage ? encoded_position + 1 : encoded_position;
|
||||
intptr_t encoded_position =
|
||||
2 * static_cast<intptr_t>(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,
|
||||
|
||||
Reference in New Issue
Block a user