// 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/cctest/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 original_bytecode, Handle 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 original_expression_entries; std::vector 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. } return true; } bool SourcePositionMatcher::HasNewExpressionPositionsInOptimized( const std::vector* const original_positions, const std::vector* const optimized_positions) { std::set 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* const original_positions, const std::vector* 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 bytecode_array, std::vector* 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 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* 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