| Index: src/compiler/graph-assembler.h
|
| diff --git a/src/compiler/graph-assembler.h b/src/compiler/graph-assembler.h
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..b7a198fcabcdaa6fe3a0b85ffe5a4c195697be72
|
| --- /dev/null
|
| +++ b/src/compiler/graph-assembler.h
|
| @@ -0,0 +1,426 @@
|
| +// 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.
|
| +
|
| +#ifndef V8_COMPILER_GRAPH_ASSEMBLER_H_
|
| +#define V8_COMPILER_GRAPH_ASSEMBLER_H_
|
| +
|
| +#include "src/compiler/js-graph.h"
|
| +#include "src/compiler/node.h"
|
| +#include "src/compiler/simplified-operator.h"
|
| +
|
| +namespace v8 {
|
| +namespace internal {
|
| +
|
| +class JSGraph;
|
| +class Graph;
|
| +
|
| +namespace compiler {
|
| +
|
| +#define PURE_ASSEMBLER_MACH_UNOP_LIST(V) \
|
| + V(ChangeInt32ToInt64) \
|
| + V(ChangeInt32ToFloat64) \
|
| + V(ChangeUint32ToFloat64) \
|
| + V(ChangeUint32ToUint64) \
|
| + V(ChangeFloat64ToInt32) \
|
| + V(ChangeFloat64ToUint32) \
|
| + V(TruncateInt64ToInt32) \
|
| + V(RoundFloat64ToInt32) \
|
| + V(TruncateFloat64ToWord32) \
|
| + V(Float64ExtractHighWord32) \
|
| + V(Float64Abs)
|
| +
|
| +#define PURE_ASSEMBLER_MACH_BINOP_LIST(V) \
|
| + V(WordShl) \
|
| + V(WordSar) \
|
| + V(WordAnd) \
|
| + V(Word32Or) \
|
| + V(Word32And) \
|
| + V(Word32Shr) \
|
| + V(Word32Shl) \
|
| + V(Int32Add) \
|
| + V(Int32Sub) \
|
| + V(Int32Mul) \
|
| + V(Int32LessThanOrEqual) \
|
| + V(Uint32LessThanOrEqual) \
|
| + V(Uint32LessThan) \
|
| + V(Int32LessThan) \
|
| + V(Float64Add) \
|
| + V(Float64Sub) \
|
| + V(Float64Mod) \
|
| + V(Float64Equal) \
|
| + V(Float64LessThan) \
|
| + V(Float64LessThanOrEqual) \
|
| + V(Word32Equal) \
|
| + V(WordEqual)
|
| +
|
| +#define CHECKED_ASSEMBLER_MACH_BINOP_LIST(V) \
|
| + V(Int32AddWithOverflow) \
|
| + V(Int32SubWithOverflow) \
|
| + V(Int32MulWithOverflow) \
|
| + V(Int32Mod) \
|
| + V(Int32Div) \
|
| + V(Uint32Mod) \
|
| + V(Uint32Div)
|
| +
|
| +class GraphAssembler;
|
| +
|
| +enum class GraphAssemblerLabelType { kDeferred, kNonDeferred };
|
| +
|
| +// Label with statically known count of incoming branches and phis.
|
| +template <size_t MergeCount, size_t VarCount = 0u>
|
| +class GraphAssemblerStaticLabel {
|
| + public:
|
| + Node* PhiAt(size_t index);
|
| +
|
| + template <typename... Reps>
|
| + explicit GraphAssemblerStaticLabel(GraphAssemblerLabelType is_deferred,
|
| + Reps... reps)
|
| + : is_deferred_(is_deferred == GraphAssemblerLabelType::kDeferred) {
|
| + STATIC_ASSERT(VarCount == sizeof...(reps));
|
| + MachineRepresentation reps_array[] = {MachineRepresentation::kNone,
|
| + reps...};
|
| + for (size_t i = 0; i < VarCount; i++) {
|
| + representations_[i] = reps_array[i + 1];
|
| + }
|
| + }
|
| +
|
| + ~GraphAssemblerStaticLabel() { DCHECK(IsBound() || MergedCount() == 0); }
|
| +
|
| + private:
|
| + friend class GraphAssembler;
|
| +
|
| + void SetBound() {
|
| + DCHECK(!IsBound());
|
| + DCHECK_EQ(merged_count_, MergeCount);
|
| + is_bound_ = true;
|
| + }
|
| + bool IsBound() const { return is_bound_; }
|
| +
|
| + size_t PhiCount() const { return VarCount; }
|
| + size_t MaxMergeCount() const { return MergeCount; }
|
| + size_t MergedCount() const { return merged_count_; }
|
| + bool IsDeferred() const { return is_deferred_; }
|
| +
|
| + // For each phi, the buffer must have at least MaxMergeCount() + 1
|
| + // node entries.
|
| + Node** GetBindingsPtrFor(size_t phi_index) {
|
| + DCHECK_LT(phi_index, PhiCount());
|
| + return &bindings_[phi_index * (MergeCount + 1)];
|
| + }
|
| + void SetBinding(size_t phi_index, size_t merge_index, Node* binding) {
|
| + DCHECK_LT(phi_index, PhiCount());
|
| + DCHECK_LT(merge_index, MergeCount);
|
| + bindings_[phi_index * (MergeCount + 1) + merge_index] = binding;
|
| + }
|
| + MachineRepresentation GetRepresentationFor(size_t phi_index) {
|
| + DCHECK_LT(phi_index, PhiCount());
|
| + return representations_[phi_index];
|
| + }
|
| + // The controls buffer must have at least MaxMergeCount() entries.
|
| + Node** GetControlsPtr() { return controls_; }
|
| + // The effects buffer must have at least MaxMergeCount() + 1 entries.
|
| + Node** GetEffectsPtr() { return effects_; }
|
| + void IncrementMergedCount() { merged_count_++; }
|
| +
|
| + bool is_bound_ = false;
|
| + bool is_deferred_;
|
| + size_t merged_count_ = 0;
|
| + Node* effects_[MergeCount + 1]; // Extra element for control edge,
|
| + // so that we can use the array to
|
| + // construct EffectPhi.
|
| + Node* controls_[MergeCount];
|
| + Node* bindings_[(MergeCount + 1) * VarCount + 1];
|
| + MachineRepresentation representations_[VarCount + 1];
|
| +};
|
| +
|
| +// General label (with zone allocated buffers for incoming branches and phi
|
| +// inputs).
|
| +class GraphAssemblerLabel {
|
| + public:
|
| + Node* PhiAt(size_t index);
|
| +
|
| + GraphAssemblerLabel(GraphAssemblerLabelType is_deferred, size_t merge_count,
|
| + size_t var_count, MachineRepresentation* representations,
|
| + Zone* zone);
|
| +
|
| + ~GraphAssemblerLabel();
|
| +
|
| + private:
|
| + friend class GraphAssembler;
|
| +
|
| + void SetBound() {
|
| + DCHECK(!is_bound_);
|
| + is_bound_ = true;
|
| + }
|
| + bool IsBound() const { return is_bound_; }
|
| + size_t PhiCount() const { return var_count_; }
|
| + size_t MaxMergeCount() const { return max_merge_count_; }
|
| + size_t MergedCount() const { return merged_count_; }
|
| + bool IsDeferred() const { return is_deferred_; }
|
| +
|
| + // For each phi, the buffer must have at least MaxMergeCount() + 1
|
| + // node entries.
|
| + Node** GetBindingsPtrFor(size_t phi_index);
|
| + void SetBinding(size_t phi_index, size_t merge_index, Node* binding);
|
| + MachineRepresentation GetRepresentationFor(size_t phi_index);
|
| + // The controls buffer must have at least MaxMergeCount() entries.
|
| + Node** GetControlsPtr();
|
| + // The effects buffer must have at least MaxMergeCount() + 1 entries.
|
| + Node** GetEffectsPtr();
|
| + void IncrementMergedCount() { merged_count_++; }
|
| +
|
| + bool is_bound_ = false;
|
| + bool is_deferred_;
|
| + size_t merged_count_ = 0;
|
| + size_t max_merge_count_;
|
| + size_t var_count_;
|
| + Node** effects_ = nullptr;
|
| + Node** controls_ = nullptr;
|
| + Node** bindings_ = nullptr;
|
| + MachineRepresentation* representations_ = nullptr;
|
| +};
|
| +
|
| +class GraphAssembler {
|
| + public:
|
| + GraphAssembler(JSGraph* jsgraph, Node* effect, Node* control, Zone* zone);
|
| +
|
| + void Reset(Node* effect, Node* control);
|
| +
|
| + // Create non-deferred label with statically known number of incoming
|
| + // gotos/branches.
|
| + template <size_t MergeCount, typename... Reps>
|
| + static GraphAssemblerStaticLabel<MergeCount, sizeof...(Reps)> MakeLabel(
|
| + Reps... reps) {
|
| + return GraphAssemblerStaticLabel<MergeCount, sizeof...(Reps)>(
|
| + GraphAssemblerLabelType::kNonDeferred, reps...);
|
| + }
|
| +
|
| + // Create deferred label with statically known number of incoming
|
| + // gotos/branches.
|
| + template <size_t MergeCount, typename... Reps>
|
| + static GraphAssemblerStaticLabel<MergeCount, sizeof...(Reps)>
|
| + MakeDeferredLabel(Reps... reps) {
|
| + return GraphAssemblerStaticLabel<MergeCount, sizeof...(Reps)>(
|
| + GraphAssemblerLabelType::kDeferred, reps...);
|
| + }
|
| +
|
| + // Create label with number of incoming branches supplied at runtime.
|
| + template <typename... Reps>
|
| + GraphAssemblerLabel MakeLabelFor(GraphAssemblerLabelType is_deferred,
|
| + size_t merge_count, Reps... reps) {
|
| + MachineRepresentation reps_array[] = {MachineRepresentation::kNone,
|
| + reps...};
|
| + return GraphAssemblerLabel(is_deferred, merge_count, sizeof...(reps),
|
| + &(reps_array[1]), temp_zone());
|
| + }
|
| +
|
| + // Value creation.
|
| + Node* TrueConstant();
|
| + Node* FalseConstant();
|
| + Node* HeapNumberMapConstant();
|
| + Node* IntPtrConstant(intptr_t value);
|
| + Node* Uint32Constant(int32_t value);
|
| + Node* Int32Constant(int32_t value);
|
| + Node* SmiConstant(int32_t value);
|
| + Node* Float64Constant(double value);
|
| + Node* Projection(int index, Node* value);
|
| + Node* HeapConstant(Handle<HeapObject> object);
|
| + Node* NoContextConstant();
|
| + Node* CEntryStubConstant(int result_size);
|
| + Node* ExternalConstant(ExternalReference ref);
|
| + Node* EmptyStringConstant();
|
| + Node* UndefinedConstant();
|
| + Node* TheHoleConstant();
|
| + Node* FixedArrayMapConstant();
|
| +
|
| +#define PURE_UNOP_DECL(Name) Node* Name(Node* input);
|
| + PURE_ASSEMBLER_MACH_UNOP_LIST(PURE_UNOP_DECL)
|
| +#undef PURE_UNOP_DECL
|
| +
|
| +#define BINOP_DECL(Name) Node* Name(Node* left, Node* right);
|
| + PURE_ASSEMBLER_MACH_BINOP_LIST(BINOP_DECL)
|
| + CHECKED_ASSEMBLER_MACH_BINOP_LIST(BINOP_DECL)
|
| +#undef BINOP_DECL
|
| +
|
| + Node* Float64RoundDown(Node* value);
|
| +
|
| + Node* ToNumber(Node* value);
|
| + Node* Allocate(PretenureFlag pretenure, Node* size);
|
| + Node* LoadField(FieldAccess const&, Node* object);
|
| + Node* LoadElement(ElementAccess const&, Node* object, Node* index);
|
| + Node* StoreField(FieldAccess const&, Node* object, Node* value);
|
| + Node* StoreElement(ElementAccess const&, Node* object, Node* index,
|
| + Node* value);
|
| +
|
| + Node* Store(StoreRepresentation rep, Node* object, Node* offset, Node* value);
|
| +
|
| + Node* Retain(Node* buffer);
|
| + Node* UnsafePointerAdd(Node* base, Node* external);
|
| +
|
| + Node* DeoptimizeIf(DeoptimizeReason reason, Node* condition,
|
| + Node* frame_state);
|
| + Node* DeoptimizeUnless(DeoptimizeReason reason, Node* condition,
|
| + Node* frame_state);
|
| + template <typename... Args>
|
| + Node* Call(const CallDescriptor* desc, Args... args);
|
| +
|
| + // Basic control operations.
|
| + template <class LabelType>
|
| + void Bind(LabelType* label);
|
| +
|
| + template <class LabelType, typename... vars>
|
| + void Goto(LabelType* label, vars...);
|
| +
|
| + void Branch(Node* condition, GraphAssemblerStaticLabel<1>* if_true,
|
| + GraphAssemblerStaticLabel<1>* if_false);
|
| +
|
| + // Control helpers.
|
| + // {GotoIf(c, l)} is equivalent to {Branch(c, l, templ);Bind(templ)}.
|
| + template <class LabelType, typename... vars>
|
| + void GotoIf(Node* condition, LabelType* label, vars...);
|
| +
|
| + // {GotoUnless(c, l)} is equivalent to {Branch(c, templ, l);Bind(templ)}.
|
| + template <class LabelType, typename... vars>
|
| + void GotoUnless(Node* condition, LabelType* label, vars...);
|
| +
|
| + // Extractors (should be only used when destructing/resetting the assembler).
|
| + Node* ExtractCurrentControl();
|
| + Node* ExtractCurrentEffect();
|
| +
|
| + private:
|
| + template <class LabelType, typename... Vars>
|
| + void MergeState(LabelType label, Vars... vars);
|
| +
|
| + Operator const* ToNumberOperator();
|
| +
|
| + JSGraph* jsgraph() const { return jsgraph_; }
|
| + Graph* graph() const { return jsgraph_->graph(); }
|
| + Zone* temp_zone() const { return temp_zone_; }
|
| + CommonOperatorBuilder* common() const { return jsgraph()->common(); }
|
| + MachineOperatorBuilder* machine() const { return jsgraph()->machine(); }
|
| + SimplifiedOperatorBuilder* simplified() const {
|
| + return jsgraph()->simplified();
|
| + }
|
| +
|
| + SetOncePointer<Operator const> to_number_operator_;
|
| + Zone* temp_zone_;
|
| + JSGraph* jsgraph_;
|
| + Node* current_effect_;
|
| + Node* current_control_;
|
| +};
|
| +
|
| +template <size_t MergeCount, size_t VarCount>
|
| +Node* GraphAssemblerStaticLabel<MergeCount, VarCount>::PhiAt(size_t index) {
|
| + DCHECK(IsBound());
|
| + return GetBindingsPtrFor(index)[0];
|
| +}
|
| +
|
| +template <class LabelType, typename... Vars>
|
| +void GraphAssembler::MergeState(LabelType label, Vars... vars) {
|
| + DCHECK(!label->IsBound());
|
| + size_t merged_count = label->MergedCount();
|
| + DCHECK_LT(merged_count, label->MaxMergeCount());
|
| + DCHECK_EQ(label->PhiCount(), sizeof...(vars));
|
| + label->GetEffectsPtr()[merged_count] = current_effect_;
|
| + label->GetControlsPtr()[merged_count] = current_control_;
|
| + // We need to start with nullptr to avoid 0-length arrays.
|
| + Node* var_array[] = {nullptr, vars...};
|
| + for (size_t i = 0; i < sizeof...(vars); i++) {
|
| + label->SetBinding(i, merged_count, var_array[i + 1]);
|
| + }
|
| + label->IncrementMergedCount();
|
| +}
|
| +
|
| +template <class LabelType>
|
| +void GraphAssembler::Bind(LabelType* label) {
|
| + DCHECK(current_control_ == nullptr);
|
| + DCHECK(current_effect_ == nullptr);
|
| + DCHECK(label->MaxMergeCount() > 0);
|
| + DCHECK_EQ(label->MaxMergeCount(), label->MergedCount());
|
| +
|
| + int merge_count = static_cast<int>(label->MaxMergeCount());
|
| + if (merge_count == 1) {
|
| + current_control_ = label->GetControlsPtr()[0];
|
| + current_effect_ = label->GetEffectsPtr()[0];
|
| + label->SetBound();
|
| + return;
|
| + }
|
| +
|
| + current_control_ = graph()->NewNode(common()->Merge(merge_count), merge_count,
|
| + label->GetControlsPtr());
|
| +
|
| + Node** effects = label->GetEffectsPtr();
|
| + current_effect_ = effects[0];
|
| + for (size_t i = 1; i < label->MaxMergeCount(); i++) {
|
| + if (current_effect_ != effects[i]) {
|
| + effects[label->MaxMergeCount()] = current_control_;
|
| + current_effect_ = graph()->NewNode(common()->EffectPhi(merge_count),
|
| + merge_count + 1, effects);
|
| + break;
|
| + }
|
| + }
|
| +
|
| + for (size_t var = 0; var < label->PhiCount(); var++) {
|
| + Node** bindings = label->GetBindingsPtrFor(var);
|
| + bindings[label->MaxMergeCount()] = current_control_;
|
| + bindings[0] = graph()->NewNode(
|
| + common()->Phi(label->GetRepresentationFor(var), merge_count),
|
| + merge_count + 1, bindings);
|
| + }
|
| +
|
| + label->SetBound();
|
| +}
|
| +
|
| +template <class LabelType, typename... Vars>
|
| +void GraphAssembler::Goto(LabelType* label, Vars... vars) {
|
| + DCHECK_NOT_NULL(current_control_);
|
| + DCHECK_NOT_NULL(current_effect_);
|
| + MergeState(label, vars...);
|
| + current_control_ = nullptr;
|
| + current_effect_ = nullptr;
|
| +}
|
| +
|
| +template <class LabelType, typename... Vars>
|
| +void GraphAssembler::GotoIf(Node* condition, LabelType* label, Vars... vars) {
|
| + BranchHint hint =
|
| + label->IsDeferred() ? BranchHint::kFalse : BranchHint::kNone;
|
| + Node* branch =
|
| + graph()->NewNode(common()->Branch(hint), condition, current_control_);
|
| +
|
| + current_control_ = graph()->NewNode(common()->IfTrue(), branch);
|
| + MergeState(label, vars...);
|
| +
|
| + current_control_ = graph()->NewNode(common()->IfFalse(), branch);
|
| +}
|
| +
|
| +template <class LabelType, typename... Vars>
|
| +void GraphAssembler::GotoUnless(Node* condition, LabelType* label,
|
| + Vars... vars) {
|
| + BranchHint hint = label->IsDeferred() ? BranchHint::kTrue : BranchHint::kNone;
|
| + Node* branch =
|
| + graph()->NewNode(common()->Branch(hint), condition, current_control_);
|
| +
|
| + current_control_ = graph()->NewNode(common()->IfFalse(), branch);
|
| + MergeState(label, vars...);
|
| +
|
| + current_control_ = graph()->NewNode(common()->IfTrue(), branch);
|
| +}
|
| +
|
| +template <typename... Args>
|
| +Node* GraphAssembler::Call(const CallDescriptor* desc, Args... args) {
|
| + const Operator* op = common()->Call(desc);
|
| + Node* args_array[] = {args..., current_effect_, current_control_};
|
| + int size = static_cast<int>(sizeof...(args)) + op->EffectInputCount() +
|
| + op->ControlInputCount();
|
| + Node* call = graph()->NewNode(op, size, args_array);
|
| + DCHECK_EQ(0, op->ControlOutputCount());
|
| + current_effect_ = call;
|
| + return call;
|
| +}
|
| +
|
| +} // namespace compiler
|
| +} // namespace internal
|
| +} // namespace v8
|
| +
|
| +#endif // V8_COMPILER_GRAPH_ASSEMBLER_H_
|
|
|