Commit ecc0bc8f authored by Tobias Tebbi's avatar Tobias Tebbi Committed by V8 LUCI CQ

[turboshaft] add basic optimization phase: liveness analysis

Bug: v8:12783
Change-Id: I15cf16bd66a97c33170ca4f1f5e3acc6ff9bf956
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3576129
Auto-Submit: Tobias Tebbi <tebbi@chromium.org>
Commit-Queue: Tobias Tebbi <tebbi@chromium.org>
Reviewed-by: 's avatarNico Hartmann <nicohartmann@chromium.org>
Cr-Commit-Position: refs/heads/main@{#80618}
parent de877f74
...@@ -2828,6 +2828,8 @@ filegroup( ...@@ -2828,6 +2828,8 @@ filegroup(
"src/compiler/turboshaft/graph.h", "src/compiler/turboshaft/graph.h",
"src/compiler/turboshaft/operations.cc", "src/compiler/turboshaft/operations.cc",
"src/compiler/turboshaft/operations.h", "src/compiler/turboshaft/operations.h",
"src/compiler/turboshaft/optimization-phase.cc",
"src/compiler/turboshaft/optimization-phase.h",
"src/compiler/turboshaft/recreate-schedule.cc", "src/compiler/turboshaft/recreate-schedule.cc",
"src/compiler/turboshaft/recreate-schedule.h", "src/compiler/turboshaft/recreate-schedule.h",
"src/compiler/type-cache.cc", "src/compiler/type-cache.cc",
......
...@@ -2905,6 +2905,7 @@ v8_header_set("v8_internal_headers") { ...@@ -2905,6 +2905,7 @@ v8_header_set("v8_internal_headers") {
"src/compiler/turboshaft/graph-builder.h", "src/compiler/turboshaft/graph-builder.h",
"src/compiler/turboshaft/graph.h", "src/compiler/turboshaft/graph.h",
"src/compiler/turboshaft/operations.h", "src/compiler/turboshaft/operations.h",
"src/compiler/turboshaft/optimization-phase.h",
"src/compiler/turboshaft/recreate-schedule.h", "src/compiler/turboshaft/recreate-schedule.h",
"src/compiler/type-cache.h", "src/compiler/type-cache.h",
"src/compiler/type-narrowing-reducer.h", "src/compiler/type-narrowing-reducer.h",
...@@ -4106,6 +4107,7 @@ v8_source_set("v8_turboshaft") { ...@@ -4106,6 +4107,7 @@ v8_source_set("v8_turboshaft") {
"src/compiler/turboshaft/graph-builder.cc", "src/compiler/turboshaft/graph-builder.cc",
"src/compiler/turboshaft/graph.cc", "src/compiler/turboshaft/graph.cc",
"src/compiler/turboshaft/operations.cc", "src/compiler/turboshaft/operations.cc",
"src/compiler/turboshaft/optimization-phase.cc",
"src/compiler/turboshaft/recreate-schedule.cc", "src/compiler/turboshaft/recreate-schedule.cc",
] ]
......
...@@ -78,6 +78,7 @@ ...@@ -78,6 +78,7 @@
#include "src/compiler/turboshaft/assembler.h" #include "src/compiler/turboshaft/assembler.h"
#include "src/compiler/turboshaft/graph-builder.h" #include "src/compiler/turboshaft/graph-builder.h"
#include "src/compiler/turboshaft/graph.h" #include "src/compiler/turboshaft/graph.h"
#include "src/compiler/turboshaft/optimization-phase.h"
#include "src/compiler/turboshaft/recreate-schedule.h" #include "src/compiler/turboshaft/recreate-schedule.h"
#include "src/compiler/type-narrowing-reducer.h" #include "src/compiler/type-narrowing-reducer.h"
#include "src/compiler/typed-optimization.h" #include "src/compiler/typed-optimization.h"
...@@ -2011,7 +2012,7 @@ struct BranchConditionDuplicationPhase { ...@@ -2011,7 +2012,7 @@ struct BranchConditionDuplicationPhase {
}; };
struct BuildTurboshaftPhase { struct BuildTurboshaftPhase {
DECL_PIPELINE_PHASE_CONSTANTS(BuildTurboShaft) DECL_PIPELINE_PHASE_CONSTANTS(BuildTurboshaft)
void Run(PipelineData* data, Zone* temp_zone) { void Run(PipelineData* data, Zone* temp_zone) {
turboshaft::BuildGraph(data->schedule(), data->graph_zone(), temp_zone, turboshaft::BuildGraph(data->schedule(), data->graph_zone(), temp_zone,
...@@ -2020,6 +2021,16 @@ struct BuildTurboshaftPhase { ...@@ -2020,6 +2021,16 @@ struct BuildTurboshaftPhase {
} }
}; };
struct OptimizeTurboshaftPhase {
DECL_PIPELINE_PHASE_CONSTANTS(OptimizeTurboshaft)
void Run(PipelineData* data, Zone* temp_zone) {
turboshaft::OptimizationPhase<
turboshaft::LivenessAnalyzer,
turboshaft::Assembler>::Run(&data->turboshaft_graph(), temp_zone);
}
};
struct TurboshaftRecreateSchedulePhase { struct TurboshaftRecreateSchedulePhase {
DECL_PIPELINE_PHASE_CONSTANTS(TurboshaftRecreateSchedule) DECL_PIPELINE_PHASE_CONSTANTS(TurboshaftRecreateSchedule)
...@@ -2860,10 +2871,12 @@ bool PipelineImpl::OptimizeGraph(Linkage* linkage) { ...@@ -2860,10 +2871,12 @@ bool PipelineImpl::OptimizeGraph(Linkage* linkage) {
AllowHandleDereference allow_deref; AllowHandleDereference allow_deref;
CodeTracer::StreamScope tracing_scope(data->GetCodeTracer()); CodeTracer::StreamScope tracing_scope(data->GetCodeTracer());
tracing_scope.stream() tracing_scope.stream()
<< "\n-- TurboShaft Graph ----------------------------\n" << "\n-- Turboshaft Graph ----------------------------\n"
<< data->turboshaft_graph(); << data->turboshaft_graph();
} }
Run<OptimizeTurboshaftPhase>();
Run<TurboshaftRecreateSchedulePhase>(linkage); Run<TurboshaftRecreateSchedulePhase>(linkage);
if (data->info()->trace_turbo_graph() || FLAG_trace_turbo_scheduler) { if (data->info()->trace_turbo_graph() || FLAG_trace_turbo_scheduler) {
UnparkedScopeIfNeeded scope(data->broker()); UnparkedScopeIfNeeded scope(data->broker());
......
...@@ -170,7 +170,8 @@ void GraphBuilder::Run() { ...@@ -170,7 +170,8 @@ void GraphBuilder::Run() {
DCHECK_EQ(block->SuccessorCount(), 1); DCHECK_EQ(block->SuccessorCount(), 1);
Block* destination = Map(block->SuccessorAt(0)); Block* destination = Map(block->SuccessorAt(0));
assembler.Goto(destination); assembler.Goto(destination);
if (destination->IsLoop()) { if (destination->IsBound()) {
DCHECK(destination->IsLoop());
FixLoopPhis(destination, target_block); FixLoopPhis(destination, target_block);
} }
break; break;
......
...@@ -22,7 +22,7 @@ namespace v8::internal::compiler::turboshaft { ...@@ -22,7 +22,7 @@ namespace v8::internal::compiler::turboshaft {
class Assembler; class Assembler;
class VarAssembler; class VarAssembler;
// `OperationBuffer` is a growable, Zone-allocated buffer to store TurboShaft // `OperationBuffer` is a growable, Zone-allocated buffer to store Turboshaft
// operations. It is part of a `Graph`. // operations. It is part of a `Graph`.
// The buffer can be seen as an array of 8-byte `OperationStorageSlot` values. // The buffer can be seen as an array of 8-byte `OperationStorageSlot` values.
// The structure is append-only, that is, we only add operations at the end. // The structure is append-only, that is, we only add operations at the end.
...@@ -220,8 +220,19 @@ class Block { ...@@ -220,8 +220,19 @@ class Block {
return result; return result;
} }
Block* LastPredecessor() const { return last_predecessor_; }
Block* NeighboringPredecessor() const { return neighboring_predecessor_; }
bool HasPredecessors() const { return last_predecessor_ != nullptr; } bool HasPredecessors() const { return last_predecessor_ != nullptr; }
// The block from the previous graph which produced the current block. This is
// used for translating phi nodes from the previous graph.
void SetOrigin(const Block* origin) {
DCHECK_NULL(origin_);
DCHECK_NE(origin->graph_, graph_);
origin_ = origin;
}
const Block* Origin() const { return origin_; }
OpIndex begin() const { OpIndex begin() const {
DCHECK(begin_.valid()); DCHECK(begin_.valid());
return begin_; return begin_;
...@@ -243,6 +254,7 @@ class Block { ...@@ -243,6 +254,7 @@ class Block {
BlockIndex index_ = BlockIndex::Invalid(); BlockIndex index_ = BlockIndex::Invalid();
Block* last_predecessor_ = nullptr; Block* last_predecessor_ = nullptr;
Block* neighboring_predecessor_ = nullptr; Block* neighboring_predecessor_ = nullptr;
const Block* origin_ = nullptr;
#ifdef DEBUG #ifdef DEBUG
Graph* graph_ = nullptr; Graph* graph_ = nullptr;
#endif #endif
...@@ -342,7 +354,7 @@ class Graph { ...@@ -342,7 +354,7 @@ class Graph {
return result; return result;
} }
bool Add(Block* block) { V8_INLINE bool Add(Block* block) {
DCHECK_EQ(block->graph_, this); DCHECK_EQ(block->graph_, this);
if (!bound_blocks_.empty() && !block->HasPredecessors()) return false; if (!bound_blocks_.empty() && !block->HasPredecessors()) return false;
bool deferred = true; bool deferred = true;
...@@ -435,12 +447,16 @@ class Graph { ...@@ -435,12 +447,16 @@ class Graph {
base::iterator_range<ConstOperationIterator> operations(OpIndex begin, base::iterator_range<ConstOperationIterator> operations(OpIndex begin,
OpIndex end) const { OpIndex end) const {
DCHECK(begin.valid());
DCHECK(end.valid());
return {ConstOperationIterator(begin, this), return {ConstOperationIterator(begin, this),
ConstOperationIterator(end, this)}; ConstOperationIterator(end, this)};
} }
base::iterator_range<MutableOperationIterator> operations(OpIndex begin, base::iterator_range<MutableOperationIterator> operations(OpIndex begin,
OpIndex end) { OpIndex end) {
DCHECK(begin.valid());
DCHECK(end.valid());
return {MutableOperationIterator(begin, this), return {MutableOperationIterator(begin, this),
MutableOperationIterator(end, this)}; MutableOperationIterator(end, this)};
} }
......
...@@ -220,7 +220,7 @@ struct OpProperties { ...@@ -220,7 +220,7 @@ struct OpProperties {
} }
}; };
// Baseclass for all TurboShaft operations. // Baseclass for all Turboshaft operations.
// The `alignas(OpIndex)` is necessary because it is followed by an array of // The `alignas(OpIndex)` is necessary because it is followed by an array of
// `OpIndex` inputs. // `OpIndex` inputs.
struct alignas(OpIndex) Operation { struct alignas(OpIndex) Operation {
...@@ -308,24 +308,25 @@ struct OperationT : Operation { ...@@ -308,24 +308,25 @@ struct OperationT : Operation {
static constexpr OpProperties properties() { return Derived::properties; } static constexpr OpProperties properties() { return Derived::properties; }
Derived& derived_this() { return *static_cast<Derived*>(this); }
const Derived& derived_this() const {
return *static_cast<const Derived*>(this);
}
// Shadow Operation::inputs to exploit static knowledge about object size. // Shadow Operation::inputs to exploit static knowledge about object size.
base::Vector<OpIndex> inputs() { base::Vector<OpIndex> inputs() {
return {reinterpret_cast<OpIndex*>(reinterpret_cast<char*>(this) + return {reinterpret_cast<OpIndex*>(reinterpret_cast<char*>(this) +
sizeof(Derived)), sizeof(Derived)),
input_count}; derived_this().input_count};
} }
base::Vector<const OpIndex> inputs() const { base::Vector<const OpIndex> inputs() const {
return {reinterpret_cast<const OpIndex*>( return {reinterpret_cast<const OpIndex*>(
reinterpret_cast<const char*>(this) + sizeof(Derived)), reinterpret_cast<const char*>(this) + sizeof(Derived)),
input_count}; derived_this().input_count};
} }
V8_INLINE OpIndex& input(size_t i) { V8_INLINE OpIndex& input(size_t i) { return derived_this().inputs()[i]; }
return static_cast<Derived*>(this)->inputs()[i]; V8_INLINE OpIndex input(size_t i) const { return derived_this().inputs()[i]; }
}
V8_INLINE OpIndex input(size_t i) const {
return static_cast<const Derived*>(this)->inputs()[i];
}
static size_t StorageSlotCount(size_t input_count) { static size_t StorageSlotCount(size_t input_count) {
// The operation size in bytes is: // The operation size in bytes is:
...@@ -373,8 +374,8 @@ struct OperationT : Operation { ...@@ -373,8 +374,8 @@ struct OperationT : Operation {
bool operator==(const Derived& other) const { bool operator==(const Derived& other) const {
const Derived& derived = *static_cast<const Derived*>(this); const Derived& derived = *static_cast<const Derived*>(this);
if (derived.inputs() != other.inputs()) return false; return derived.inputs() == other.inputs() &&
return derived.options() == other.options(); derived.options() == other.options();
} }
size_t hash_value() const { size_t hash_value() const {
const Derived& derived = *static_cast<const Derived*>(this); const Derived& derived = *static_cast<const Derived*>(this);
...@@ -382,7 +383,7 @@ struct OperationT : Operation { ...@@ -382,7 +383,7 @@ struct OperationT : Operation {
} }
void PrintOptions(std::ostream& os) const { void PrintOptions(std::ostream& os) const {
const auto& options = static_cast<const Derived*>(this)->options(); const auto& options = derived_this().options();
constexpr size_t options_count = constexpr size_t options_count =
std::tuple_size<std::remove_reference_t<decltype(options)>>::value; std::tuple_size<std::remove_reference_t<decltype(options)>>::value;
if (options_count == 0) { if (options_count == 0) {
...@@ -411,19 +412,8 @@ struct FixedArityOperationT : OperationT<Derived> { ...@@ -411,19 +412,8 @@ struct FixedArityOperationT : OperationT<Derived> {
// Enable concise base access in derived struct. // Enable concise base access in derived struct.
using Base = FixedArityOperationT; using Base = FixedArityOperationT;
// Shadow OperationT<Derived>::inputs to exploit static knowledge about input // Shadow Operation::input_count to exploit static knowledge.
// count.
static constexpr uint16_t input_count = InputCount; static constexpr uint16_t input_count = InputCount;
base::Vector<OpIndex> inputs() {
return {reinterpret_cast<OpIndex*>(reinterpret_cast<char*>(this) +
sizeof(Derived)),
InputCount};
}
base::Vector<const OpIndex> inputs() const {
return {reinterpret_cast<const OpIndex*>(
reinterpret_cast<const char*>(this) + sizeof(Derived)),
InputCount};
}
template <class... Args> template <class... Args>
explicit FixedArityOperationT(Args... args) explicit FixedArityOperationT(Args... args)
...@@ -434,12 +424,6 @@ struct FixedArityOperationT : OperationT<Derived> { ...@@ -434,12 +424,6 @@ struct FixedArityOperationT : OperationT<Derived> {
((inputs[i++] = args), ...); ((inputs[i++] = args), ...);
} }
bool operator==(const Derived& other) const {
return std::equal(inputs().begin(), inputs().end(),
other.inputs().begin()) &&
static_cast<const Derived*>(this)->options() == other.options();
}
// Redefine the input initialization to tell C++ about the static input size. // Redefine the input initialization to tell C++ about the static input size.
template <class... Args> template <class... Args>
static Derived& New(Graph* graph, Args... args) { static Derived& New(Graph* graph, Args... args) {
...@@ -695,6 +679,8 @@ struct PhiOp : OperationT<PhiOp> { ...@@ -695,6 +679,8 @@ struct PhiOp : OperationT<PhiOp> {
static constexpr OpProperties properties = OpProperties::Pure(); static constexpr OpProperties properties = OpProperties::Pure();
static constexpr size_t kLoopPhiBackEdgeIndex = 1;
explicit PhiOp(base::Vector<const OpIndex> inputs, MachineRepresentation rep) explicit PhiOp(base::Vector<const OpIndex> inputs, MachineRepresentation rep)
: Base(inputs), rep(rep) {} : Base(inputs), rep(rep) {}
auto options() const { return std::tuple{rep}; } auto options() const { return std::tuple{rep}; }
...@@ -705,7 +691,7 @@ struct PhiOp : OperationT<PhiOp> { ...@@ -705,7 +691,7 @@ struct PhiOp : OperationT<PhiOp> {
struct PendingLoopPhiOp : FixedArityOperationT<1, PendingLoopPhiOp> { struct PendingLoopPhiOp : FixedArityOperationT<1, PendingLoopPhiOp> {
MachineRepresentation rep; MachineRepresentation rep;
union { union {
// Used when transforming a TurboShaft graph. // Used when transforming a Turboshaft graph.
// This is not an input because it refers to the old graph. // This is not an input because it refers to the old graph.
OpIndex old_backedge_index = OpIndex::Invalid(); OpIndex old_backedge_index = OpIndex::Invalid();
// Used when translating from sea-of-nodes. // Used when translating from sea-of-nodes.
...@@ -896,6 +882,8 @@ struct ConstantOp : FixedArityOperationT<0, ConstantOp> { ...@@ -896,6 +882,8 @@ struct ConstantOp : FixedArityOperationT<0, ConstantOp> {
} }
} }
auto options() const { return std::tuple{kind, storage}; }
void PrintOptions(std::ostream& os) const; void PrintOptions(std::ostream& os) const;
size_t hash_value() const { size_t hash_value() const {
switch (kind) { switch (kind) {
...@@ -985,7 +973,7 @@ struct IndexedLoadOp : FixedArityOperationT<2, IndexedLoadOp> { ...@@ -985,7 +973,7 @@ struct IndexedLoadOp : FixedArityOperationT<2, IndexedLoadOp> {
offset(offset) {} offset(offset) {}
void PrintOptions(std::ostream& os) const; void PrintOptions(std::ostream& os) const;
auto options() const { auto options() const {
return std::tuple{kind, loaded_rep, element_size_log2, offset}; return std::tuple{kind, loaded_rep, offset, element_size_log2};
} }
}; };
...@@ -1047,8 +1035,8 @@ struct IndexedStoreOp : FixedArityOperationT<3, IndexedStoreOp> { ...@@ -1047,8 +1035,8 @@ struct IndexedStoreOp : FixedArityOperationT<3, IndexedStoreOp> {
offset(offset) {} offset(offset) {}
void PrintOptions(std::ostream& os) const; void PrintOptions(std::ostream& os) const;
auto options() const { auto options() const {
return std::tuple{kind, stored_rep, write_barrier, element_size_log2, return std::tuple{kind, stored_rep, write_barrier, offset,
offset}; element_size_log2};
} }
}; };
......
// Copyright 2022 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 "src/compiler/turboshaft/optimization-phase.h"
namespace v8::internal::compiler::turboshaft {
int CountDecimalDigits(uint32_t value) {
int result = 1;
while (value > 9) {
result++;
value = value / 10;
}
return result;
}
std::ostream& operator<<(std::ostream& os, PaddingSpace padding) {
if (padding.spaces > 10000) return os;
for (int i = 0; i < padding.spaces; ++i) {
os << ' ';
}
return os;
}
} // namespace v8::internal::compiler::turboshaft
This diff is collapsed.
...@@ -106,7 +106,7 @@ RecreateScheduleResult ScheduleBuilder::Run() { ...@@ -106,7 +106,7 @@ RecreateScheduleResult ScheduleBuilder::Run() {
DCHECK_GE(input_graph.block_count(), 1); DCHECK_GE(input_graph.block_count(), 1);
// The schedule needs to contain an dummy end block because the register // The schedule needs to contain an dummy end block because the register
// allocator expects this. This block is not actually reachable with control // allocator expects this. This block is not actually reachable with control
// flow. It is added here because the TurboShaft grahp doesn't contain such a // flow. It is added here because the Turboshaft grahp doesn't contain such a
// block. // block.
blocks.reserve(input_graph.block_count() + 1); blocks.reserve(input_graph.block_count() + 1);
blocks.push_back(current_block); blocks.push_back(current_block);
......
...@@ -966,7 +966,9 @@ DEFINE_FLOAT(script_delay_fraction, 0.0, ...@@ -966,7 +966,9 @@ DEFINE_FLOAT(script_delay_fraction, 0.0,
"busy wait after each Script::Run by the given fraction of the " "busy wait after each Script::Run by the given fraction of the "
"run's duration") "run's duration")
DEFINE_BOOL(turboshaft, false, "enable TurboFan's TurboShaft phases") DEFINE_BOOL(turboshaft, false, "enable TurboFan's Turboshaft phases")
DEFINE_BOOL(turboshaft_trace_reduction, false,
"trace individual Turboshaft reduction steps")
// Favor memory over execution speed. // Favor memory over execution speed.
DEFINE_BOOL(optimize_for_size, false, DEFINE_BOOL(optimize_for_size, false,
......
...@@ -369,7 +369,8 @@ class RuntimeCallTimer final { ...@@ -369,7 +369,8 @@ class RuntimeCallTimer final {
ADD_THREAD_SPECIFIC_COUNTER(V, Optimize, SimplifiedLowering) \ ADD_THREAD_SPECIFIC_COUNTER(V, Optimize, SimplifiedLowering) \
ADD_THREAD_SPECIFIC_COUNTER(V, Optimize, StoreStoreElimination) \ ADD_THREAD_SPECIFIC_COUNTER(V, Optimize, StoreStoreElimination) \
ADD_THREAD_SPECIFIC_COUNTER(V, Optimize, TraceScheduleAndVerify) \ ADD_THREAD_SPECIFIC_COUNTER(V, Optimize, TraceScheduleAndVerify) \
ADD_THREAD_SPECIFIC_COUNTER(V, Optimize, BuildTurboShaft) \ ADD_THREAD_SPECIFIC_COUNTER(V, Optimize, BuildTurboshaft) \
ADD_THREAD_SPECIFIC_COUNTER(V, Optimize, OptimizeTurboshaft) \
ADD_THREAD_SPECIFIC_COUNTER(V, Optimize, TurboshaftRecreateSchedule) \ ADD_THREAD_SPECIFIC_COUNTER(V, Optimize, TurboshaftRecreateSchedule) \
ADD_THREAD_SPECIFIC_COUNTER(V, Optimize, TypeAssertions) \ ADD_THREAD_SPECIFIC_COUNTER(V, Optimize, TypeAssertions) \
ADD_THREAD_SPECIFIC_COUNTER(V, Optimize, TypedLowering) \ ADD_THREAD_SPECIFIC_COUNTER(V, Optimize, TypedLowering) \
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment