b3d004aaf9
... /interpreter. This CL moves cctest/interpreter/{bytecode-expectations-printer, test-bytecode-generator, test-interpreter-intrinsics, interpreter-tester, test-interpreter, test-source-positions, source-position-matcher} to unittests/interpreter/{ bytecode-expectations-printer, bytecode-generator-unittest, interpreter-intrinsics-unittest, interpreter-tester, interpreter-unittest, source-positions-unittest, source-position-matcher}. Bug: v8:12781 Change-Id: I187583bd34f709dd0d7dfc0f92e18f191da0e30f Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3609752 Reviewed-by: Leszek Swirski <leszeks@chromium.org> Commit-Queue: 王澳 <wangao.james@bytedance.com> Reviewed-by: Toon Verwaest <verwaest@chromium.org> Cr-Commit-Position: refs/heads/main@{#81057}
224 lines
7.6 KiB
C++
224 lines
7.6 KiB
C++
// Copyright 2016 the V8 project authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include "test/unittests/interpreter/source-position-matcher.h"
|
|
|
|
#include "src/objects/objects-inl.h"
|
|
#include "src/objects/objects.h"
|
|
|
|
namespace v8 {
|
|
namespace internal {
|
|
namespace interpreter {
|
|
|
|
// Comparer for PositionTableEntry instances.
|
|
struct PositionTableEntryComparer {
|
|
bool operator()(const PositionTableEntry& lhs,
|
|
const PositionTableEntry& rhs) const {
|
|
int lhs_type_score = type_score(lhs);
|
|
int rhs_type_score = type_score(rhs);
|
|
if (lhs_type_score == rhs_type_score) {
|
|
return lhs.source_position < rhs.source_position;
|
|
} else {
|
|
return lhs_type_score < rhs_type_score;
|
|
}
|
|
}
|
|
|
|
int type_score(const PositionTableEntry& entry) const {
|
|
return entry.is_statement ? 1 : 0;
|
|
}
|
|
};
|
|
|
|
//
|
|
// The principles for comparing source positions in bytecode arrays
|
|
// are:
|
|
//
|
|
// 1. The number of statement positions must be the same in both.
|
|
//
|
|
// 2. Statement positions may be moved provide they do not affect the
|
|
// debuggers causal view of the v8 heap and local state. This means
|
|
// statement positions may be moved when their initial position is
|
|
// on bytecodes that manipulate the accumulator and temporary
|
|
// registers.
|
|
//
|
|
// 3. When duplicate expression positions are present, either may
|
|
// be dropped.
|
|
//
|
|
// 4. Expression positions may be applied to later bytecodes in the
|
|
// bytecode array if the current bytecode does not throw.
|
|
//
|
|
// 5. Expression positions may be dropped when they are applied to
|
|
// bytecodes that manipulate local frame state and immediately
|
|
// proceeded by another source position.
|
|
//
|
|
// 6. The relative ordering of source positions must be preserved.
|
|
//
|
|
bool SourcePositionMatcher::Match(Handle<BytecodeArray> original_bytecode,
|
|
Handle<BytecodeArray> optimized_bytecode) {
|
|
SourcePositionTableIterator original(
|
|
original_bytecode->SourcePositionTable());
|
|
SourcePositionTableIterator optimized(
|
|
optimized_bytecode->SourcePositionTable());
|
|
|
|
int last_original_bytecode_offset = 0;
|
|
int last_optimized_bytecode_offset = 0;
|
|
|
|
// Ordered lists of expression positions immediately before the
|
|
// latest statements in each bytecode array.
|
|
std::vector<PositionTableEntry> original_expression_entries;
|
|
std::vector<PositionTableEntry> optimized_expression_entries;
|
|
|
|
while (true) {
|
|
MoveToNextStatement(&original, &original_expression_entries);
|
|
MoveToNextStatement(&optimized, &optimized_expression_entries);
|
|
|
|
if (original.done() && optimized.done()) {
|
|
return true;
|
|
} else if (original.done()) {
|
|
return false;
|
|
} else if (optimized.done()) {
|
|
return false;
|
|
}
|
|
|
|
if (HasNewExpressionPositionsInOptimized(&original_expression_entries,
|
|
&optimized_expression_entries)) {
|
|
return false;
|
|
}
|
|
|
|
StripUnneededExpressionPositions(original_bytecode,
|
|
&original_expression_entries,
|
|
original.code_offset());
|
|
StripUnneededExpressionPositions(optimized_bytecode,
|
|
&optimized_expression_entries,
|
|
optimized.code_offset());
|
|
|
|
if (!CompareExpressionPositions(&original_expression_entries,
|
|
&optimized_expression_entries)) {
|
|
// Message logged in CompareExpressionPositions().
|
|
return false;
|
|
}
|
|
|
|
// Check original and optimized have matching source positions.
|
|
if (original.source_position() != optimized.source_position()) {
|
|
return false;
|
|
}
|
|
|
|
if (original.code_offset() < last_original_bytecode_offset) {
|
|
return false;
|
|
}
|
|
last_original_bytecode_offset = original.code_offset();
|
|
|
|
if (optimized.code_offset() < last_optimized_bytecode_offset) {
|
|
return false;
|
|
}
|
|
last_optimized_bytecode_offset = optimized.code_offset();
|
|
|
|
// TODO(oth): Can we compare statement positions are semantically
|
|
// equivalent? e.g. before a bytecode that has debugger observable
|
|
// effects. This is likely non-trivial.
|
|
}
|
|
}
|
|
|
|
bool SourcePositionMatcher::HasNewExpressionPositionsInOptimized(
|
|
const std::vector<PositionTableEntry>* const original_positions,
|
|
const std::vector<PositionTableEntry>* const optimized_positions) {
|
|
std::set<PositionTableEntry, PositionTableEntryComparer> original_set(
|
|
original_positions->begin(), original_positions->end());
|
|
|
|
bool retval = false;
|
|
for (auto optimized_position : *optimized_positions) {
|
|
if (original_set.find(optimized_position) == original_set.end()) {
|
|
retval = true;
|
|
}
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
bool SourcePositionMatcher::CompareExpressionPositions(
|
|
const std::vector<PositionTableEntry>* const original_positions,
|
|
const std::vector<PositionTableEntry>* const optimized_positions) {
|
|
if (original_positions->size() != optimized_positions->size()) {
|
|
return false;
|
|
}
|
|
|
|
if (original_positions->size() == 0) {
|
|
return true;
|
|
}
|
|
|
|
for (size_t i = 0; i < original_positions->size(); ++i) {
|
|
PositionTableEntry original = original_positions->at(i);
|
|
PositionTableEntry optimized = original_positions->at(i);
|
|
CHECK_GT(original.source_position, 0);
|
|
if ((original.is_statement || optimized.is_statement) ||
|
|
(original.source_position != optimized.source_position) ||
|
|
(original.source_position < 0)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void SourcePositionMatcher::StripUnneededExpressionPositions(
|
|
Handle<BytecodeArray> bytecode_array,
|
|
std::vector<PositionTableEntry>* expression_positions,
|
|
int next_statement_bytecode_offset) {
|
|
size_t j = 0;
|
|
for (size_t i = 0; i < expression_positions->size(); ++i) {
|
|
CHECK(expression_positions->at(i).source_position > 0 &&
|
|
!expression_positions->at(i).is_statement);
|
|
int bytecode_end = (i == expression_positions->size() - 1)
|
|
? next_statement_bytecode_offset
|
|
: expression_positions->at(i + 1).code_offset;
|
|
if (ExpressionPositionIsNeeded(bytecode_array,
|
|
expression_positions->at(i).code_offset,
|
|
bytecode_end)) {
|
|
expression_positions->at(j++) = expression_positions->at(i);
|
|
}
|
|
}
|
|
expression_positions->resize(j);
|
|
}
|
|
|
|
void SourcePositionMatcher::AdvanceBytecodeIterator(
|
|
BytecodeArrayIterator* iterator, int bytecode_offset) {
|
|
while (iterator->current_offset() != bytecode_offset) {
|
|
iterator->Advance();
|
|
}
|
|
}
|
|
|
|
bool SourcePositionMatcher::ExpressionPositionIsNeeded(
|
|
Handle<BytecodeArray> bytecode_array, int start_offset, int end_offset) {
|
|
CHECK_GT(end_offset, start_offset);
|
|
BytecodeArrayIterator iterator(bytecode_array);
|
|
AdvanceBytecodeIterator(&iterator, start_offset);
|
|
|
|
while (iterator.current_offset() != end_offset) {
|
|
if (Bytecodes::IsWithoutExternalSideEffects(iterator.current_bytecode())) {
|
|
iterator.Advance();
|
|
} else {
|
|
// Bytecode could throw so need an expression position.
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void SourcePositionMatcher::MoveToNextStatement(
|
|
SourcePositionTableIterator* iterator,
|
|
std::vector<PositionTableEntry>* positions) {
|
|
iterator->Advance();
|
|
positions->clear();
|
|
while (!iterator->done()) {
|
|
if (iterator->is_statement()) {
|
|
break;
|
|
}
|
|
positions->push_back({iterator->code_offset(),
|
|
iterator->source_position().raw(),
|
|
iterator->is_statement()});
|
|
iterator->Advance();
|
|
}
|
|
}
|
|
|
|
} // namespace interpreter
|
|
} // namespace internal
|
|
} // namespace v8
|