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

[turboshaft] initial commit

TurboShaft is a new, CFG-based IR for TurboFan.
This CL adds the basic IR and bidirectional translation from/to
TurboFan's sea-of-nodes-based IR for some common operators (still
incomplete even for JS).

Bug: v8:12783
Change-Id: I162fdf10d583a9275a9f655f5b44b888faf813f6
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3563562Reviewed-by: 's avatarClemens Backes <clemensb@chromium.org>
Reviewed-by: 's avatarMaya Lekova <mslekova@chromium.org>
Reviewed-by: 's avatarNico Hartmann <nicohartmann@chromium.org>
Commit-Queue: Tobias Tebbi <tebbi@chromium.org>
Cr-Commit-Position: refs/heads/main@{#80136}
parent f11e4028
......@@ -2798,6 +2798,16 @@ filegroup(
"src/compiler/state-values-utils.h",
"src/compiler/store-store-elimination.cc",
"src/compiler/store-store-elimination.h",
"src/compiler/turboshaft/assembler.h",
"src/compiler/turboshaft/deopt-data.h",
"src/compiler/turboshaft/graph-builder.cc",
"src/compiler/turboshaft/graph-builder.h",
"src/compiler/turboshaft/graph.cc",
"src/compiler/turboshaft/graph.h",
"src/compiler/turboshaft/operations.cc",
"src/compiler/turboshaft/operations.h",
"src/compiler/turboshaft/recreate-schedule.cc",
"src/compiler/turboshaft/recreate-schedule.h",
"src/compiler/type-cache.cc",
"src/compiler/type-cache.h",
"src/compiler/type-narrowing-reducer.cc",
......
......@@ -2876,6 +2876,12 @@ v8_header_set("v8_internal_headers") {
"src/compiler/simplified-operator.h",
"src/compiler/state-values-utils.h",
"src/compiler/store-store-elimination.h",
"src/compiler/turboshaft/assembler.h",
"src/compiler/turboshaft/deopt-data.h",
"src/compiler/turboshaft/graph-builder.h",
"src/compiler/turboshaft/graph.h",
"src/compiler/turboshaft/operations.h",
"src/compiler/turboshaft/recreate-schedule.h",
"src/compiler/type-cache.h",
"src/compiler/type-narrowing-reducer.h",
"src/compiler/typed-optimization.h",
......@@ -4060,6 +4066,34 @@ v8_source_set("v8_compiler") {
configs = [ ":internal_config" ]
}
# The src/compiler files with default optimization behavior.
v8_source_set("v8_turboshaft") {
visibility = [ ":*" ] # Only targets in this file can depend on this.
sources = [
"src/compiler/turboshaft/graph-builder.cc",
"src/compiler/turboshaft/graph.cc",
"src/compiler/turboshaft/operations.cc",
"src/compiler/turboshaft/recreate-schedule.cc",
]
public_deps = [
":generate_bytecode_builtins_list",
":run_torque",
":v8_internal_headers",
":v8_maybe_icu",
":v8_tracing",
]
deps = [
":v8_base_without_compiler",
":v8_libbase",
":v8_shared_internal_headers",
]
configs = [ ":internal_config" ]
}
group("v8_compiler_for_mksnapshot") {
if (is_debug && !v8_optimized_debug && v8_enable_fast_mksnapshot) {
deps = [ ":v8_compiler_opt" ]
......@@ -4971,6 +5005,7 @@ group("v8_base") {
public_deps = [
":v8_base_without_compiler",
":v8_compiler",
":v8_turboshaft",
]
}
......@@ -5879,6 +5914,7 @@ if (current_toolchain == v8_snapshot_toolchain) {
":v8_maybe_icu",
":v8_shared_internal_headers",
":v8_tracing",
":v8_turboshaft",
"//build/win:default_exe_manifest",
]
}
......
......@@ -11,6 +11,7 @@
#include <cstddef>
#include <cstring>
#include <functional>
#include <type_traits>
#include <utility>
#include "src/base/base-export.h"
......@@ -137,6 +138,22 @@ V8_INLINE size_t hash_value(std::pair<T1, T2> const& v) {
return hash_combine(v.first, v.second);
}
template <typename... T, size_t... I>
V8_INLINE size_t hash_value_impl(std::tuple<T...> const& v,
std::index_sequence<I...>) {
return hash_combine(std::get<I>(v)...);
}
template <typename... T>
V8_INLINE size_t hash_value(std::tuple<T...> const& v) {
return hash_value_impl(v, std::make_index_sequence<sizeof...(T)>());
}
template <typename T, typename = std::enable_if_t<std::is_enum<T>::value>>
V8_INLINE size_t hash_value(T v) {
return hash_value(static_cast<std::underlying_type_t<T>>(v));
}
template <typename T>
struct hash {
V8_INLINE size_t operator()(T const& v) const { return hash_value(v); }
......
......@@ -39,12 +39,12 @@ class iterator_range {
iterator_range(ForwardIterator begin, ForwardIterator end)
: begin_(begin), end_(end) {}
iterator begin() { return begin_; }
iterator end() { return end_; }
const_iterator begin() const { return begin_; }
const_iterator end() const { return end_; }
iterator begin() const { return begin_; }
iterator end() const { return end_; }
const_iterator cbegin() const { return begin_; }
const_iterator cend() const { return end_; }
auto rbegin() const { return std::make_reverse_iterator(end_); }
auto rend() const { return std::make_reverse_iterator(begin_); }
bool empty() const { return cbegin() == cend(); }
......@@ -62,6 +62,24 @@ auto make_iterator_range(ForwardIterator begin, ForwardIterator end) {
return iterator_range<ForwardIterator>{begin, end};
}
template <class T>
struct DerefPtrIterator : base::iterator<std::bidirectional_iterator_tag, T> {
T* const* ptr;
explicit DerefPtrIterator(T* const* ptr) : ptr(ptr) {}
T& operator*() { return **ptr; }
DerefPtrIterator& operator++() {
++ptr;
return *this;
}
DerefPtrIterator& operator--() {
--ptr;
return *this;
}
bool operator!=(DerefPtrIterator other) { return ptr != other.ptr; }
};
// {Reversed} returns a container adapter usable in a range-based "for"
// statement for iterating a reversible container in reverse order.
//
......@@ -71,11 +89,22 @@ auto make_iterator_range(ForwardIterator begin, ForwardIterator end) {
// for (int i : base::Reversed(v)) {
// // iterates through v from back to front
// }
//
// The signature avoids binding to temporaries (T&& / const T&) on purpose. The
// lifetime of a temporary would not extend to a range-based for loop using it.
template <typename T>
auto Reversed(T& t) { // NOLINT(runtime/references): match {rbegin} and {rend}
return make_iterator_range(std::rbegin(t), std::rend(t));
}
// This overload of `Reversed` is safe even when the argument is a temporary,
// because we rely on the wrapped iterators instead of the `iterator_range`
// object itself.
template <typename T>
auto Reversed(const iterator_range<T>& t) {
return make_iterator_range(std::rbegin(t), std::rend(t));
}
} // namespace base
} // namespace v8
......
......@@ -11,6 +11,7 @@
#include "src/base/bits.h"
#include "src/base/macros.h"
#include "src/base/vector.h"
namespace v8 {
namespace base {
......@@ -29,7 +30,8 @@ class SmallVector {
explicit SmallVector(const Allocator& allocator = Allocator())
: allocator_(allocator) {}
explicit SmallVector(size_t size, const Allocator& allocator = Allocator())
explicit V8_INLINE SmallVector(size_t size,
const Allocator& allocator = Allocator())
: allocator_(allocator) {
resize_no_init(size);
}
......@@ -43,10 +45,14 @@ class SmallVector {
: allocator_(allocator) {
*this = std::move(other);
}
SmallVector(std::initializer_list<T> init,
const Allocator& allocator = Allocator())
: allocator_(allocator) {
resize_no_init(init.size());
V8_INLINE SmallVector(std::initializer_list<T> init,
const Allocator& allocator = Allocator())
: SmallVector(init.size(), allocator) {
memcpy(begin_, init.begin(), sizeof(T) * init.size());
}
explicit V8_INLINE SmallVector(base::Vector<const T> init,
const Allocator& allocator = Allocator())
: SmallVector(init.size(), allocator) {
memcpy(begin_, init.begin(), sizeof(T) * init.size());
}
......@@ -127,6 +133,8 @@ class SmallVector {
end_ = end + 1;
}
void push_back(T x) { emplace_back(std::move(x)); }
void pop_back(size_t count = 1) {
DCHECK_GE(size(), count);
end_ -= count;
......
......@@ -12,6 +12,7 @@
#include <memory>
#include <type_traits>
#include "src/base/functional.h"
#include "src/base/logging.h"
#include "src/base/macros.h"
......@@ -42,6 +43,21 @@ class Vector {
DCHECK_LE(to, length_);
return Vector<T>(begin() + from, to - from);
}
Vector<T> SubVectorFrom(size_t from) const {
return SubVector(from, length_);
}
template <class U>
void OverwriteWith(Vector<U> other) {
DCHECK_EQ(size(), other.size());
std::copy(other.begin(), other.end(), begin());
}
template <class U, size_t n>
void OverwriteWith(const std::array<U, n>& other) {
DCHECK_EQ(size(), other.size());
std::copy(other.begin(), other.end(), begin());
}
// Returns the length of the vector. Only use this if you really need an
// integer return value. Use {size()} otherwise.
......@@ -80,6 +96,13 @@ class Vector {
// Returns a pointer past the end of the data in the vector.
constexpr T* end() const { return start_ + length_; }
constexpr std::reverse_iterator<T*> rbegin() const {
return std::make_reverse_iterator(end());
}
constexpr std::reverse_iterator<T*> rend() const {
return std::make_reverse_iterator(begin());
}
// Returns a clone of this vector with a new backing store.
Vector<T> Clone() const {
T* result = new T[length_];
......@@ -140,6 +163,11 @@ class Vector {
size_t length_;
};
template <typename T>
V8_INLINE size_t hash_value(base::Vector<T> v) {
return hash_range(v.begin(), v.end());
}
template <typename T>
class V8_NODISCARD ScopedVector : public Vector<T> {
public:
......
......@@ -6,6 +6,7 @@
#define V8_CODEGEN_MACHINE_TYPE_H_
#include <iosfwd>
#include <limits>
#include "include/v8-fast-api-calls.h"
#include "src/base/bits.h"
......@@ -417,6 +418,25 @@ V8_EXPORT_PRIVATE inline constexpr int ElementSizeInBytes(
return 1 << ElementSizeLog2Of(rep);
}
inline constexpr int ElementSizeInBits(MachineRepresentation rep) {
return 8 * ElementSizeInBytes(rep);
}
inline constexpr uint64_t MaxUnsignedValue(MachineRepresentation rep) {
switch (rep) {
case MachineRepresentation::kWord8:
return std::numeric_limits<uint8_t>::max();
case MachineRepresentation::kWord16:
return std::numeric_limits<uint16_t>::max();
case MachineRepresentation::kWord32:
return std::numeric_limits<uint32_t>::max();
case MachineRepresentation::kWord64:
return std::numeric_limits<uint64_t>::max();
default:
UNREACHABLE();
}
}
V8_EXPORT_PRIVATE inline constexpr int ElementSizeInPointers(
MachineRepresentation rep) {
return (ElementSizeInBytes(rep) + kSystemPointerSize - 1) /
......
......@@ -3295,7 +3295,7 @@ FrameStateDescriptor* GetFrameStateDescriptorInternal(Zone* zone,
const FrameStateInfo& state_info = FrameStateInfoOf(state->op());
int parameters = state_info.parameter_count();
int locals = state_info.local_count();
int stack = state_info.type() == FrameStateType::kUnoptimizedFunction ? 1 : 0;
int stack = state_info.stack_count();
FrameStateDescriptor* outer_state = nullptr;
if (state.outer_frame_state()->opcode() == IrOpcode::kFrameState) {
......
......@@ -610,7 +610,7 @@ class FrameState : public CommonNodeWrapperBase {
DCHECK_EQ(node->opcode(), IrOpcode::kFrameState);
}
FrameStateInfo frame_state_info() const {
const FrameStateInfo& frame_state_info() const {
return FrameStateInfoOf(node()->op());
}
......
......@@ -154,6 +154,9 @@ class FrameStateInfo final {
int local_count() const {
return info_ == nullptr ? 0 : info_->local_count();
}
int stack_count() const {
return type() == FrameStateType::kUnoptimizedFunction ? 1 : 0;
}
const FrameStateFunctionInfo* function_info() const { return info_; }
private:
......
......@@ -25,7 +25,7 @@ inline bool CollectFeedbackInGenericLowering() {
return FLAG_turbo_collect_feedback_in_generic_lowering;
}
enum class StackCheckKind {
enum class StackCheckKind : uint8_t {
kJSFunctionEntry = 0,
kJSIterationBody,
kCodeStubAssembler,
......
......@@ -75,6 +75,10 @@
#include "src/compiler/simplified-operator-reducer.h"
#include "src/compiler/simplified-operator.h"
#include "src/compiler/store-store-elimination.h"
#include "src/compiler/turboshaft/assembler.h"
#include "src/compiler/turboshaft/graph-builder.h"
#include "src/compiler/turboshaft/graph.h"
#include "src/compiler/turboshaft/recreate-schedule.h"
#include "src/compiler/type-narrowing-reducer.h"
#include "src/compiler/typed-optimization.h"
#include "src/compiler/typer.h"
......@@ -150,7 +154,7 @@ class PipelineData {
allocator_(isolate->allocator()),
info_(info),
debug_name_(info_->GetDebugName()),
may_have_unverifiable_graph_(false),
may_have_unverifiable_graph_(FLAG_turboshaft),
zone_stats_(zone_stats),
pipeline_statistics_(pipeline_statistics),
graph_zone_scope_(zone_stats_, kGraphZoneName, kCompressGraphZone),
......@@ -168,6 +172,7 @@ class PipelineData {
assembler_options_(AssemblerOptions::Default(isolate)) {
PhaseScope scope(pipeline_statistics, "V8.TFInitPipelineData");
graph_ = graph_zone_->New<Graph>(graph_zone_);
turboshaft_graph_ = std::make_unique<turboshaft::Graph>(graph_zone_);
source_positions_ = graph_zone_->New<SourcePositionTable>(graph_);
node_origins_ = info->trace_turbo_json()
? graph_zone_->New<NodeOriginTable>(graph_)
......@@ -340,6 +345,8 @@ class PipelineData {
Zone* graph_zone() const { return graph_zone_; }
Graph* graph() const { return graph_; }
void set_graph(Graph* graph) { graph_ = graph; }
turboshaft::Graph& turboshaft_graph() const { return *turboshaft_graph_; }
SourcePositionTable* source_positions() const { return source_positions_; }
NodeOriginTable* node_origins() const { return node_origins_; }
MachineOperatorBuilder* machine() const { return machine_; }
......@@ -460,6 +467,7 @@ class PipelineData {
graph_zone_scope_.Destroy();
graph_zone_ = nullptr;
graph_ = nullptr;
turboshaft_graph_ = nullptr;
source_positions_ = nullptr;
node_origins_ = nullptr;
simplified_ = nullptr;
......@@ -619,6 +627,7 @@ class PipelineData {
ZoneStats::Scope graph_zone_scope_;
Zone* graph_zone_ = nullptr;
Graph* graph_ = nullptr;
std::unique_ptr<turboshaft::Graph> turboshaft_graph_ = nullptr;
SourcePositionTable* source_positions_ = nullptr;
NodeOriginTable* node_origins_ = nullptr;
SimplifiedOperatorBuilder* simplified_ = nullptr;
......@@ -1991,6 +2000,28 @@ struct BranchConditionDuplicationPhase {
}
};
struct BuildTurboshaftPhase {
DECL_PIPELINE_PHASE_CONSTANTS(BuildTurboShaft)
void Run(PipelineData* data, Zone* temp_zone) {
turboshaft::BuildGraph(data->schedule(), data->graph_zone(), temp_zone,
&data->turboshaft_graph());
data->reset_schedule();
}
};
struct TurboshaftRecreateSchedulePhase {
DECL_PIPELINE_PHASE_CONSTANTS(TurboshaftRecreateSchedule)
void Run(PipelineData* data, Zone* temp_zone, Linkage* linkage) {
auto result = turboshaft::RecreateSchedule(data->turboshaft_graph(),
linkage->GetIncomingDescriptor(),
data->graph_zone(), temp_zone);
data->set_graph(result.graph);
data->set_schedule(result.schedule);
}
};
#if V8_ENABLE_WEBASSEMBLY
struct WasmOptimizationPhase {
DECL_PIPELINE_PHASE_CONSTANTS(WasmOptimization)
......@@ -2812,6 +2843,28 @@ bool PipelineImpl::OptimizeGraph(Linkage* linkage) {
ComputeScheduledGraph();
if (FLAG_turboshaft) {
Run<BuildTurboshaftPhase>();
if (data->info()->trace_turbo_graph()) {
UnparkedScopeIfNeeded scope(data->broker());
AllowHandleDereference allow_deref;
CodeTracer::StreamScope tracing_scope(data->GetCodeTracer());
tracing_scope.stream()
<< "\n-- TurboShaft Graph ----------------------------\n"
<< data->turboshaft_graph();
}
Run<TurboshaftRecreateSchedulePhase>(linkage);
if (data->info()->trace_turbo_graph() || FLAG_trace_turbo_scheduler) {
UnparkedScopeIfNeeded scope(data->broker());
AllowHandleDereference allow_deref;
CodeTracer::StreamScope tracing_scope(data->GetCodeTracer());
tracing_scope.stream()
<< "\n-- Recreated Schedule ----------------------------\n"
<< *data->schedule();
}
}
return SelectInstructions(linkage);
}
......
// 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.
#ifndef V8_COMPILER_TURBOSHAFT_ASSEMBLER_H_
#define V8_COMPILER_TURBOSHAFT_ASSEMBLER_H_
#include <cstring>
#include <iterator>
#include <limits>
#include <memory>
#include <type_traits>
#include "src/base/iterator.h"
#include "src/base/logging.h"
#include "src/base/macros.h"
#include "src/base/small-vector.h"
#include "src/base/template-utils.h"
#include "src/codegen/machine-type.h"
#include "src/compiler/turboshaft/graph.h"
#include "src/compiler/turboshaft/operations.h"
#include "src/zone/zone-containers.h"
namespace v8::internal::compiler::turboshaft {
// This class is used to extend an assembler with useful short-hands that still
// forward to the regular operations of the deriving assembler.
template <class Subclass, class Superclass>
class AssemblerInterface : public Superclass {
public:
using Superclass::Superclass;
using Base = Superclass;
OpIndex Add(OpIndex left, OpIndex right, MachineRepresentation rep) {
return subclass().Binop(left, right, BinopOp::Kind::kAdd, rep);
}
OpIndex AddWithOverflow(OpIndex left, OpIndex right,
MachineRepresentation rep) {
DCHECK(rep == MachineRepresentation::kWord32 ||
rep == MachineRepresentation::kWord64);
return subclass().OverflowCheckedBinop(
left, right, OverflowCheckedBinopOp::Kind::kSignedAdd, rep);
}
OpIndex Sub(OpIndex left, OpIndex right, MachineRepresentation rep) {
DCHECK(rep == MachineRepresentation::kWord32 ||
rep == MachineRepresentation::kWord64);
return subclass().Binop(left, right, BinopOp::Kind::kSub, rep);
}
OpIndex SubWithOverflow(OpIndex left, OpIndex right,
MachineRepresentation rep) {
return subclass().OverflowCheckedBinop(
left, right, OverflowCheckedBinopOp::Kind::kSignedSub, rep);
}
OpIndex Mul(OpIndex left, OpIndex right, MachineRepresentation rep) {
return subclass().Binop(left, right, BinopOp::Kind::kMul, rep);
}
OpIndex MulWithOverflow(OpIndex left, OpIndex right,
MachineRepresentation rep) {
DCHECK(rep == MachineRepresentation::kWord32 ||
rep == MachineRepresentation::kWord64);
return subclass().OverflowCheckedBinop(
left, right, OverflowCheckedBinopOp::Kind::kSignedMul, rep);
}
OpIndex BitwiseAnd(OpIndex left, OpIndex right, MachineRepresentation rep) {
DCHECK(rep == MachineRepresentation::kWord32 ||
rep == MachineRepresentation::kWord64);
return subclass().Binop(left, right, BinopOp::Kind::kBitwiseAnd, rep);
}
OpIndex BitwiseOr(OpIndex left, OpIndex right, MachineRepresentation rep) {
DCHECK(rep == MachineRepresentation::kWord32 ||
rep == MachineRepresentation::kWord64);
return subclass().Binop(left, right, BinopOp::Kind::kBitwiseOr, rep);
}
OpIndex BitwiseXor(OpIndex left, OpIndex right, MachineRepresentation rep) {
DCHECK(rep == MachineRepresentation::kWord32 ||
rep == MachineRepresentation::kWord64);
return subclass().Binop(left, right, BinopOp::Kind::kBitwiseXor, rep);
}
OpIndex ShiftLeft(OpIndex left, OpIndex right, MachineRepresentation rep) {
DCHECK(rep == MachineRepresentation::kWord32 ||
rep == MachineRepresentation::kWord64);
return subclass().Shift(left, right, ShiftOp::Kind::kShiftLeft, rep);
}
OpIndex Word32Constant(uint32_t value) {
return subclass().Constant(ConstantOp::Kind::kWord32, uint64_t{value});
}
OpIndex Word64Constant(uint64_t value) {
return subclass().Constant(ConstantOp::Kind::kWord64, value);
}
OpIndex IntegralConstant(uint64_t value, MachineRepresentation rep) {
switch (rep) {
case MachineRepresentation::kWord32:
return Word32Constant(static_cast<uint32_t>(value));
case MachineRepresentation::kWord64:
return Word64Constant(value);
default:
UNREACHABLE();
}
}
OpIndex Float32Constant(float value) {
return subclass().Constant(ConstantOp::Kind::kFloat32, value);
}
OpIndex Float64Constant(double value) {
return subclass().Constant(ConstantOp::Kind::kFloat64, value);
}
OpIndex TrucateWord64ToWord32(OpIndex value) {
return subclass().Change(value, ChangeOp::Kind::kIntegerTruncate,
MachineRepresentation::kWord64,
MachineRepresentation::kWord32);
}
private:
Subclass& subclass() { return *static_cast<Subclass*>(this); }
};
// This empty base-class is used to provide default-implementations of plain
// methods emitting operations.
template <class Assembler>
class AssemblerBase {
public:
#define EMIT_OP(Name) \
template <class... Args> \
OpIndex Name(Args... args) { \
return static_cast<Assembler*>(this)->template Emit<Name##Op>(args...); \
}
TURBOSHAFT_OPERATION_LIST(EMIT_OP)
#undef EMIT_OP
};
class Assembler
: public AssemblerInterface<Assembler, AssemblerBase<Assembler>> {
public:
Block* NewBlock(Block::Kind kind) { return graph_.NewBlock(kind); }
V8_INLINE bool Bind(Block* block) {
if (!graph().Add(block)) return false;
DCHECK_NULL(current_block_);
current_block_ = block;
return true;
}
OpIndex Phi(base::Vector<const OpIndex> inputs, MachineRepresentation rep) {
DCHECK(current_block()->IsMerge() &&
inputs.size() == current_block()->Predecessors().size());
return Base::Phi(inputs, rep);
}
template <class... Args>
OpIndex PendingLoopPhi(Args... args) {
DCHECK(current_block()->IsLoop());
return Base::PendingLoopPhi(args...);
}
OpIndex Goto(Block* destination) {
destination->AddPredecessor(current_block());
return Base::Goto(destination);
}
OpIndex Branch(OpIndex condition, Block* if_true, Block* if_false) {
if_true->AddPredecessor(current_block());
if_false->AddPredecessor(current_block());
return Base::Branch(condition, if_true, if_false);
}
OpIndex Switch(OpIndex input, base::Vector<const SwitchOp::Case> cases,
Block* default_case) {
for (SwitchOp::Case c : cases) {
c.destination->AddPredecessor(current_block());
}
default_case->AddPredecessor(current_block());
return Base::Switch(input, cases, default_case);
}
explicit Assembler(Graph* graph, Zone* phase_zone)
: graph_(*graph), phase_zone_(phase_zone) {
graph_.Reset();
}
Block* current_block() { return current_block_; }
Zone* graph_zone() { return graph().graph_zone(); }
Graph& graph() { return graph_; }
Zone* phase_zone() { return phase_zone_; }
private:
friend class AssemblerBase<Assembler>;
void FinalizeBlock() {
graph().Finalize(current_block_);
current_block_ = nullptr;
}
template <class Op, class... Args>
OpIndex Emit(Args... args) {
STATIC_ASSERT((std::is_base_of<Operation, Op>::value));
STATIC_ASSERT(!(std::is_same<Op, Operation>::value));
DCHECK_NOT_NULL(current_block_);
OpIndex result = graph().Add<Op>(args...);
if (Op::properties.is_block_terminator) FinalizeBlock();
return result;
}
Block* current_block_ = nullptr;
Graph& graph_;
Zone* const phase_zone_;
};
} // namespace v8::internal::compiler::turboshaft
#endif // V8_COMPILER_TURBOSHAFT_ASSEMBLER_H_
// 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.
#ifndef V8_COMPILER_TURBOSHAFT_DEOPT_DATA_H_
#define V8_COMPILER_TURBOSHAFT_DEOPT_DATA_H_
#include "src/compiler/turboshaft/operations.h"
namespace v8::internal::compiler::turboshaft {
struct FrameStateData {
// The data is encoded as a pre-traversal of a tree.
enum class Instr : uint8_t {
kInput, // 1 Operand: input machine type
kUnusedRegister,
kDematerializedObject, // 2 Operands: id, field_count
kDematerializedObjectReference // 1 Operand: id
};
class Builder {
public:
void AddParentFrameState(OpIndex parent) {
DCHECK(inputs_.empty());
inlined_ = true;
inputs_.push_back(parent);
}
void AddInput(MachineType type, OpIndex input) {
instructions_.push_back(Instr::kInput);
machine_types_.push_back(type);
inputs_.push_back(input);
}
void AddUnusedRegister() {
instructions_.push_back(Instr::kUnusedRegister);
}
void AddDematerializedObjectReference(uint32_t id) {
instructions_.push_back(Instr::kDematerializedObjectReference);
int_operands_.push_back(id);
}
void AddDematerializedObject(uint32_t id, uint32_t field_count) {
instructions_.push_back(Instr::kDematerializedObject);
int_operands_.push_back(id);
int_operands_.push_back(field_count);
}
const FrameStateData* AllocateFrameStateData(
const FrameStateInfo& frame_state_info, Zone* zone) {
return zone->New<FrameStateData>(FrameStateData{
frame_state_info, zone->CloneVector(base::VectorOf(instructions_)),
zone->CloneVector(base::VectorOf(machine_types_)),
zone->CloneVector(base::VectorOf(int_operands_))});
}
base::Vector<const OpIndex> Inputs() { return base::VectorOf(inputs_); }
bool inlined() const { return inlined_; }
private:
base::SmallVector<Instr, 32> instructions_;
base::SmallVector<MachineType, 32> machine_types_;
base::SmallVector<uint32_t, 16> int_operands_;
base::SmallVector<OpIndex, 32> inputs_;
bool inlined_ = false;
};
struct Iterator {
base::Vector<const Instr> instructions;
base::Vector<const MachineType> machine_types;
base::Vector<const uint32_t> int_operands;
base::Vector<const OpIndex> inputs;
bool has_more() const { return !instructions.empty(); }
Instr current_instr() { return instructions[0]; }
void ConsumeInput(MachineType* machine_type, OpIndex* input) {
DCHECK_EQ(instructions[0], Instr::kInput);
instructions += 1;
*machine_type = machine_types[0];
machine_types += 1;
*input = inputs[0];
inputs += 1;
}
void ConsumeUnusedRegister() {
DCHECK_EQ(instructions[0], Instr::kUnusedRegister);
instructions += 1;
}
void ConsumeDematerializedObject(uint32_t* id, uint32_t* field_count) {
DCHECK_EQ(instructions[0], Instr::kDematerializedObject);
instructions += 1;
*id = int_operands[0];
*field_count = int_operands[1];
int_operands += 2;
}
void ConsumeDematerializedObjectReference(uint32_t* id) {
DCHECK_EQ(instructions[0], Instr::kDematerializedObjectReference);
instructions += 1;
*id = int_operands[0];
int_operands += 1;
}
};
Iterator iterator(base::Vector<const OpIndex> state_values) const {
return Iterator{instructions, machine_types, int_operands, state_values};
}
const FrameStateInfo& frame_state_info;
base::Vector<Instr> instructions;
base::Vector<MachineType> machine_types;
base::Vector<uint32_t> int_operands;
};
} // namespace v8::internal::compiler::turboshaft
#endif // V8_COMPILER_TURBOSHAFT_DEOPT_DATA_H_
This diff is collapsed.
// 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.
#ifndef V8_COMPILER_TURBOSHAFT_GRAPH_BUILDER_H_
#define V8_COMPILER_TURBOSHAFT_GRAPH_BUILDER_H_
#include "src/compiler/turboshaft/graph.h"
namespace v8::internal::compiler {
class Schedule;
}
namespace v8::internal::compiler::turboshaft {
void BuildGraph(Schedule* schedule, Zone* graph_zone, Zone* phase_zone,
Graph* graph);
}
#endif // V8_COMPILER_TURBOSHAFT_GRAPH_BUILDER_H_
// 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/graph.h"
#include <iomanip>
namespace v8::internal::compiler::turboshaft {
std::ostream& operator<<(std::ostream& os, PrintAsBlockHeader block_header) {
const Block& block = block_header.block;
const char* block_type =
block.IsLoop() ? "LOOP" : block.IsMerge() ? "MERGE" : "BLOCK";
os << "\n" << block_type << " " << block.index();
if (block.IsDeferred()) os << " (deferred)";
if (!block.Predecessors().empty()) {
os << " <- ";
bool first = true;
for (const Block* pred : block.Predecessors()) {
if (!first) os << ", ";
os << pred->index();
first = false;
}
}
return os;
}
std::ostream& operator<<(std::ostream& os, const Graph& graph) {
for (const Block& block : graph.blocks()) {
os << PrintAsBlockHeader{block} << "\n";
for (const Operation& op : graph.operations(block)) {
os << std::setw(5) << graph.Index(op).id() << ": " << op << "\n";
}
}
return os;
}
} // namespace v8::internal::compiler::turboshaft
This diff is collapsed.
// 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/operations.h"
#include <atomic>
#include <sstream>
#include "src/base/platform/platform.h"
#include "src/common/assert-scope.h"
#include "src/compiler/frame-states.h"
#include "src/compiler/turboshaft/deopt-data.h"
#include "src/compiler/turboshaft/graph.h"
#include "src/handles/handles-inl.h"
namespace v8::internal::compiler::turboshaft {
const char* OpcodeName(Opcode opcode) {
#define OPCODE_NAME(Name) #Name,
const char* table[kNumberOfOpcodes] = {
TURBOSHAFT_OPERATION_LIST(OPCODE_NAME)};
#undef OPCODE_NAME
return table[OpcodeIndex(opcode)];
}
std::ostream& operator<<(std::ostream& os, OperationPrintStyle styled_op) {
const Operation& op = styled_op.op;
os << OpcodeName(op.opcode) << "(";
bool first = true;
for (OpIndex input : op.inputs()) {
if (!first) os << ", ";
first = false;
os << styled_op.op_index_prefix << input.id();
}
os << ")";
switch (op.opcode) {
#define SWITCH_CASE(Name) \
case Opcode::k##Name: \
op.Cast<Name##Op>().PrintOptions(os); \
break;
TURBOSHAFT_OPERATION_LIST(SWITCH_CASE)
#undef SWITCH_CASE
}
return os;
}
std::ostream& operator<<(std::ostream& os, FloatUnaryOp::Kind kind) {
switch (kind) {
case FloatUnaryOp::Kind::kAbs:
return os << "Abs";
case FloatUnaryOp::Kind::kNegate:
return os << "Negate";
case FloatUnaryOp::Kind::kSilenceNaN:
return os << "SilenceNaN";
}
}
std::ostream& operator<<(std::ostream& os, ShiftOp::Kind kind) {
switch (kind) {
case ShiftOp::Kind::kShiftRightArithmeticShiftOutZeros:
return os << "ShiftRightArithmeticShiftOutZeros";
case ShiftOp::Kind::kShiftRightArithmetic:
return os << "ShiftRightArithmetic";
case ShiftOp::Kind::kShiftRightLogical:
return os << "ShiftRightLogical";
case ShiftOp::Kind::kShiftLeft:
return os << "ShiftLeft";
}
}
std::ostream& operator<<(std::ostream& os, ComparisonOp::Kind kind) {
switch (kind) {
case ComparisonOp::Kind::kSignedLessThan:
return os << "SignedLessThan";
case ComparisonOp::Kind::kSignedLessThanOrEqual:
return os << "SignedLessThanOrEqual";
case ComparisonOp::Kind::kUnsignedLessThan:
return os << "UnsignedLessThan";
case ComparisonOp::Kind::kUnsignedLessThanOrEqual:
return os << "UnsignedLessThanOrEqual";
}
}
std::ostream& operator<<(std::ostream& os, ChangeOp::Kind kind) {
switch (kind) {
case ChangeOp::Kind::kSignedNarrowing:
return os << "SignedNarrowing";
case ChangeOp::Kind::kUnsignedNarrowing:
return os << "UnsignedNarrowing";
case ChangeOp::Kind::kIntegerTruncate:
return os << "IntegerTruncate";
case ChangeOp::Kind::kSignedFloatTruncate:
return os << "SignedFloatTruncate";
case ChangeOp::Kind::kUnsignedFloatTruncate:
return os << "UnsignedFloatTruncate";
case ChangeOp::Kind::kSignedFloatTruncateOverflowToMin:
return os << "SignedFloatTruncateOverflowToMin";
case ChangeOp::Kind::kSignedToFloat:
return os << "SignedToFloat";
case ChangeOp::Kind::kUnsignedToFloat:
return os << "UnsignedToFloat";
case ChangeOp::Kind::kExtractHighHalf:
return os << "ExtractHighHalf";
case ChangeOp::Kind::kExtractLowHalf:
return os << "ExtractLowHalf";
case ChangeOp::Kind::kZeroExtend:
return os << "ZeroExtend";
case ChangeOp::Kind::kSignExtend:
return os << "SignExtend";
case ChangeOp::Kind::kBitcast:
return os << "Bitcast";
}
}
std::ostream& operator<<(std::ostream& os, ProjectionOp::Kind kind) {
switch (kind) {
case ProjectionOp::Kind::kOverflowBit:
return os << "overflow bit";
case ProjectionOp::Kind::kResult:
return os << "result";
}
}
void PendingLoopPhiOp::PrintOptions(std::ostream& os) const {
os << "[" << rep << ", #o" << old_backedge_index.id() << "]";
}
void ConstantOp::PrintOptions(std::ostream& os) const {
os << "[";
switch (kind) {
case Kind::kWord32:
os << "word32: " << static_cast<int32_t>(storage.integral);
break;
case Kind::kWord64:
os << "word64: " << static_cast<int64_t>(storage.integral);
break;
case Kind::kNumber:
os << "number: " << number();
break;
case Kind::kTaggedIndex:
os << "tagged index: " << tagged_index();
break;
case Kind::kFloat64:
os << "float64: " << float64();
break;
case Kind::kFloat32:
os << "float32: " << float32();
break;
case Kind::kExternal:
os << "external: " << external_reference();
break;
case Kind::kHeapObject:
os << "heap object: " << handle();
break;
case Kind::kCompressedHeapObject:
os << "compressed heap object: " << handle();
break;
case Kind::kDelayedString:
os << delayed_string();
break;
}
os << "]";
}
void LoadOp::PrintOptions(std::ostream& os) const {
os << "[";
switch (kind) {
case Kind::kRaw:
os << "raw";
break;
case Kind::kOnHeap:
os << "on heap";
break;
}
os << ", " << loaded_rep;
if (offset != 0) os << ", offset: " << offset;
os << "]";
}
void ParameterOp::PrintOptions(std::ostream& os) const {
os << "[" << parameter_index;
if (debug_name) os << ", " << debug_name;
os << "]";
}
void IndexedLoadOp::PrintOptions(std::ostream& os) const {
os << "[";
switch (kind) {
case Kind::kRaw:
os << "raw";
break;
case Kind::kOnHeap:
os << "on heap";
break;
}
os << ", " << loaded_rep;
if (element_size_log2 != 0)
os << ", element size: 2^" << int{element_size_log2};
if (offset != 0) os << ", offset: " << offset;
os << "]";
}
void StoreOp::PrintOptions(std::ostream& os) const {
os << "[";
switch (kind) {
case Kind::kRaw:
os << "raw";
break;
case Kind::kOnHeap:
os << "on heap";
break;
}
os << ", " << stored_rep;
os << ", " << write_barrier;
if (offset != 0) os << ", offset: " << offset;
os << "]";
}
void IndexedStoreOp::PrintOptions(std::ostream& os) const {
os << "[";
switch (kind) {
case Kind::kRaw:
os << "raw";
break;
case Kind::kOnHeap:
os << "on heap";
break;
}
os << ", " << stored_rep;
os << ", " << write_barrier;
if (element_size_log2 != 0)
os << ", element size: 2^" << int{element_size_log2};
if (offset != 0) os << ", offset: " << offset;
os << "]";
}
void FrameStateOp::PrintOptions(std::ostream& os) const {
os << "[";
os << (inlined ? "inlined" : "not inlined");
os << ", ";
os << data->frame_state_info;
os << ", state values:";
FrameStateData::Iterator it = data->iterator(state_values());
while (it.has_more()) {
os << " ";
switch (it.current_instr()) {
case FrameStateData::Instr::kInput: {
MachineType type;
OpIndex input;
it.ConsumeInput(&type, &input);
os << "#" << input.id() << "(" << type << ")";
break;
}
case FrameStateData::Instr::kUnusedRegister:
it.ConsumeUnusedRegister();
os << ".";
break;
case FrameStateData::Instr::kDematerializedObject: {
uint32_t id;
uint32_t field_count;
it.ConsumeDematerializedObject(&id, &field_count);
os << "$" << id << "(field count: " << field_count << ")";
break;
}
case FrameStateData::Instr::kDematerializedObjectReference: {
uint32_t id;
it.ConsumeDematerializedObjectReference(&id);
os << "$" << id;
break;
}
}
}
os << "]";
}
void BinopOp::PrintOptions(std::ostream& os) const {
os << "[";
switch (kind) {
case Kind::kAdd:
os << "add, ";
break;
case Kind::kSub:
os << "sub, ";
break;
case Kind::kMul:
os << "signed mul, ";
break;
case Kind::kBitwiseAnd:
os << "bitwise and, ";
break;
case Kind::kBitwiseOr:
os << "bitwise or, ";
break;
case Kind::kBitwiseXor:
os << "bitwise xor, ";
break;
}
os << rep;
os << "]";
}
void OverflowCheckedBinopOp::PrintOptions(std::ostream& os) const {
os << "[";
switch (kind) {
case Kind::kSignedAdd:
os << "signed add, ";
break;
case Kind::kSignedSub:
os << "signed sub, ";
break;
case Kind::kSignedMul:
os << "signed mul, ";
break;
}
os << rep;
os << "]";
}
std::ostream& operator<<(std::ostream& os, BlockIndex b) {
if (!b.valid()) {
return os << "<invalid block>";
}
return os << 'B' << b.id();
}
std::ostream& operator<<(std::ostream& os, const Block* b) {
return os << b->index();
}
void SwitchOp::PrintOptions(std::ostream& os) const {
os << "[";
for (const Case& c : cases) {
os << "case " << c.value << ": " << c.destination << ", ";
}
os << " default: " << default_case << "]";
}
std::string Operation::ToString() const {
std::stringstream ss;
ss << *this;
return ss.str();
}
} // namespace v8::internal::compiler::turboshaft
This diff is collapsed.
This diff is collapsed.
// 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.
#ifndef V8_COMPILER_TURBOSHAFT_RECREATE_SCHEDULE_H_
#define V8_COMPILER_TURBOSHAFT_RECREATE_SCHEDULE_H_
namespace v8::internal {
class Zone;
}
namespace v8::internal::compiler {
class Schedule;
class Graph;
class CallDescriptor;
} // namespace v8::internal::compiler
namespace v8::internal::compiler::turboshaft {
class Graph;
struct RecreateScheduleResult {
compiler::Graph* graph;
Schedule* schedule;
};
RecreateScheduleResult RecreateSchedule(const Graph& graph,
CallDescriptor* call_descriptor,
Zone* graph_zone, Zone* phase_zone);
} // namespace v8::internal::compiler::turboshaft
#endif // V8_COMPILER_TURBOSHAFT_RECREATE_SCHEDULE_H_
......@@ -946,6 +946,8 @@ DEFINE_FLOAT(script_delay_fraction, 0.0,
"busy wait after each Script::Run by the given fraction of the "
"run's duration")
DEFINE_BOOL(turboshaft, false, "enable TurboFan's TurboShaft phases")
// Favor memory over execution speed.
DEFINE_BOOL(optimize_for_size, false,
"Enables optimizations which favor memory size over execution "
......
......@@ -368,6 +368,8 @@ class RuntimeCallTimer final {
ADD_THREAD_SPECIFIC_COUNTER(V, Optimize, SimplifiedLowering) \
ADD_THREAD_SPECIFIC_COUNTER(V, Optimize, StoreStoreElimination) \
ADD_THREAD_SPECIFIC_COUNTER(V, Optimize, TraceScheduleAndVerify) \
ADD_THREAD_SPECIFIC_COUNTER(V, Optimize, BuildTurboShaft) \
ADD_THREAD_SPECIFIC_COUNTER(V, Optimize, TurboshaftRecreateSchedule) \
ADD_THREAD_SPECIFIC_COUNTER(V, Optimize, TypeAssertions) \
ADD_THREAD_SPECIFIC_COUNTER(V, Optimize, TypedLowering) \
ADD_THREAD_SPECIFIC_COUNTER(V, Optimize, Typer) \
......
......@@ -7,6 +7,7 @@
#include <deque>
#include <forward_list>
#include <initializer_list>
#include <list>
#include <map>
#include <queue>
......@@ -46,6 +47,11 @@ class ZoneVector : public std::vector<T, ZoneAllocator<T>> {
ZoneVector(std::initializer_list<T> list, Zone* zone)
: std::vector<T, ZoneAllocator<T>>(list, ZoneAllocator<T>(zone)) {}
ZoneVector& operator=(std::initializer_list<T> ilist) {
std::vector<T, ZoneAllocator<T>>::operator=(ilist);
return *this;
}
// Constructs a new vector and fills it with the contents of the range
// [first, last).
template <class InputIt>
......
......@@ -6,8 +6,11 @@
#define V8_ZONE_ZONE_H_
#include <limits>
#include <memory>
#include <type_traits>
#include "src/base/logging.h"
#include "src/base/vector.h"
#include "src/common/globals.h"
#include "src/utils/utils.h"
#include "src/zone/accounting-allocator.h"
......@@ -121,6 +124,20 @@ class V8_EXPORT_PRIVATE Zone final {
return static_cast<T*>(Allocate<TypeTag>(length * sizeof(T)));
}
template <typename T, typename TypeTag = T[]>
base::Vector<T> NewVector(size_t length, T value) {
T* new_array = NewArray<T, TypeTag>(length);
std::uninitialized_fill_n(new_array, length, value);
return {new_array, length};
}
template <typename T, typename TypeTag = std::remove_const_t<T>[]>
base::Vector<std::remove_const_t<T>> CloneVector(base::Vector<T> v) {
auto* new_array = NewArray<std::remove_const_t<T>, TypeTag>(v.size());
std::uninitialized_copy(v.begin(), v.end(), new_array);
return {new_array, v.size()};
}
// Return array of 'length' elements back to Zone. These bytes can be reused
// for following allocations.
//
......
......@@ -269,6 +269,11 @@
# Needs deterministic test helpers for concurrent maglev tiering.
# TODO(jgruber,v8:7700): Implement ASAP.
'maglev/18': [SKIP],
# Stress variants cause operators that are currently still unsupported by
# TurboShaft.
# TODO(v8:12783)
'turboshaft/simple': [PASS, NO_VARIANTS],
}], # ALWAYS
##############################################################################
......
// 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.
//
// Flags: --turboshaft --allow-natives-syntax
function f(x) {
return x.a + x.b;
}
%PrepareFunctionForOptimization(f);
assertEquals(5, f({a: 2, b: 3}));
assertEquals(7, f({a: 2, b: 5}));
%OptimizeFunctionOnNextCall(f);
assertEquals(5, f({a: 2, b: 3}));
assertEquals(7, f({a: 2, b: 5}));
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