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