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_ |