// 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) \ V(BitcastWordToTagged) #define PURE_ASSEMBLER_MACH_BINOP_LIST(V) \ V(WordShl) \ V(WordSar) \ V(WordAnd) \ V(Word32Or) \ V(Word32And) \ V(Word32Shr) \ V(Word32Shl) \ V(IntAdd) \ V(IntSub) \ V(UintLessThan) \ 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) #define JSGRAPH_SINGLETON_CONSTANT_LIST(V) \ V(TrueConstant) \ V(FalseConstant) \ V(HeapNumberMapConstant) \ V(NoContextConstant) \ V(EmptyStringConstant) \ V(UndefinedConstant) \ V(TheHoleConstant) \ V(FixedArrayMapConstant) \ V(ToNumberBuiltinConstant) \ V(AllocateInNewSpaceStubConstant) \ V(AllocateInOldSpaceStubConstant) 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* IntPtrConstant(intptr_t value); Node* Uint32Constant(int32_t value); Node* Int32Constant(int32_t value); Node* UniqueInt32Constant(int32_t value); Node* SmiConstant(int32_t value); Node* Float64Constant(double value); Node* Projection(int index, Node* value); Node* HeapConstant(Handle<HeapObject> object); Node* CEntryStubConstant(int result_size); Node* ExternalConstant(ExternalReference ref); #define SINGLETON_CONST_DECL(Name) Node* Name(); JSGRAPH_SINGLETON_CONSTANT_LIST(SINGLETON_CONST_DECL) #undef SINGLETON_CONST_DECL #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* Load(MachineType rep, Node* object, Node* offset); Node* Retain(Node* buffer); Node* UnsafePointerAdd(Node* base, Node* external); Node* DeoptimizeIf(DeoptimizeReason reason, Node* condition, Node* frame_state); Node* DeoptimizeUnless(DeoptimizeKind kind, 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); template <typename... Args> Node* Call(const Operator* op, 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); return Call(op, args...); } template <typename... Args> Node* GraphAssembler::Call(const Operator* op, Args... args) { DCHECK_EQ(IrOpcode::kCall, op->opcode()); 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_