Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(669)

Unified Diff: src/interpreter/mkpeephole.cc

Issue 2118183002: [interpeter] Move to table based peephole optimizer. (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: Broader check for host and target arch wanting separate toolchain. Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/interpreter/bytecodes.cc ('k') | src/v8.gyp » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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;
+}
« no previous file with comments | « src/interpreter/bytecodes.cc ('k') | src/v8.gyp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698