| // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| #ifndef RUNTIME_VM_COMPILER_FRONTEND_KERNEL_TO_IL_H_ |
| #define RUNTIME_VM_COMPILER_FRONTEND_KERNEL_TO_IL_H_ |
| |
| #if !defined(DART_PRECOMPILED_RUNTIME) |
| |
| #include "vm/growable_array.h" |
| #include "vm/hash_map.h" |
| |
| #include "vm/compiler/backend/flow_graph.h" |
| #include "vm/compiler/backend/il.h" |
| #include "vm/compiler/frontend/base_flow_graph_builder.h" |
| #include "vm/compiler/frontend/kernel_translation_helper.h" |
| #include "vm/compiler/frontend/scope_builder.h" |
| |
| namespace dart { |
| |
| class InlineExitCollector; |
| |
| namespace kernel { |
| |
| class BaseFlowGraphBuilder; |
| class StreamingFlowGraphBuilder; |
| struct InferredTypeMetadata; |
| class BreakableBlock; |
| class CatchBlock; |
| class FlowGraphBuilder; |
| class SwitchBlock; |
| class TryFinallyBlock; |
| |
| struct YieldContinuation { |
| Instruction* entry; |
| intptr_t try_index; |
| |
| YieldContinuation(Instruction* entry, intptr_t try_index) |
| : entry(entry), try_index(try_index) {} |
| |
| YieldContinuation() |
| : entry(NULL), try_index(CatchClauseNode::kInvalidTryIndex) {} |
| }; |
| |
| class FlowGraphBuilder : public BaseFlowGraphBuilder { |
| public: |
| FlowGraphBuilder(ParsedFunction* parsed_function, |
| const ZoneGrowableArray<const ICData*>& ic_data_array, |
| ZoneGrowableArray<intptr_t>* context_level_array, |
| InlineExitCollector* exit_collector, |
| bool optimizing, |
| intptr_t osr_id, |
| intptr_t first_block_id = 1); |
| virtual ~FlowGraphBuilder(); |
| |
| FlowGraph* BuildGraph(); |
| |
| private: |
| BlockEntryInstr* BuildPrologue(TargetEntryInstr* normal_entry, |
| PrologueInfo* prologue_info); |
| |
| FlowGraph* BuildGraphOfMethodExtractor(const Function& method); |
| FlowGraph* BuildGraphOfNoSuchMethodDispatcher(const Function& function); |
| FlowGraph* BuildGraphOfInvokeFieldDispatcher(const Function& function); |
| |
| Fragment NativeFunctionBody(const Function& function, |
| LocalVariable* first_parameter); |
| |
| Fragment EnterScope(intptr_t kernel_offset, |
| intptr_t* num_context_variables = NULL); |
| Fragment ExitScope(intptr_t kernel_offset); |
| |
| Fragment AdjustContextTo(int depth); |
| |
| Fragment PushContext(int size); |
| Fragment PopContext(); |
| |
| Fragment LoadInstantiatorTypeArguments(); |
| Fragment LoadFunctionTypeArguments(); |
| Fragment InstantiateType(const AbstractType& type); |
| Fragment InstantiateTypeArguments(const TypeArguments& type_arguments); |
| Fragment TranslateInstantiatedTypeArguments( |
| const TypeArguments& type_arguments); |
| |
| Fragment AllocateContext(intptr_t size); |
| Fragment AllocateObject(TokenPosition position, |
| const Class& klass, |
| intptr_t argument_count); |
| Fragment AllocateObject(const Class& klass, const Function& closure_function); |
| Fragment BooleanNegate(); |
| Fragment CatchBlockEntry(const Array& handler_types, |
| intptr_t handler_index, |
| bool needs_stacktrace, |
| bool is_synthesized); |
| Fragment TryCatch(int try_handler_index); |
| Fragment CheckStackOverflowInPrologue(TokenPosition position); |
| Fragment CloneContext(intptr_t num_context_variables); |
| Fragment CreateArray(); |
| Fragment InstanceCall(TokenPosition position, |
| const String& name, |
| Token::Kind kind, |
| intptr_t type_args_len, |
| intptr_t argument_count, |
| const Array& argument_names, |
| intptr_t checked_argument_count, |
| const Function& interface_target, |
| const InferredTypeMetadata* result_type = NULL); |
| Fragment ClosureCall(intptr_t type_args_len, |
| intptr_t argument_count, |
| const Array& argument_names); |
| Fragment RethrowException(TokenPosition position, int catch_try_index); |
| Fragment LoadClassId(); |
| Fragment LoadField(intptr_t offset, intptr_t class_id = kDynamicCid); |
| Fragment LoadField(const Field& field); |
| Fragment LoadLocal(LocalVariable* variable); |
| Fragment InitStaticField(const Field& field); |
| Fragment LoadStaticField(); |
| Fragment NativeCall(const String* name, const Function* function); |
| Fragment Return(TokenPosition position); |
| Fragment CheckNull(TokenPosition position, |
| LocalVariable* receiver, |
| const String& function_name); |
| void SetResultTypeForStaticCall(StaticCallInstr* call, |
| const Function& target, |
| intptr_t argument_count, |
| const InferredTypeMetadata* result_type); |
| Fragment StaticCall(TokenPosition position, |
| const Function& target, |
| intptr_t argument_count, |
| ICData::RebindRule rebind_rule); |
| Fragment StaticCall(TokenPosition position, |
| const Function& target, |
| intptr_t argument_count, |
| const Array& argument_names, |
| ICData::RebindRule rebind_rule, |
| const InferredTypeMetadata* result_type = NULL, |
| intptr_t type_args_len = 0); |
| Fragment StoreIndexed(intptr_t class_id); |
| Fragment StoreInstanceFieldGuarded(const Field& field, |
| bool is_initialization_store); |
| Fragment StoreInstanceField( |
| TokenPosition position, |
| intptr_t offset, |
| StoreBarrierType emit_store_barrier = kEmitStoreBarrier); |
| Fragment StoreInstanceField( |
| const Field& field, |
| bool is_initialization_store, |
| StoreBarrierType emit_store_barrier = kEmitStoreBarrier); |
| Fragment StoreStaticField(TokenPosition position, const Field& field); |
| Fragment StringInterpolate(TokenPosition position); |
| Fragment StringInterpolateSingle(TokenPosition position); |
| Fragment ThrowTypeError(); |
| Fragment ThrowNoSuchMethodError(); |
| Fragment BuildImplicitClosureCreation(const Function& target); |
| Fragment GuardFieldLength(const Field& field, intptr_t deopt_id); |
| Fragment GuardFieldClass(const Field& field, intptr_t deopt_id); |
| |
| Fragment EvaluateAssertion(); |
| Fragment CheckVariableTypeInCheckedMode(const AbstractType& dst_type, |
| const String& name_symbol); |
| Fragment CheckBoolean(TokenPosition position); |
| Fragment CheckAssignable( |
| const AbstractType& dst_type, |
| const String& dst_name, |
| AssertAssignableInstr::Kind kind = AssertAssignableInstr::kUnknown); |
| |
| Fragment AssertAssignable( |
| TokenPosition position, |
| const AbstractType& dst_type, |
| const String& dst_name, |
| AssertAssignableInstr::Kind kind = AssertAssignableInstr::kUnknown); |
| Fragment AssertSubtype(TokenPosition position, |
| const AbstractType& sub_type, |
| const AbstractType& super_type, |
| const String& dst_name); |
| |
| bool NeedsDebugStepCheck(const Function& function, TokenPosition position); |
| bool NeedsDebugStepCheck(Value* value, TokenPosition position); |
| Fragment DebugStepCheck(TokenPosition position); |
| |
| LocalVariable* LookupVariable(intptr_t kernel_offset); |
| |
| bool IsCompiledForOsr() { return osr_id_ != Thread::kNoDeoptId; } |
| |
| TranslationHelper translation_helper_; |
| Thread* thread_; |
| Zone* zone_; |
| |
| ParsedFunction* parsed_function_; |
| const bool optimizing_; |
| intptr_t osr_id_; |
| const ZoneGrowableArray<const ICData*>& ic_data_array_; |
| |
| intptr_t next_function_id_; |
| intptr_t AllocateFunctionId() { return next_function_id_++; } |
| |
| intptr_t try_depth_; |
| intptr_t catch_depth_; |
| intptr_t for_in_depth_; |
| |
| GraphEntryInstr* graph_entry_; |
| |
| ScopeBuildingResult* scopes_; |
| |
| GrowableArray<YieldContinuation> yield_continuations_; |
| |
| LocalVariable* CurrentException() { |
| return scopes_->exception_variables[catch_depth_ - 1]; |
| } |
| LocalVariable* CurrentStackTrace() { |
| return scopes_->stack_trace_variables[catch_depth_ - 1]; |
| } |
| LocalVariable* CurrentRawException() { |
| return scopes_->raw_exception_variables[catch_depth_ - 1]; |
| } |
| LocalVariable* CurrentRawStackTrace() { |
| return scopes_->raw_stack_trace_variables[catch_depth_ - 1]; |
| } |
| LocalVariable* CurrentCatchContext() { |
| return scopes_->catch_context_variables[try_depth_]; |
| } |
| |
| // A chained list of breakable blocks. Chaining and lookup is done by the |
| // [BreakableBlock] class. |
| BreakableBlock* breakable_block_; |
| |
| // A chained list of switch blocks. Chaining and lookup is done by the |
| // [SwitchBlock] class. |
| SwitchBlock* switch_block_; |
| |
| // A chained list of try-finally blocks. Chaining and lookup is done by the |
| // [TryFinallyBlock] class. |
| TryFinallyBlock* try_finally_block_; |
| |
| // A chained list of catch blocks. Chaining and lookup is done by the |
| // [CatchBlock] class. |
| CatchBlock* catch_block_; |
| |
| ActiveClass active_class_; |
| |
| friend class BreakableBlock; |
| friend class CatchBlock; |
| friend class ConstantEvaluator; |
| friend class StreamingFlowGraphBuilder; |
| friend class SwitchBlock; |
| friend class TryFinallyBlock; |
| |
| DISALLOW_COPY_AND_ASSIGN(FlowGraphBuilder); |
| }; |
| |
| class SwitchBlock { |
| public: |
| SwitchBlock(FlowGraphBuilder* builder, intptr_t case_count) |
| : builder_(builder), |
| outer_(builder->switch_block_), |
| outer_finally_(builder->try_finally_block_), |
| case_count_(case_count), |
| context_depth_(builder->context_depth_), |
| try_index_(builder->CurrentTryIndex()) { |
| builder_->switch_block_ = this; |
| if (outer_ != NULL) { |
| depth_ = outer_->depth_ + outer_->case_count_; |
| } else { |
| depth_ = 0; |
| } |
| } |
| ~SwitchBlock() { builder_->switch_block_ = outer_; } |
| |
| bool HadJumper(intptr_t case_num) { |
| return destinations_.Lookup(case_num) != NULL; |
| } |
| |
| // Get destination via absolute target number (i.e. the correct destination |
| // is not not necessarily in this block. |
| JoinEntryInstr* Destination(intptr_t target_index, |
| TryFinallyBlock** outer_finally = NULL, |
| intptr_t* context_depth = NULL) { |
| // Find corresponding [SwitchStatement]. |
| SwitchBlock* block = this; |
| while (block->depth_ > target_index) { |
| block = block->outer_; |
| } |
| |
| // Set the outer finally block. |
| if (outer_finally != NULL) { |
| *outer_finally = block->outer_finally_; |
| *context_depth = block->context_depth_; |
| } |
| |
| // Ensure there's [JoinEntryInstr] for that [SwitchCase]. |
| return block->EnsureDestination(target_index - block->depth_); |
| } |
| |
| // Get destination via relative target number (i.e. relative to this block, |
| // 0 is first case in this block etc). |
| JoinEntryInstr* DestinationDirect(intptr_t case_num, |
| TryFinallyBlock** outer_finally = NULL, |
| intptr_t* context_depth = NULL) { |
| // Set the outer finally block. |
| if (outer_finally != NULL) { |
| *outer_finally = outer_finally_; |
| *context_depth = context_depth_; |
| } |
| |
| // Ensure there's [JoinEntryInstr] for that [SwitchCase]. |
| return EnsureDestination(case_num); |
| } |
| |
| private: |
| JoinEntryInstr* EnsureDestination(intptr_t case_num) { |
| JoinEntryInstr* cached_inst = destinations_.Lookup(case_num); |
| if (cached_inst == NULL) { |
| JoinEntryInstr* inst = builder_->BuildJoinEntry(try_index_); |
| destinations_.Insert(case_num, inst); |
| return inst; |
| } |
| return cached_inst; |
| } |
| |
| FlowGraphBuilder* builder_; |
| SwitchBlock* outer_; |
| |
| IntMap<JoinEntryInstr*> destinations_; |
| |
| TryFinallyBlock* outer_finally_; |
| intptr_t case_count_; |
| intptr_t depth_; |
| intptr_t context_depth_; |
| intptr_t try_index_; |
| }; |
| |
| class TryFinallyBlock { |
| public: |
| TryFinallyBlock(FlowGraphBuilder* builder, intptr_t finalizer_kernel_offset) |
| : builder_(builder), |
| outer_(builder->try_finally_block_), |
| finalizer_kernel_offset_(finalizer_kernel_offset), |
| context_depth_(builder->context_depth_), |
| // Finalizers are executed outside of the try block hence |
| // try depth of finalizers are one less than current try |
| // depth. |
| try_depth_(builder->try_depth_ - 1), |
| try_index_(builder_->CurrentTryIndex()) { |
| builder_->try_finally_block_ = this; |
| } |
| ~TryFinallyBlock() { builder_->try_finally_block_ = outer_; } |
| |
| intptr_t finalizer_kernel_offset() const { return finalizer_kernel_offset_; } |
| intptr_t context_depth() const { return context_depth_; } |
| intptr_t try_depth() const { return try_depth_; } |
| intptr_t try_index() const { return try_index_; } |
| TryFinallyBlock* outer() const { return outer_; } |
| |
| private: |
| FlowGraphBuilder* const builder_; |
| TryFinallyBlock* const outer_; |
| intptr_t finalizer_kernel_offset_; |
| const intptr_t context_depth_; |
| const intptr_t try_depth_; |
| const intptr_t try_index_; |
| |
| DISALLOW_COPY_AND_ASSIGN(TryFinallyBlock); |
| }; |
| |
| class BreakableBlock { |
| public: |
| explicit BreakableBlock(FlowGraphBuilder* builder) |
| : builder_(builder), |
| outer_(builder->breakable_block_), |
| destination_(NULL), |
| outer_finally_(builder->try_finally_block_), |
| context_depth_(builder->context_depth_), |
| try_index_(builder->CurrentTryIndex()) { |
| if (builder_->breakable_block_ == NULL) { |
| index_ = 0; |
| } else { |
| index_ = builder_->breakable_block_->index_ + 1; |
| } |
| builder_->breakable_block_ = this; |
| } |
| ~BreakableBlock() { builder_->breakable_block_ = outer_; } |
| |
| bool HadJumper() { return destination_ != NULL; } |
| |
| JoinEntryInstr* destination() { return destination_; } |
| |
| JoinEntryInstr* BreakDestination(intptr_t label_index, |
| TryFinallyBlock** outer_finally, |
| intptr_t* context_depth) { |
| BreakableBlock* block = builder_->breakable_block_; |
| while (block->index_ != label_index) { |
| block = block->outer_; |
| } |
| ASSERT(block != NULL); |
| *outer_finally = block->outer_finally_; |
| *context_depth = block->context_depth_; |
| return block->EnsureDestination(); |
| } |
| |
| private: |
| JoinEntryInstr* EnsureDestination() { |
| if (destination_ == NULL) { |
| destination_ = builder_->BuildJoinEntry(try_index_); |
| } |
| return destination_; |
| } |
| |
| FlowGraphBuilder* builder_; |
| intptr_t index_; |
| BreakableBlock* outer_; |
| JoinEntryInstr* destination_; |
| TryFinallyBlock* outer_finally_; |
| intptr_t context_depth_; |
| intptr_t try_index_; |
| |
| DISALLOW_COPY_AND_ASSIGN(BreakableBlock); |
| }; |
| |
| class CatchBlock { |
| public: |
| CatchBlock(FlowGraphBuilder* builder, |
| LocalVariable* exception_var, |
| LocalVariable* stack_trace_var, |
| intptr_t catch_try_index) |
| : builder_(builder), |
| outer_(builder->catch_block_), |
| exception_var_(exception_var), |
| stack_trace_var_(stack_trace_var), |
| catch_try_index_(catch_try_index) { |
| builder_->catch_block_ = this; |
| } |
| ~CatchBlock() { builder_->catch_block_ = outer_; } |
| |
| LocalVariable* exception_var() { return exception_var_; } |
| LocalVariable* stack_trace_var() { return stack_trace_var_; } |
| intptr_t catch_try_index() { return catch_try_index_; } |
| |
| private: |
| FlowGraphBuilder* builder_; |
| CatchBlock* outer_; |
| LocalVariable* exception_var_; |
| LocalVariable* stack_trace_var_; |
| intptr_t catch_try_index_; |
| |
| DISALLOW_COPY_AND_ASSIGN(CatchBlock); |
| }; |
| |
| } // namespace kernel |
| } // namespace dart |
| |
| #endif // !defined(DART_PRECOMPILED_RUNTIME) |
| #endif // RUNTIME_VM_COMPILER_FRONTEND_KERNEL_TO_IL_H_ |