diff --git a/runtime/vm/compiler/frontend/base_flow_graph_builder.cc b/runtime/vm/compiler/frontend/base_flow_graph_builder.cc index b37c682aa21..1124ea15f9b 100644 --- a/runtime/vm/compiler/frontend/base_flow_graph_builder.cc +++ b/runtime/vm/compiler/frontend/base_flow_graph_builder.cc @@ -1244,33 +1244,44 @@ Fragment BaseFlowGraphBuilder::MathUnary(MathUnaryInstr::MathUnaryKind kind) { } Fragment BaseFlowGraphBuilder::RecordCoverage(TokenPosition position) { + return RecordCoverageImpl(position, false /** is_branch_coverage **/); +} + +Fragment BaseFlowGraphBuilder::RecordBranchCoverage(TokenPosition position) { + return RecordCoverageImpl(position, true /** is_branch_coverage **/); +} + +Fragment BaseFlowGraphBuilder::RecordCoverageImpl(TokenPosition position, + bool is_branch_coverage) { Fragment instructions; - if (SupportsCoverage()) { - const intptr_t coverage_index = GetCoverageIndexFor(position); - instructions <<= new (Z) RecordCoverageInstr( - coverage_array(), coverage_index, InstructionSource(position)); - } + if (!SupportsCoverage()) return instructions; + if (!position.IsReal()) return instructions; + if (is_branch_coverage && !IG->branch_coverage()) return instructions; + + const intptr_t coverage_index = + GetCoverageIndexFor(position.EncodeCoveragePosition(is_branch_coverage)); + instructions <<= new (Z) RecordCoverageInstr(coverage_array(), coverage_index, + InstructionSource(position)); return instructions; } -intptr_t BaseFlowGraphBuilder::GetCoverageIndexFor(TokenPosition token_pos) { +intptr_t BaseFlowGraphBuilder::GetCoverageIndexFor(intptr_t encoded_position) { if (coverage_array_.IsNull()) { - // We have not yet created coverage_array, this is the first time - // we are building the graph for this function. Collect coverage - // positions. + // We have not yet created coverage_array, this is the first time we are + // building the graph for this function. Collect coverage positions. for (intptr_t i = 0; i < coverage_array_positions_.length(); i++) { - if (coverage_array_positions_.At(i) == token_pos) { + if (coverage_array_positions_.At(i) == encoded_position) { return 2 * i + 1; } } const auto index = 2 * coverage_array_positions_.length() + 1; - coverage_array_positions_.Add(token_pos); + coverage_array_positions_.Add(encoded_position); return index; } for (intptr_t i = 0; i < coverage_array_.Length(); i += 2) { - if (TokenPosition::Deserialize(Smi::Value( - static_cast(coverage_array_.At(i)))) == token_pos) { + if (Smi::Value(static_cast(coverage_array_.At(i))) == + encoded_position) { return i + 1; } } @@ -1294,7 +1305,7 @@ void BaseFlowGraphBuilder::FinalizeCoverageArray() { Smi& value = Smi::Handle(); for (intptr_t i = 0; i < coverage_array_positions_.length(); i++) { - value = Smi::New(coverage_array_positions_[i].Serialize()); + value = Smi::New(coverage_array_positions_[i]); coverage_array_.SetAt(2 * i, value); value = Smi::New(0); // no coverage recorded. coverage_array_.SetAt(2 * i + 1, value); diff --git a/runtime/vm/compiler/frontend/base_flow_graph_builder.h b/runtime/vm/compiler/frontend/base_flow_graph_builder.h index b0597b8c312..5aa008b3838 100644 --- a/runtime/vm/compiler/frontend/base_flow_graph_builder.h +++ b/runtime/vm/compiler/frontend/base_flow_graph_builder.h @@ -166,7 +166,6 @@ class BaseFlowGraphBuilder { const Array& coverage_array() const { return coverage_array_; } - intptr_t GetCoverageIndexFor(TokenPosition token_pos); void FinalizeCoverageArray(); Fragment LoadField(const Field& field, bool calls_initializer); @@ -473,6 +472,7 @@ class BaseFlowGraphBuilder { // Records coverage for this position, if the current VM mode supports it. Fragment RecordCoverage(TokenPosition position); + Fragment RecordBranchCoverage(TokenPosition position); // Returns whether this function has a saved arguments descriptor array. bool has_saved_args_desc_array() { @@ -487,6 +487,8 @@ class BaseFlowGraphBuilder { protected: intptr_t AllocateBlockId() { return ++last_used_block_id_; } + Fragment RecordCoverageImpl(TokenPosition position, bool is_branch_coverage); + intptr_t GetCoverageIndexFor(intptr_t encoded_position); const ParsedFunction* parsed_function_; const Function& function_; @@ -507,7 +509,7 @@ class BaseFlowGraphBuilder { const bool inlining_unchecked_entry_; const Array& saved_args_desc_array_; - GrowableArray coverage_array_positions_; + GrowableArray coverage_array_positions_; Array& coverage_array_; friend class StreamingFlowGraphBuilder; diff --git a/runtime/vm/source_report.cc b/runtime/vm/source_report.cc index 12f6096d9af..b826aa903c0 100644 --- a/runtime/vm/source_report.cc +++ b/runtime/vm/source_report.cc @@ -346,11 +346,14 @@ void SourceReport::PrintCoverageData(JSONObject* jsobj, const Array& coverage_array = Array::Handle(function.GetCoverageArray()); if (!coverage_array.IsNull()) { for (intptr_t i = 0; i < coverage_array.Length(); i += 2) { - const TokenPosition token_pos = TokenPosition::Deserialize( - Smi::Value(Smi::RawCast(coverage_array.At(i)))); - const bool was_executed = - Smi::Value(Smi::RawCast(coverage_array.At(i + 1))) != 0; - update_coverage(token_pos, was_executed); + bool is_branch_coverage; + const TokenPosition token_pos = TokenPosition::DecodeCoveragePosition( + Smi::Value(Smi::RawCast(coverage_array.At(i))), &is_branch_coverage); + if (!is_branch_coverage) { + const bool was_executed = + Smi::Value(Smi::RawCast(coverage_array.At(i + 1))) != 0; + update_coverage(token_pos, was_executed); + } } } diff --git a/runtime/vm/token_position.cc b/runtime/vm/token_position.cc index 14f7058dfd0..88b27c1db05 100644 --- a/runtime/vm/token_position.cc +++ b/runtime/vm/token_position.cc @@ -21,6 +21,19 @@ int32_t TokenPosition::Serialize() const { return static_cast(value_); } +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; +} + +TokenPosition TokenPosition::DecodeCoveragePosition(intptr_t encoded_position, + bool* is_branch_coverage) { + *is_branch_coverage = ((encoded_position % 2) == 1); + return TokenPosition(encoded_position / 2); +} + #define DEFINE_VALUES(name, value) \ const TokenPosition TokenPosition::k##name(value); SENTINEL_TOKEN_DESCRIPTORS(DEFINE_VALUES); diff --git a/runtime/vm/token_position.h b/runtime/vm/token_position.h index 8dcf100257b..677872670e2 100644 --- a/runtime/vm/token_position.h +++ b/runtime/vm/token_position.h @@ -202,6 +202,13 @@ class TokenPosition { return TokenPosition((kLastPos - 1) - value); } + // Encode the token position for storage in the coverage array. + intptr_t EncodeCoveragePosition(bool is_branch_coverage); + + // Decode a token position that was stored in the coverage array. + static TokenPosition DecodeCoveragePosition(intptr_t encoded_position, + bool* is_branch_coverage); + const char* ToCString() const; private: