Index: src/interpreter/mkpeephole.cc |
diff --git a/src/interpreter/mkpeephole.cc b/src/interpreter/mkpeephole.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..d89390d45d125f9ca2d6dfaf619ce911794bdcb6 |
--- /dev/null |
+++ b/src/interpreter/mkpeephole.cc |
@@ -0,0 +1,387 @@ |
+// 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 <array> |
+#include <fstream> |
+#include <map> |
+#include <string> |
+#include <vector> |
+ |
+#include "src/globals.h" |
+#include "src/interpreter/bytecode-peephole-table.h" |
+#include "src/interpreter/bytecodes.h" |
+ |
+namespace v8 { |
+namespace internal { |
+ |
+namespace interpreter { |
+ |
+const char* ActionName(PeepholeAction action) { |
+ switch (action) { |
+#define CASE(Name) \ |
+ case PeepholeAction::k##Name: \ |
+ return "PeepholeAction::k" #Name; |
+ PEEPHOLE_ACTION_LIST(CASE) |
+#undef CASE |
+ default: |
+ UNREACHABLE(); |
+ return ""; |
+ } |
+} |
+ |
+std::string BytecodeName(Bytecode bytecode) { |
+ return "Bytecode::k" + std::string(Bytecodes::ToString(bytecode)); |
+} |
+ |
+class PeepholeActionTableWriter final { |
+ public: |
+ static const size_t kNumberOfBytecodes = |
+ static_cast<size_t>(Bytecode::kLast) + 1; |
+ typedef std::array<PeepholeActionAndData, kNumberOfBytecodes> Row; |
+ |
+ void BuildTable(); |
+ void Write(std::ostream& os); |
+ |
+ private: |
+ static const char* kIndent; |
+ static const char* kNamespaceElements[]; |
+ |
+ void WriteHeader(std::ostream& os); |
+ void WriteIncludeFiles(std::ostream& os); |
+ void WriteClassMethods(std::ostream& os); |
+ void WriteUniqueRows(std::ostream& os); |
+ void WriteRowMap(std::ostream& os); |
+ void WriteRow(std::ostream& os, size_t row_index); |
+ void WriteOpenNamespace(std::ostream& os); |
+ void WriteCloseNamespace(std::ostream& os); |
+ |
+ PeepholeActionAndData LookupActionAndData(Bytecode last, Bytecode current); |
+ void BuildRow(Bytecode last, Row* row); |
+ size_t HashRow(const Row* row); |
+ void InsertRow(size_t row_index, const Row* const row, size_t row_hash, |
+ std::map<size_t, size_t>* hash_to_row_map); |
+ bool RowsEqual(const Row* const first, const Row* const second); |
+ |
+ std::vector<Row>* table() { return &table_; } |
+ |
+ // Table of unique rows. |
+ std::vector<Row> table_; |
+ |
+ // Mapping of row index to unique row index. |
+ std::array<size_t, kNumberOfBytecodes> row_map_; |
+}; |
+ |
+const char* PeepholeActionTableWriter::kIndent = " "; |
+const char* PeepholeActionTableWriter::kNamespaceElements[] = {"v8", "internal", |
+ "interpreter"}; |
+ |
+// static |
+PeepholeActionAndData PeepholeActionTableWriter::LookupActionAndData( |
+ Bytecode last, Bytecode current) { |
+ // Optimize various accumulator loads followed by store accumulator |
+ // to an equivalent register load and loading the accumulator with |
+ // the register. The latter accumulator load can often be elided as |
+ // it is side-effect free and often followed by another accumulator |
+ // load so can be elided. |
+ if (current == Bytecode::kStar) { |
+ switch (last) { |
+ case Bytecode::kLdaNamedProperty: |
+ return {PeepholeAction::kTransformLdaStarToLdrLdarAction, |
+ Bytecode::kLdrNamedProperty}; |
+ case Bytecode::kLdaKeyedProperty: |
+ return {PeepholeAction::kTransformLdaStarToLdrLdarAction, |
+ Bytecode::kLdrKeyedProperty}; |
+ case Bytecode::kLdaGlobal: |
+ return {PeepholeAction::kTransformLdaStarToLdrLdarAction, |
+ Bytecode::kLdrGlobal}; |
+ case Bytecode::kLdaContextSlot: |
+ return {PeepholeAction::kTransformLdaStarToLdrLdarAction, |
+ Bytecode::kLdrContextSlot}; |
+ case Bytecode::kLdaUndefined: |
+ return {PeepholeAction::kTransformLdaStarToLdrLdarAction, |
+ Bytecode::kLdrUndefined}; |
+ default: |
+ break; |
+ } |
+ } |
+ |
+ // ToName optimizations: remove unnecessary ToName bytecodes. |
+ if (current == Bytecode::kToName) { |
+ if (last == Bytecode::kLdaConstant) { |
+ return {PeepholeAction::kElideCurrentIfLoadingNameConstantAction, |
+ Bytecode::kIllegal}; |
+ } else if (Bytecodes::PutsNameInAccumulator(last)) { |
+ return {PeepholeAction::kElideCurrentAction, Bytecode::kIllegal}; |
+ } |
+ } |
+ |
+ // Nop are placeholders for holding source position information and can be |
+ // elided if there is no source information. |
+ if (last == Bytecode::kNop) { |
+ if (Bytecodes::IsJump(current)) { |
+ return {PeepholeAction::kElideLastBeforeJumpAction, Bytecode::kIllegal}; |
+ } else { |
+ return {PeepholeAction::kElideLastAction, Bytecode::kIllegal}; |
+ } |
+ } |
+ |
+ // The accumulator is invisible to the debugger. If there is a sequence |
+ // of consecutive accumulator loads (that don't have side effects) then |
+ // only the final load is potentially visible. |
+ if (Bytecodes::IsAccumulatorLoadWithoutEffects(last) && |
+ Bytecodes::IsAccumulatorLoadWithoutEffects(current)) { |
+ return {PeepholeAction::kElideLastAction, Bytecode::kIllegal}; |
+ } |
+ |
+ // The current instruction clobbers the accumulator without reading |
+ // it. The load in the last instruction can be elided as it has no |
+ // effect. |
+ if (Bytecodes::IsAccumulatorLoadWithoutEffects(last) && |
+ Bytecodes::GetAccumulatorUse(current) == AccumulatorUse::kWrite) { |
+ return {PeepholeAction::kElideLastAction, Bytecode::kIllegal}; |
+ } |
+ |
+ // Ldar and Star make the accumulator and register hold equivalent |
+ // values. Only the first bytecode is needed if there's a sequence |
+ // of back-to-back Ldar and Star bytecodes with the same operand. |
+ if (Bytecodes::IsLdarOrStar(last) && Bytecodes::IsLdarOrStar(current)) { |
+ return {PeepholeAction::kElideCurrentIfOperand0MatchesAction, |
+ Bytecode::kIllegal}; |
+ } |
+ |
+ // Remove ToBoolean coercion from conditional jumps where possible. |
+ if (Bytecodes::WritesBooleanToAccumulator(last)) { |
+ if (Bytecodes::IsJumpIfToBoolean(current)) { |
+ return {PeepholeAction::kChangeJumpBytecodeAction, |
+ Bytecodes::GetJumpWithoutToBoolean(current)}; |
+ } else if (current == Bytecode::kToBooleanLogicalNot) { |
+ return {PeepholeAction::kChangeBytecodeAction, Bytecode::kLogicalNot}; |
+ } |
+ } |
+ |
+ // Fuse LdaSmi followed by binary op to produce binary op with a |
+ // immediate integer argument. This savaes on dispatches and size. |
+ if (last == Bytecode::kLdaSmi) { |
+ switch (current) { |
+ case Bytecode::kAdd: |
+ return {PeepholeAction::kTransformLdaSmiBinaryOpToBinaryOpWithSmiAction, |
+ Bytecode::kAddSmi}; |
+ case Bytecode::kSub: |
+ return {PeepholeAction::kTransformLdaSmiBinaryOpToBinaryOpWithSmiAction, |
+ Bytecode::kSubSmi}; |
+ case Bytecode::kBitwiseAnd: |
+ return {PeepholeAction::kTransformLdaSmiBinaryOpToBinaryOpWithSmiAction, |
+ Bytecode::kBitwiseAndSmi}; |
+ case Bytecode::kBitwiseOr: |
+ return {PeepholeAction::kTransformLdaSmiBinaryOpToBinaryOpWithSmiAction, |
+ Bytecode::kBitwiseOrSmi}; |
+ case Bytecode::kShiftLeft: |
+ return {PeepholeAction::kTransformLdaSmiBinaryOpToBinaryOpWithSmiAction, |
+ Bytecode::kShiftLeftSmi}; |
+ case Bytecode::kShiftRight: |
+ return {PeepholeAction::kTransformLdaSmiBinaryOpToBinaryOpWithSmiAction, |
+ Bytecode::kShiftRightSmi}; |
+ default: |
+ break; |
+ } |
+ } |
+ |
+ // Fuse LdaZero followed by binary op to produce binary op with a |
+ // zero immediate argument. This saves dispatches, but not size. |
+ if (last == Bytecode::kLdaZero) { |
+ switch (current) { |
+ case Bytecode::kAdd: |
+ return { |
+ PeepholeAction::kTransformLdaZeroBinaryOpToBinaryOpWithZeroAction, |
+ Bytecode::kAddSmi}; |
+ case Bytecode::kSub: |
+ return { |
+ PeepholeAction::kTransformLdaZeroBinaryOpToBinaryOpWithZeroAction, |
+ Bytecode::kSubSmi}; |
+ case Bytecode::kBitwiseAnd: |
+ return { |
+ PeepholeAction::kTransformLdaZeroBinaryOpToBinaryOpWithZeroAction, |
+ Bytecode::kBitwiseAndSmi}; |
+ case Bytecode::kBitwiseOr: |
+ return { |
+ PeepholeAction::kTransformLdaZeroBinaryOpToBinaryOpWithZeroAction, |
+ Bytecode::kBitwiseOrSmi}; |
+ case Bytecode::kShiftLeft: |
+ return { |
+ PeepholeAction::kTransformLdaZeroBinaryOpToBinaryOpWithZeroAction, |
+ Bytecode::kShiftLeftSmi}; |
+ case Bytecode::kShiftRight: |
+ return { |
+ PeepholeAction::kTransformLdaZeroBinaryOpToBinaryOpWithZeroAction, |
+ Bytecode::kShiftRightSmi}; |
+ default: |
+ break; |
+ } |
+ } |
+ |
+ // If there is no last bytecode to optimize against, store the incoming |
+ // bytecode or for jumps emit incoming bytecode immediately. |
+ if (last == Bytecode::kIllegal) { |
+ if (Bytecodes::IsJump(current)) { |
+ return {PeepholeAction::kUpdateLastJumpAction, Bytecode::kIllegal}; |
+ } else if (current == Bytecode::kNop) { |
+ return {PeepholeAction::kUpdateLastIfSourceInfoPresentAction, |
+ Bytecode::kIllegal}; |
+ } else { |
+ return {PeepholeAction::kUpdateLastAction, Bytecode::kIllegal}; |
+ } |
+ } |
+ |
+ // No matches, take the default action. |
+ if (Bytecodes::IsJump(current)) { |
+ return {PeepholeAction::kDefaultJumpAction, Bytecode::kIllegal}; |
+ } else { |
+ return {PeepholeAction::kDefaultAction, Bytecode::kIllegal}; |
+ } |
+} |
+ |
+void PeepholeActionTableWriter::Write(std::ostream& os) { |
+ WriteHeader(os); |
+ WriteIncludeFiles(os); |
+ WriteOpenNamespace(os); |
+ WriteUniqueRows(os); |
+ WriteRowMap(os); |
+ WriteClassMethods(os); |
+ WriteCloseNamespace(os); |
+} |
+ |
+void PeepholeActionTableWriter::WriteHeader(std::ostream& os) { |
+ os << "// Copyright 2016 the V8 project authors. All rights reserved.\n" |
+ << "// Use of this source code is governed by a BSD-style license that\n" |
+ << "// can be found in the LICENSE file.\n\n" |
+ << "// Autogenerated by " __FILE__ ". Do not edit.\n\n"; |
+} |
+ |
+void PeepholeActionTableWriter::WriteIncludeFiles(std::ostream& os) { |
+ os << "#include \"src/interpreter/bytecode-peephole-table.h\"\n\n"; |
+} |
+ |
+void PeepholeActionTableWriter::WriteUniqueRows(std::ostream& os) { |
+ os << "const PeepholeActionAndData PeepholeActionTable::row_data_[" |
+ << table_.size() << "][" << kNumberOfBytecodes << "] = {\n"; |
+ for (size_t i = 0; i < table_.size(); ++i) { |
+ os << "{\n"; |
+ WriteRow(os, i); |
+ os << "},\n"; |
+ } |
+ os << "};\n\n"; |
+} |
+ |
+void PeepholeActionTableWriter::WriteRowMap(std::ostream& os) { |
+ os << "const PeepholeActionAndData* const PeepholeActionTable::row_[" |
+ << kNumberOfBytecodes << "] = {\n"; |
+ for (size_t i = 0; i < kNumberOfBytecodes; ++i) { |
+ os << kIndent << " PeepholeActionTable::row_data_[" << row_map_[i] |
+ << "], \n"; |
+ } |
+ os << "};\n\n"; |
+} |
+ |
+void PeepholeActionTableWriter::WriteRow(std::ostream& os, size_t row_index) { |
+ const Row row = table_.at(row_index); |
+ for (PeepholeActionAndData action_data : row) { |
+ os << kIndent << "{" << ActionName(action_data.action) << "," |
+ << BytecodeName(action_data.bytecode) << "},\n"; |
+ } |
+} |
+ |
+void PeepholeActionTableWriter::WriteOpenNamespace(std::ostream& os) { |
+ for (auto element : kNamespaceElements) { |
+ os << "namespace " << element << " {\n"; |
+ } |
+ os << "\n"; |
+} |
+ |
+void PeepholeActionTableWriter::WriteCloseNamespace(std::ostream& os) { |
+ for (auto element : kNamespaceElements) { |
+ os << "} // namespace " << element << "\n"; |
+ } |
+} |
+ |
+void PeepholeActionTableWriter::WriteClassMethods(std::ostream& os) { |
+ os << "// static\n" |
+ << "const PeepholeActionAndData*\n" |
+ << "PeepholeActionTable::Lookup(Bytecode last, Bytecode current) {\n" |
+ << kIndent |
+ << "return &row_[Bytecodes::ToByte(last)][Bytecodes::ToByte(current)];\n" |
+ << "}\n\n"; |
+} |
+ |
+void PeepholeActionTableWriter::BuildTable() { |
+ std::map<size_t, size_t> hash_to_row_map; |
+ Row row; |
+ for (size_t i = 0; i < kNumberOfBytecodes; ++i) { |
+ uint8_t byte_value = static_cast<uint8_t>(i); |
+ Bytecode last = Bytecodes::FromByte(byte_value); |
+ BuildRow(last, &row); |
+ size_t row_hash = HashRow(&row); |
+ InsertRow(i, &row, row_hash, &hash_to_row_map); |
+ } |
+} |
+ |
+void PeepholeActionTableWriter::BuildRow(Bytecode last, Row* row) { |
+ for (size_t i = 0; i < kNumberOfBytecodes; ++i) { |
+ uint8_t byte_value = static_cast<uint8_t>(i); |
+ Bytecode current = Bytecodes::FromByte(byte_value); |
+ PeepholeActionAndData action_data = LookupActionAndData(last, current); |
+ row->at(i) = action_data; |
+ } |
+} |
+ |
+// static |
+bool PeepholeActionTableWriter::RowsEqual(const Row* const first, |
+ const Row* const second) { |
+ return memcmp(first, second, sizeof(*first)) == 0; |
+} |
+ |
+// static |
+void PeepholeActionTableWriter::InsertRow( |
+ size_t row_index, const Row* const row, size_t row_hash, |
+ std::map<size_t, size_t>* hash_to_row_map) { |
+ // Insert row if no existing row matches, otherwise use existing row. |
+ auto iter = hash_to_row_map->find(row_hash); |
+ if (iter == hash_to_row_map->end()) { |
+ row_map_[row_index] = table()->size(); |
+ table()->push_back(*row); |
+ } else { |
+ row_map_[row_index] = iter->second; |
+ |
+ // If the following DCHECK fails, the HashRow() is not adequate. |
+ DCHECK(RowsEqual(&table()->at(iter->second), row)); |
+ } |
+} |
+ |
+// static |
+size_t PeepholeActionTableWriter::HashRow(const Row* row) { |
+ static const size_t kHashShift = 3; |
+ std::size_t result = (1u << 31) - 1u; |
+ const uint8_t* raw_data = reinterpret_cast<const uint8_t*>(row); |
+ for (size_t i = 0; i < sizeof(*row); ++i) { |
+ size_t top_bits = result >> (kBitsPerByte * sizeof(size_t) - kHashShift); |
+ result = (result << kHashShift) ^ top_bits ^ raw_data[i]; |
+ } |
+ return result; |
+} |
+ |
+} // namespace interpreter |
+} // namespace internal |
+} // namespace v8 |
+ |
+int main(int argc, const char* argv[]) { |
+ CHECK_EQ(argc, 2); |
+ |
+ std::ofstream ofs(argv[1], std::ofstream::trunc); |
+ v8::internal::interpreter::PeepholeActionTableWriter writer; |
+ writer.BuildTable(); |
+ writer.Write(ofs); |
+ ofs.flush(); |
+ ofs.close(); |
+ |
+ return 0; |
+} |