| // Copyright (c) 2012, 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. |
| |
| #include "vm/flow_graph_builder.h" |
| |
| #include "lib/invocation_mirror.h" |
| #include "vm/ast_printer.h" |
| #include "vm/bit_vector.h" |
| #include "vm/class_finalizer.h" |
| #include "vm/exceptions.h" |
| #include "vm/flags.h" |
| #include "vm/flow_graph.h" |
| #include "vm/flow_graph_compiler.h" |
| #include "vm/heap.h" |
| #include "vm/il_printer.h" |
| #include "vm/intermediate_language.h" |
| #include "vm/isolate.h" |
| #include "vm/longjump.h" |
| #include "vm/object.h" |
| #include "vm/object_store.h" |
| #include "vm/os.h" |
| #include "vm/parser.h" |
| #include "vm/resolver.h" |
| #include "vm/scopes.h" |
| #include "vm/stack_frame.h" |
| #include "vm/stub_code.h" |
| #include "vm/symbols.h" |
| #include "vm/token.h" |
| #include "vm/zone.h" |
| |
| namespace dart { |
| |
| DEFINE_FLAG(bool, eliminate_type_checks, true, |
| "Eliminate type checks when allowed by static type analysis."); |
| DEFINE_FLAG(bool, print_ast, false, "Print abstract syntax tree."); |
| DEFINE_FLAG(bool, print_scopes, false, "Print scopes of local variables."); |
| DEFINE_FLAG(bool, trace_type_check_elimination, false, |
| "Trace type check elimination at compile time."); |
| DECLARE_FLAG(bool, enable_type_checks); |
| |
| |
| // TODO(srdjan): Allow compiler to add constants as they are encountered in |
| // the compilation. |
| const double kCommonDoubleConstants[] = |
| {-1.0, -0.5, -0.1, 0.0, 0.1, 0.5, 1.0, 2.0, 4.0, 5.0, |
| 10.0, 20.0, 30.0, 64.0, 255.0, NAN, |
| // From dart:math |
| 2.718281828459045, 2.302585092994046, 0.6931471805599453, |
| 1.4426950408889634, 0.4342944819032518, 3.1415926535897932, |
| 0.7071067811865476, 1.4142135623730951}; |
| |
| uword FlowGraphBuilder::FindDoubleConstant(double value) { |
| intptr_t len = sizeof(kCommonDoubleConstants) / sizeof(double); // NOLINT |
| for (intptr_t i = 0; i < len; i++) { |
| if (Utils::DoublesBitEqual(value, kCommonDoubleConstants[i])) { |
| return reinterpret_cast<uword>(&kCommonDoubleConstants[i]); |
| } |
| } |
| return 0; |
| } |
| |
| |
| // Base class for a stack of enclosing statements of interest (e.g., |
| // blocks (breakable) and loops (continuable)). |
| class NestedStatement : public ValueObject { |
| public: |
| FlowGraphBuilder* owner() const { return owner_; } |
| const SourceLabel* label() const { return label_; } |
| NestedStatement* outer() const { return outer_; } |
| JoinEntryInstr* break_target() const { return break_target_; } |
| |
| virtual intptr_t ContextLevel() const; |
| |
| virtual JoinEntryInstr* BreakTargetFor(SourceLabel* label); |
| virtual JoinEntryInstr* ContinueTargetFor(SourceLabel* label); |
| |
| protected: |
| NestedStatement(FlowGraphBuilder* owner, const SourceLabel* label) |
| : owner_(owner), |
| label_(label), |
| outer_(owner->nesting_stack_), |
| break_target_(NULL) { |
| // Push on the owner's nesting stack. |
| owner->nesting_stack_ = this; |
| } |
| |
| virtual ~NestedStatement() { |
| // Pop from the owner's nesting stack. |
| ASSERT(owner_->nesting_stack_ == this); |
| owner_->nesting_stack_ = outer_; |
| } |
| |
| private: |
| FlowGraphBuilder* owner_; |
| const SourceLabel* label_; |
| NestedStatement* outer_; |
| |
| JoinEntryInstr* break_target_; |
| }; |
| |
| |
| intptr_t NestedStatement::ContextLevel() const { |
| // Context level is determined by the innermost nested statement having one. |
| return (outer() == NULL) ? 0 : outer()->ContextLevel(); |
| } |
| |
| |
| intptr_t FlowGraphBuilder::context_level() const { |
| return (nesting_stack() == NULL) ? 0 : nesting_stack()->ContextLevel(); |
| } |
| |
| |
| JoinEntryInstr* NestedStatement::BreakTargetFor(SourceLabel* label) { |
| if (label != label_) return NULL; |
| if (break_target_ == NULL) { |
| break_target_ = |
| new JoinEntryInstr(owner()->AllocateBlockId(), owner()->try_index()); |
| } |
| return break_target_; |
| } |
| |
| |
| JoinEntryInstr* NestedStatement::ContinueTargetFor(SourceLabel* label) { |
| return NULL; |
| } |
| |
| |
| // A nested statement that has its own context level. |
| class NestedBlock : public NestedStatement { |
| public: |
| NestedBlock(FlowGraphBuilder* owner, SequenceNode* node) |
| : NestedStatement(owner, node->label()), scope_(node->scope()) {} |
| |
| virtual intptr_t ContextLevel() const; |
| |
| private: |
| LocalScope* scope_; |
| }; |
| |
| |
| intptr_t NestedBlock::ContextLevel() const { |
| return ((scope_ == NULL) || (scope_->num_context_variables() == 0)) |
| ? NestedStatement::ContextLevel() |
| : scope_->context_level(); |
| } |
| |
| |
| // A nested statement that can be the target of a continue as well as a |
| // break. |
| class NestedLoop : public NestedStatement { |
| public: |
| NestedLoop(FlowGraphBuilder* owner, SourceLabel* label) |
| : NestedStatement(owner, label), continue_target_(NULL) { |
| owner->IncrementLoopDepth(); |
| } |
| |
| virtual ~NestedLoop() { |
| owner()->DecrementLoopDepth(); |
| } |
| |
| JoinEntryInstr* continue_target() const { return continue_target_; } |
| |
| virtual JoinEntryInstr* ContinueTargetFor(SourceLabel* label); |
| |
| private: |
| JoinEntryInstr* continue_target_; |
| }; |
| |
| |
| JoinEntryInstr* NestedLoop::ContinueTargetFor(SourceLabel* label) { |
| if (label != this->label()) return NULL; |
| if (continue_target_ == NULL) { |
| continue_target_ = |
| new JoinEntryInstr(owner()->AllocateBlockId(), owner()->try_index()); |
| } |
| return continue_target_; |
| } |
| |
| |
| // A nested switch which can be the target of a break if labeled, and whose |
| // cases can be the targets of continues. |
| class NestedSwitch : public NestedStatement { |
| public: |
| NestedSwitch(FlowGraphBuilder* owner, SwitchNode* node); |
| |
| virtual JoinEntryInstr* ContinueTargetFor(SourceLabel* label); |
| |
| private: |
| GrowableArray<SourceLabel*> case_labels_; |
| GrowableArray<JoinEntryInstr*> case_targets_; |
| }; |
| |
| |
| NestedSwitch::NestedSwitch(FlowGraphBuilder* owner, SwitchNode* node) |
| : NestedStatement(owner, node->label()), |
| case_labels_(node->body()->length()), |
| case_targets_(node->body()->length()) { |
| SequenceNode* body = node->body(); |
| for (intptr_t i = 0; i < body->length(); ++i) { |
| CaseNode* case_node = body->NodeAt(i)->AsCaseNode(); |
| if (case_node != NULL) { |
| case_labels_.Add(case_node->label()); |
| case_targets_.Add(NULL); |
| } |
| } |
| } |
| |
| |
| JoinEntryInstr* NestedSwitch::ContinueTargetFor(SourceLabel* label) { |
| // Allocate a join for a case clause that matches the label. This block |
| // is not necessarily targeted by a continue, but we always use a join in |
| // the graph anyway. |
| for (intptr_t i = 0; i < case_labels_.length(); ++i) { |
| if (label != case_labels_[i]) continue; |
| if (case_targets_[i] == NULL) { |
| case_targets_[i] = |
| new JoinEntryInstr(owner()->AllocateBlockId(), owner()->try_index()); |
| } |
| return case_targets_[i]; |
| } |
| return NULL; |
| } |
| |
| |
| FlowGraphBuilder::FlowGraphBuilder(ParsedFunction* parsed_function, |
| const Array& ic_data_array, |
| InlineExitCollector* exit_collector, |
| intptr_t osr_id, |
| bool is_optimizing) |
| : parsed_function_(parsed_function), |
| ic_data_array_(ic_data_array), |
| num_copied_params_(parsed_function->num_copied_params()), |
| // All parameters are copied if any parameter is. |
| num_non_copied_params_((num_copied_params_ == 0) |
| ? parsed_function->function().num_fixed_parameters() |
| : 0), |
| num_stack_locals_(parsed_function->num_stack_locals()), |
| exit_collector_(exit_collector), |
| guarded_fields_(new ZoneGrowableArray<const Field*>()), |
| last_used_block_id_(0), // 0 is used for the graph entry. |
| try_index_(CatchClauseNode::kInvalidTryIndex), |
| catch_try_index_(CatchClauseNode::kInvalidTryIndex), |
| loop_depth_(0), |
| graph_entry_(NULL), |
| temp_count_(0), |
| args_pushed_(0), |
| nesting_stack_(NULL), |
| osr_id_(osr_id), |
| is_optimizing_(is_optimizing) { } |
| |
| |
| void FlowGraphBuilder::AddCatchEntry(CatchBlockEntryInstr* entry) { |
| graph_entry_->AddCatchEntry(entry); |
| } |
| |
| |
| void InlineExitCollector::PrepareGraphs(FlowGraph* callee_graph) { |
| ASSERT(callee_graph->graph_entry()->SuccessorCount() == 1); |
| ASSERT(callee_graph->max_block_id() > caller_graph_->max_block_id()); |
| ASSERT(callee_graph->max_virtual_register_number() > |
| caller_graph_->max_virtual_register_number()); |
| |
| // Adjust the caller's maximum block id and current SSA temp index. |
| caller_graph_->set_max_block_id(callee_graph->max_block_id()); |
| caller_graph_->set_current_ssa_temp_index( |
| callee_graph->max_virtual_register_number()); |
| |
| // Attach the outer environment on each instruction in the callee graph. |
| ASSERT(call_->env() != NULL); |
| // Scale the edge weights by the call count for the inlined function. |
| double scale_factor = static_cast<double>(call_->CallCount()) |
| / static_cast<double>(caller_graph_->graph_entry()->entry_count()); |
| for (BlockIterator block_it = callee_graph->postorder_iterator(); |
| !block_it.Done(); |
| block_it.Advance()) { |
| BlockEntryInstr* block = block_it.Current(); |
| if (block->IsTargetEntry()) { |
| block->AsTargetEntry()->adjust_edge_weight(scale_factor); |
| } |
| Instruction* instr = block; |
| for (ForwardInstructionIterator it(block); !it.Done(); it.Advance()) { |
| instr = it.Current(); |
| // TODO(zerny): Avoid creating unnecessary environments. Note that some |
| // optimizations need deoptimization info for non-deoptable instructions, |
| // eg, LICM on GOTOs. |
| if (instr->env() != NULL) call_->env()->DeepCopyToOuter(instr); |
| } |
| if (instr->IsGoto()) { |
| instr->AsGoto()->adjust_edge_weight(scale_factor); |
| } |
| } |
| } |
| |
| |
| void InlineExitCollector::AddExit(ReturnInstr* exit) { |
| Data data = { NULL, exit }; |
| exits_.Add(data); |
| } |
| |
| |
| void InlineExitCollector::Union(const InlineExitCollector* other) { |
| // It doesn't make sense to combine different calls or calls from |
| // different graphs. |
| ASSERT(caller_graph_ == other->caller_graph_); |
| ASSERT(call_ == other->call_); |
| exits_.AddArray(other->exits_); |
| } |
| |
| |
| int InlineExitCollector::LowestBlockIdFirst(const Data* a, const Data* b) { |
| return (a->exit_block->block_id() - b->exit_block->block_id()); |
| } |
| |
| |
| void InlineExitCollector::SortExits() { |
| // Assign block entries here because we did not necessarily know them when |
| // the return exit was added to the array. |
| for (int i = 0; i < exits_.length(); ++i) { |
| exits_[i].exit_block = exits_[i].exit_return->GetBlock(); |
| } |
| exits_.Sort(LowestBlockIdFirst); |
| } |
| |
| |
| Definition* InlineExitCollector::JoinReturns(BlockEntryInstr** exit_block, |
| Instruction** last_instruction) { |
| // First sort the list of exits by block id (caching return instruction |
| // block entries as a side effect). |
| SortExits(); |
| intptr_t num_exits = exits_.length(); |
| if (num_exits == 1) { |
| ReturnAt(0)->UnuseAllInputs(); |
| *exit_block = ExitBlockAt(0); |
| *last_instruction = LastInstructionAt(0); |
| return call_->HasUses() ? ValueAt(0)->definition() : NULL; |
| } else { |
| ASSERT(num_exits > 1); |
| // Create a join of the returns. |
| intptr_t join_id = caller_graph_->max_block_id() + 1; |
| caller_graph_->set_max_block_id(join_id); |
| JoinEntryInstr* join = |
| new JoinEntryInstr(join_id, CatchClauseNode::kInvalidTryIndex); |
| join->InheritDeoptTargetAfter(call_); |
| |
| // The dominator set of the join is the intersection of the dominator |
| // sets of all the predecessors. If we keep the dominator sets ordered |
| // by height in the dominator tree, we can also get the immediate |
| // dominator of the join node from the intersection. |
| // |
| // block_dominators is the dominator set for each block, ordered from |
| // the immediate dominator to the root of the dominator tree. This is |
| // the order we collect them in (adding at the end). |
| // |
| // join_dominators is the join's dominators ordered from the root of the |
| // dominator tree to the immediate dominator. This order supports |
| // removing during intersection by truncating the list. |
| GrowableArray<BlockEntryInstr*> block_dominators; |
| GrowableArray<BlockEntryInstr*> join_dominators; |
| for (intptr_t i = 0; i < num_exits; ++i) { |
| // Add the control-flow edge. |
| GotoInstr* goto_instr = new GotoInstr(join); |
| goto_instr->InheritDeoptTarget(ReturnAt(i)); |
| LastInstructionAt(i)->LinkTo(goto_instr); |
| ExitBlockAt(i)->set_last_instruction(LastInstructionAt(i)->next()); |
| join->predecessors_.Add(ExitBlockAt(i)); |
| |
| // Collect the block's dominators. |
| block_dominators.Clear(); |
| BlockEntryInstr* dominator = ExitBlockAt(i)->dominator(); |
| while (dominator != NULL) { |
| block_dominators.Add(dominator); |
| dominator = dominator->dominator(); |
| } |
| |
| if (i == 0) { |
| // The initial dominator set is the first predecessor's dominator |
| // set. Reverse it. |
| for (intptr_t j = block_dominators.length() - 1; j >= 0; --j) { |
| join_dominators.Add(block_dominators[j]); |
| } |
| } else { |
| // Intersect the block's dominators with the join's dominators so far. |
| intptr_t last = block_dominators.length() - 1; |
| for (intptr_t j = 0; j < join_dominators.length(); ++j) { |
| intptr_t k = last - j; // Corresponding index in block_dominators. |
| if ((k < 0) || (join_dominators[j] != block_dominators[k])) { |
| // We either exhausted the dominators for this block before |
| // exhausting the current intersection, or else we found a block |
| // on the path from the root of the tree that is not in common. |
| // I.e., there cannot be an empty set of dominators. |
| ASSERT(j > 0); |
| join_dominators.TruncateTo(j); |
| break; |
| } |
| } |
| } |
| } |
| // The immediate dominator of the join is the last one in the ordered |
| // intersection. |
| join_dominators.Last()->AddDominatedBlock(join); |
| *exit_block = join; |
| *last_instruction = join; |
| |
| // If the call has uses, create a phi of the returns. |
| if (call_->HasUses()) { |
| // Add a phi of the return values. |
| PhiInstr* phi = new PhiInstr(join, num_exits); |
| phi->set_ssa_temp_index(caller_graph_->alloc_ssa_temp_index()); |
| phi->mark_alive(); |
| for (intptr_t i = 0; i < num_exits; ++i) { |
| ReturnAt(i)->RemoveEnvironment(); |
| phi->SetInputAt(i, ValueAt(i)); |
| } |
| join->InsertPhi(phi); |
| return phi; |
| } else { |
| // In the case that the result is unused, remove the return value uses |
| // from their definition's use list. |
| for (intptr_t i = 0; i < num_exits; ++i) { |
| ReturnAt(i)->UnuseAllInputs(); |
| } |
| return NULL; |
| } |
| } |
| } |
| |
| |
| void InlineExitCollector::ReplaceCall(TargetEntryInstr* callee_entry) { |
| ASSERT(call_->previous() != NULL); |
| ASSERT(call_->next() != NULL); |
| BlockEntryInstr* call_block = call_->GetBlock(); |
| |
| // Insert the callee graph into the caller graph. |
| BlockEntryInstr* callee_exit = NULL; |
| Instruction* callee_last_instruction = NULL; |
| |
| if (exits_.length() == 0) { |
| // Handle the case when there are no normal return exits from the callee |
| // (i.e. the callee unconditionally throws) by inserting an artificial |
| // branch (true === true). |
| // The true successor is the inlined body, the false successor |
| // goes to the rest of the caller graph. It is removed as unreachable code |
| // by the constant propagation. |
| TargetEntryInstr* false_block = |
| new TargetEntryInstr(caller_graph_->allocate_block_id(), |
| call_block->try_index()); |
| false_block->InheritDeoptTargetAfter(call_); |
| false_block->LinkTo(call_->next()); |
| call_block->ReplaceAsPredecessorWith(false_block); |
| |
| ConstantInstr* true_const = caller_graph_->GetConstant(Bool::True()); |
| BranchInstr* branch = |
| new BranchInstr(new StrictCompareInstr(call_block->start_pos(), |
| Token::kEQ_STRICT, |
| new Value(true_const), |
| new Value(true_const), |
| false)); // No number check. |
| branch->InheritDeoptTarget(call_); |
| *branch->true_successor_address() = callee_entry; |
| *branch->false_successor_address() = false_block; |
| |
| call_->previous()->AppendInstruction(branch); |
| call_block->set_last_instruction(branch); |
| |
| // Update dominator tree. |
| call_block->AddDominatedBlock(callee_entry); |
| call_block->AddDominatedBlock(false_block); |
| |
| } else { |
| Definition* callee_result = JoinReturns(&callee_exit, |
| &callee_last_instruction); |
| if (callee_result != NULL) { |
| call_->ReplaceUsesWith(callee_result); |
| } |
| if (callee_last_instruction == callee_entry) { |
| // There are no instructions in the inlined function (e.g., it might be |
| // a return of a parameter or a return of a constant defined in the |
| // initial definitions). |
| call_->previous()->LinkTo(call_->next()); |
| } else { |
| call_->previous()->LinkTo(callee_entry->next()); |
| callee_last_instruction->LinkTo(call_->next()); |
| } |
| if (callee_exit != callee_entry) { |
| // In case of control flow, locally update the predecessors, phis and |
| // dominator tree. |
| // |
| // Pictorially, the graph structure is: |
| // |
| // Bc : call_block Bi : callee_entry |
| // before_call inlined_head |
| // call ... other blocks ... |
| // after_call Be : callee_exit |
| // inlined_foot |
| // And becomes: |
| // |
| // Bc : call_block |
| // before_call |
| // inlined_head |
| // ... other blocks ... |
| // Be : callee_exit |
| // inlined_foot |
| // after_call |
| // |
| // For successors of 'after_call', the call block (Bc) is replaced as a |
| // predecessor by the callee exit (Be). |
| call_block->ReplaceAsPredecessorWith(callee_exit); |
| // For successors of 'inlined_head', the callee entry (Bi) is replaced |
| // as a predecessor by the call block (Bc). |
| callee_entry->ReplaceAsPredecessorWith(call_block); |
| |
| // The callee exit is now the immediate dominator of blocks whose |
| // immediate dominator was the call block. |
| ASSERT(callee_exit->dominated_blocks().is_empty()); |
| for (intptr_t i = 0; i < call_block->dominated_blocks().length(); ++i) { |
| BlockEntryInstr* block = call_block->dominated_blocks()[i]; |
| callee_exit->AddDominatedBlock(block); |
| } |
| // The call block is now the immediate dominator of blocks whose |
| // immediate dominator was the callee entry. |
| call_block->ClearDominatedBlocks(); |
| for (intptr_t i = 0; i < callee_entry->dominated_blocks().length(); ++i) { |
| BlockEntryInstr* block = callee_entry->dominated_blocks()[i]; |
| call_block->AddDominatedBlock(block); |
| } |
| } |
| |
| // Callee entry in not in the graph anymore. Remove it from use lists. |
| callee_entry->UnuseAllInputs(); |
| } |
| // Neither call nor the graph entry (if present) are in the |
| // graph at this point. Remove them from use lists. |
| if (callee_entry->PredecessorCount() > 0) { |
| callee_entry->PredecessorAt(0)->AsGraphEntry()->UnuseAllInputs(); |
| } |
| call_->UnuseAllInputs(); |
| } |
| |
| |
| void EffectGraphVisitor::Append(const EffectGraphVisitor& other_fragment) { |
| ASSERT(is_open()); |
| if (other_fragment.is_empty()) return; |
| if (is_empty()) { |
| entry_ = other_fragment.entry(); |
| } else { |
| exit()->LinkTo(other_fragment.entry()); |
| } |
| exit_ = other_fragment.exit(); |
| } |
| |
| |
| Value* EffectGraphVisitor::Bind(Definition* definition) { |
| ASSERT(is_open()); |
| owner()->DeallocateTemps(definition->InputCount()); |
| owner()->add_args_pushed(-definition->ArgumentCount()); |
| definition->set_use_kind(Definition::kValue); |
| definition->set_temp_index(owner()->AllocateTemp()); |
| if (is_empty()) { |
| entry_ = definition; |
| } else { |
| exit()->LinkTo(definition); |
| } |
| exit_ = definition; |
| return new Value(definition); |
| } |
| |
| |
| void EffectGraphVisitor::Do(Definition* definition) { |
| ASSERT(is_open()); |
| owner()->DeallocateTemps(definition->InputCount()); |
| owner()->add_args_pushed(-definition->ArgumentCount()); |
| definition->set_use_kind(Definition::kEffect); |
| if (is_empty()) { |
| entry_ = definition; |
| } else { |
| exit()->LinkTo(definition); |
| } |
| exit_ = definition; |
| } |
| |
| |
| void EffectGraphVisitor::AddInstruction(Instruction* instruction) { |
| ASSERT(is_open()); |
| ASSERT(instruction->IsPushArgument() || !instruction->IsDefinition()); |
| ASSERT(!instruction->IsBlockEntry()); |
| owner()->DeallocateTemps(instruction->InputCount()); |
| owner()->add_args_pushed(-instruction->ArgumentCount()); |
| if (is_empty()) { |
| entry_ = exit_ = instruction; |
| } else { |
| exit()->LinkTo(instruction); |
| exit_ = instruction; |
| } |
| } |
| |
| |
| void EffectGraphVisitor::AddReturnExit(intptr_t token_pos, Value* value) { |
| ASSERT(is_open()); |
| ReturnInstr* return_instr = new ReturnInstr(token_pos, value); |
| AddInstruction(return_instr); |
| InlineExitCollector* exit_collector = owner()->exit_collector(); |
| if (exit_collector != NULL) { |
| exit_collector->AddExit(return_instr); |
| } |
| CloseFragment(); |
| } |
| |
| |
| void EffectGraphVisitor::Goto(JoinEntryInstr* join) { |
| ASSERT(is_open()); |
| if (is_empty()) { |
| entry_ = new GotoInstr(join); |
| } else { |
| exit()->Goto(join); |
| } |
| CloseFragment(); |
| } |
| |
| |
| // Appends a graph fragment to a block entry instruction. Returns the entry |
| // instruction if the fragment was empty or else the exit of the fragment if |
| // it was non-empty (so NULL if the fragment is closed). |
| // |
| // Note that the fragment is no longer a valid fragment after calling this |
| // function -- the fragment is closed at its entry because the entry has a |
| // predecessor in the graph. |
| static Instruction* AppendFragment(BlockEntryInstr* entry, |
| const EffectGraphVisitor& fragment) { |
| if (fragment.is_empty()) return entry; |
| entry->LinkTo(fragment.entry()); |
| return fragment.exit(); |
| } |
| |
| |
| void EffectGraphVisitor::Join(const TestGraphVisitor& test_fragment, |
| const EffectGraphVisitor& true_fragment, |
| const EffectGraphVisitor& false_fragment) { |
| // We have: a test graph fragment with zero, one, or two available exits; |
| // and a pair of effect graph fragments with zero or one available exits. |
| // We want to append the branch and (if necessary) a join node to this |
| // graph fragment. |
| ASSERT(is_open()); |
| |
| // 1. Connect the test to this graph. |
| Append(test_fragment); |
| |
| // 2. Connect the true and false bodies to the test and record their exits |
| // (if any). |
| BlockEntryInstr* true_entry = test_fragment.CreateTrueSuccessor(); |
| Instruction* true_exit = AppendFragment(true_entry, true_fragment); |
| |
| BlockEntryInstr* false_entry = test_fragment.CreateFalseSuccessor(); |
| Instruction* false_exit = AppendFragment(false_entry, false_fragment); |
| |
| // 3. Add a join or select one (or neither) of the arms as exit. |
| if (true_exit == NULL) { |
| exit_ = false_exit; // May be NULL. |
| } else if (false_exit == NULL) { |
| exit_ = true_exit; |
| } else { |
| JoinEntryInstr* join = |
| new JoinEntryInstr(owner()->AllocateBlockId(), owner()->try_index()); |
| true_exit->Goto(join); |
| false_exit->Goto(join); |
| exit_ = join; |
| } |
| } |
| |
| |
| void EffectGraphVisitor::TieLoop(intptr_t token_pos, |
| const TestGraphVisitor& test_fragment, |
| const EffectGraphVisitor& body_fragment) { |
| // We have: a test graph fragment with zero, one, or two available exits; |
| // and an effect graph fragment with zero or one available exits. We want |
| // to append the 'while loop' consisting of the test graph fragment as |
| // condition and the effect graph fragment as body. |
| ASSERT(is_open()); |
| |
| // 1. Connect the body to the test if it is reachable, and if so record |
| // its exit (if any). |
| BlockEntryInstr* body_entry = test_fragment.CreateTrueSuccessor(); |
| Instruction* body_exit = AppendFragment(body_entry, body_fragment); |
| |
| // 2. Connect the test to this graph, including the body if reachable and |
| // using a fresh join node if the body is reachable and has an open exit. |
| if (body_exit == NULL) { |
| Append(test_fragment); |
| } else { |
| JoinEntryInstr* join = |
| new JoinEntryInstr(owner()->AllocateBlockId(), owner()->try_index()); |
| CheckStackOverflowInstr* check = |
| new CheckStackOverflowInstr(token_pos, owner()->loop_depth()); |
| join->LinkTo(check); |
| check->LinkTo(test_fragment.entry()); |
| Goto(join); |
| body_exit->Goto(join); |
| } |
| |
| // 3. Set the exit to the graph to be the false successor of the test, a |
| // fresh target node |
| exit_ = test_fragment.CreateFalseSuccessor(); |
| } |
| |
| |
| PushArgumentInstr* EffectGraphVisitor::PushArgument(Value* value) { |
| owner_->add_args_pushed(1); |
| PushArgumentInstr* result = new PushArgumentInstr(value); |
| AddInstruction(result); |
| return result; |
| } |
| |
| |
| Definition* EffectGraphVisitor::BuildStoreTemp(const LocalVariable& local, |
| Value* value) { |
| ASSERT(!local.is_captured()); |
| return new StoreLocalInstr(local, value); |
| } |
| |
| |
| Definition* EffectGraphVisitor::BuildStoreExprTemp(Value* value) { |
| return BuildStoreTemp(*owner()->parsed_function()->expression_temp_var(), |
| value); |
| } |
| |
| |
| Definition* EffectGraphVisitor::BuildLoadExprTemp() { |
| return BuildLoadLocal(*owner()->parsed_function()->expression_temp_var()); |
| } |
| |
| |
| Definition* EffectGraphVisitor::BuildStoreLocal(const LocalVariable& local, |
| Value* value) { |
| if (local.is_captured()) { |
| LocalVariable* tmp_var = EnterTempLocalScope(value); |
| intptr_t delta = |
| owner()->context_level() - local.owner()->context_level(); |
| ASSERT(delta >= 0); |
| Value* context = Bind(new CurrentContextInstr()); |
| while (delta-- > 0) { |
| context = Bind(new LoadFieldInstr( |
| context, Context::parent_offset(), Type::ZoneHandle(), |
| Scanner::kNoSourcePos)); |
| } |
| Value* tmp_val = Bind(new LoadLocalInstr(*tmp_var)); |
| StoreInstanceFieldInstr* store = |
| new StoreInstanceFieldInstr(Context::variable_offset(local.index()), |
| context, |
| tmp_val, |
| kEmitStoreBarrier, |
| Scanner::kNoSourcePos); |
| Do(store); |
| return ExitTempLocalScope(tmp_var); |
| } else { |
| return new StoreLocalInstr(local, value); |
| } |
| } |
| |
| |
| Definition* EffectGraphVisitor::BuildLoadLocal(const LocalVariable& local) { |
| if (local.IsConst()) { |
| return new ConstantInstr(*local.ConstValue()); |
| } else if (local.is_captured()) { |
| intptr_t delta = |
| owner()->context_level() - local.owner()->context_level(); |
| ASSERT(delta >= 0); |
| Value* context = Bind(new CurrentContextInstr()); |
| while (delta-- > 0) { |
| context = Bind(new LoadFieldInstr( |
| context, Context::parent_offset(), Type::ZoneHandle(), |
| Scanner::kNoSourcePos)); |
| } |
| return new LoadFieldInstr(context, |
| Context::variable_offset(local.index()), |
| local.type(), |
| Scanner::kNoSourcePos); |
| } else { |
| return new LoadLocalInstr(local); |
| } |
| } |
| |
| |
| // Stores current context into the 'variable' |
| void EffectGraphVisitor::BuildSaveContext(const LocalVariable& variable) { |
| Value* context = Bind(new CurrentContextInstr()); |
| Do(BuildStoreLocal(variable, context)); |
| } |
| |
| |
| // Loads context saved in 'context_variable' into the current context. |
| void EffectGraphVisitor::BuildRestoreContext(const LocalVariable& variable) { |
| Value* load_saved_context = Bind(BuildLoadLocal(variable)); |
| AddInstruction(new StoreContextInstr(load_saved_context)); |
| } |
| |
| |
| void TestGraphVisitor::ConnectBranchesTo( |
| const GrowableArray<TargetEntryInstr**>& branches, |
| JoinEntryInstr* join) const { |
| ASSERT(!branches.is_empty()); |
| for (intptr_t i = 0; i < branches.length(); i++) { |
| TargetEntryInstr* target = |
| new TargetEntryInstr(owner()->AllocateBlockId(), owner()->try_index()); |
| *(branches[i]) = target; |
| target->Goto(join); |
| } |
| } |
| |
| |
| void TestGraphVisitor::IfTrueGoto(JoinEntryInstr* join) const { |
| ConnectBranchesTo(true_successor_addresses_, join); |
| } |
| |
| |
| void TestGraphVisitor::IfFalseGoto(JoinEntryInstr* join) const { |
| ConnectBranchesTo(false_successor_addresses_, join); |
| } |
| |
| |
| BlockEntryInstr* TestGraphVisitor::CreateSuccessorFor( |
| const GrowableArray<TargetEntryInstr**>& branches) const { |
| ASSERT(!branches.is_empty()); |
| |
| if (branches.length() == 1) { |
| TargetEntryInstr* target = |
| new TargetEntryInstr(owner()->AllocateBlockId(), owner()->try_index()); |
| *(branches[0]) = target; |
| return target; |
| } |
| |
| JoinEntryInstr* join = |
| new JoinEntryInstr(owner()->AllocateBlockId(), owner()->try_index()); |
| ConnectBranchesTo(branches, join); |
| return join; |
| } |
| |
| |
| BlockEntryInstr* TestGraphVisitor::CreateTrueSuccessor() const { |
| return CreateSuccessorFor(true_successor_addresses_); |
| } |
| |
| |
| BlockEntryInstr* TestGraphVisitor::CreateFalseSuccessor() const { |
| return CreateSuccessorFor(false_successor_addresses_); |
| } |
| |
| |
| void TestGraphVisitor::ReturnValue(Value* value) { |
| if (FLAG_enable_type_checks) { |
| value = Bind(new AssertBooleanInstr(condition_token_pos(), value)); |
| } |
| Value* constant_true = Bind(new ConstantInstr(Bool::True())); |
| StrictCompareInstr* comp = |
| new StrictCompareInstr(condition_token_pos(), |
| Token::kEQ_STRICT, |
| value, |
| constant_true, |
| false); // No number check. |
| BranchInstr* branch = new BranchInstr(comp); |
| AddInstruction(branch); |
| CloseFragment(); |
| |
| true_successor_addresses_.Add(branch->true_successor_address()); |
| false_successor_addresses_.Add(branch->false_successor_address()); |
| } |
| |
| |
| void TestGraphVisitor::MergeBranchWithComparison(ComparisonInstr* comp) { |
| BranchInstr* branch; |
| if (Token::IsStrictEqualityOperator(comp->kind())) { |
| ASSERT(comp->IsStrictCompare()); |
| branch = new BranchInstr(comp); |
| } else if (Token::IsEqualityOperator(comp->kind()) && |
| (comp->left()->BindsToConstantNull() || |
| comp->right()->BindsToConstantNull())) { |
| branch = new BranchInstr(new StrictCompareInstr( |
| comp->token_pos(), |
| (comp->kind() == Token::kEQ) ? Token::kEQ_STRICT : Token::kNE_STRICT, |
| comp->left(), |
| comp->right(), |
| false)); // No number check. |
| } else { |
| branch = new BranchInstr(comp); |
| branch->set_is_checked(FLAG_enable_type_checks); |
| } |
| AddInstruction(branch); |
| CloseFragment(); |
| true_successor_addresses_.Add(branch->true_successor_address()); |
| false_successor_addresses_.Add(branch->false_successor_address()); |
| } |
| |
| |
| void TestGraphVisitor::MergeBranchWithNegate(BooleanNegateInstr* neg) { |
| ASSERT(!FLAG_enable_type_checks); |
| Value* constant_true = Bind(new ConstantInstr(Bool::True())); |
| StrictCompareInstr* comp = |
| new StrictCompareInstr(condition_token_pos(), |
| Token::kNE_STRICT, |
| neg->value(), |
| constant_true, |
| false); // No number check. |
| BranchInstr* branch = new BranchInstr(comp); |
| AddInstruction(branch); |
| CloseFragment(); |
| true_successor_addresses_.Add(branch->true_successor_address()); |
| false_successor_addresses_.Add(branch->false_successor_address()); |
| } |
| |
| |
| void TestGraphVisitor::ReturnDefinition(Definition* definition) { |
| ComparisonInstr* comp = definition->AsComparison(); |
| if (comp != NULL) { |
| MergeBranchWithComparison(comp); |
| return; |
| } |
| if (!FLAG_enable_type_checks) { |
| BooleanNegateInstr* neg = definition->AsBooleanNegate(); |
| if (neg != NULL) { |
| MergeBranchWithNegate(neg); |
| return; |
| } |
| } |
| ReturnValue(Bind(definition)); |
| } |
| |
| |
| // Special handling for AND/OR. |
| void TestGraphVisitor::VisitBinaryOpNode(BinaryOpNode* node) { |
| // Operators "&&" and "||" cannot be overloaded therefore do not call |
| // operator. |
| if ((node->kind() == Token::kAND) || (node->kind() == Token::kOR)) { |
| TestGraphVisitor for_left(owner(), node->left()->token_pos()); |
| node->left()->Visit(&for_left); |
| |
| TestGraphVisitor for_right(owner(), node->right()->token_pos()); |
| node->right()->Visit(&for_right); |
| |
| Append(for_left); |
| |
| if (node->kind() == Token::kAND) { |
| AppendFragment(for_left.CreateTrueSuccessor(), for_right); |
| true_successor_addresses_.AddArray(for_right.true_successor_addresses_); |
| false_successor_addresses_.AddArray(for_left.false_successor_addresses_); |
| false_successor_addresses_.AddArray(for_right.false_successor_addresses_); |
| } else { |
| ASSERT(node->kind() == Token::kOR); |
| AppendFragment(for_left.CreateFalseSuccessor(), for_right); |
| false_successor_addresses_.AddArray(for_right.false_successor_addresses_); |
| true_successor_addresses_.AddArray(for_left.true_successor_addresses_); |
| true_successor_addresses_.AddArray(for_right.true_successor_addresses_); |
| } |
| CloseFragment(); |
| return; |
| } |
| ValueGraphVisitor::VisitBinaryOpNode(node); |
| } |
| |
| |
| void EffectGraphVisitor::Bailout(const char* reason) { |
| owner()->Bailout(reason); |
| } |
| |
| |
| void EffectGraphVisitor::InlineBailout(const char* reason) { |
| owner()->parsed_function()->function().set_is_inlinable(false); |
| if (owner()->IsInlining()) owner()->Bailout(reason); |
| } |
| |
| |
| // <Statement> ::= Return { value: <Expression> |
| // inlined_finally_list: <InlinedFinally>* } |
| void EffectGraphVisitor::VisitReturnNode(ReturnNode* node) { |
| ValueGraphVisitor for_value(owner()); |
| node->value()->Visit(&for_value); |
| Append(for_value); |
| Value* return_value = for_value.value(); |
| |
| if (node->inlined_finally_list_length() > 0) { |
| LocalVariable* temp = node->saved_return_value_var(); |
| Do(BuildStoreLocal(*temp, return_value)); |
| for (intptr_t i = 0; i < node->inlined_finally_list_length(); i++) { |
| InlineBailout("EffectGraphVisitor::VisitReturnNode (exception)"); |
| EffectGraphVisitor for_effect(owner()); |
| node->InlinedFinallyNodeAt(i)->Visit(&for_effect); |
| Append(for_effect); |
| if (!is_open()) { |
| return; |
| } |
| } |
| return_value = Bind(BuildLoadLocal(*temp)); |
| } |
| |
| // Call to stub that checks whether the debugger is in single |
| // step mode. This call must happen before the contexts are |
| // unchained so that captured variables can be inspected. |
| // No debugger check is done in native functions or for return |
| // statements for which there is no associated source position. |
| const Function& function = owner()->parsed_function()->function(); |
| if ((node->token_pos() != Scanner::kNoSourcePos) && |
| !function.is_native()) { |
| AddInstruction(new DebugStepCheckInstr(node->token_pos(), |
| PcDescriptors::kReturn)); |
| } |
| |
| if (FLAG_enable_type_checks) { |
| const bool is_implicit_dynamic_getter = |
| (!function.is_static() && |
| ((function.kind() == RawFunction::kImplicitGetter) || |
| (function.kind() == RawFunction::kImplicitStaticFinalGetter))); |
| // Implicit getters do not need a type check at return, unless they compute |
| // the initial value of a static field. |
| // The body of a constructor cannot modify the type of the |
| // constructed instance, which is passed in as an implicit parameter. |
| // However, factories may create an instance of the wrong type. |
| if (!is_implicit_dynamic_getter && !function.IsConstructor()) { |
| const AbstractType& dst_type = |
| AbstractType::ZoneHandle(function.result_type()); |
| return_value = BuildAssignableValue(node->value()->token_pos(), |
| return_value, |
| dst_type, |
| Symbols::FunctionResult()); |
| } |
| } |
| |
| intptr_t current_context_level = owner()->context_level(); |
| ASSERT(current_context_level >= 0); |
| if (owner()->parsed_function()->saved_entry_context_var() != NULL) { |
| // CTX on entry was saved, but not linked as context parent. |
| BuildRestoreContext(*owner()->parsed_function()->saved_entry_context_var()); |
| } else { |
| UnchainContexts(current_context_level); |
| } |
| |
| AddReturnExit(node->token_pos(), return_value); |
| } |
| |
| |
| // <Expression> ::= Literal { literal: Instance } |
| void EffectGraphVisitor::VisitLiteralNode(LiteralNode* node) { |
| ReturnDefinition(new ConstantInstr(node->literal())); |
| } |
| |
| |
| // Type nodes are used when a type is referenced as a literal. Type nodes |
| // can also be used for the right-hand side of instanceof comparisons, |
| // but they are handled specially in that context, not here. |
| void EffectGraphVisitor::VisitTypeNode(TypeNode* node) { |
| return; |
| } |
| |
| |
| void ValueGraphVisitor::VisitTypeNode(TypeNode* node) { |
| const AbstractType& type = node->type(); |
| // Type may be malbounded, but not malformed. |
| ASSERT(type.IsFinalized() && !type.IsMalformed()); |
| if (type.IsInstantiated()) { |
| ReturnDefinition(new ConstantInstr(type)); |
| } else { |
| const Class& instantiator_class = Class::ZoneHandle( |
| owner()->parsed_function()->function().Owner()); |
| Value* instantiator_value = BuildInstantiatorTypeArguments( |
| node->token_pos(), instantiator_class, NULL); |
| ReturnDefinition(new InstantiateTypeInstr( |
| node->token_pos(), type, instantiator_class, instantiator_value)); |
| } |
| } |
| |
| |
| // Returns true if the type check can be skipped, for example, if the |
| // destination type is dynamic or if the compile type of the value is a subtype |
| // of the destination type. |
| bool EffectGraphVisitor::CanSkipTypeCheck(intptr_t token_pos, |
| Value* value, |
| const AbstractType& dst_type, |
| const String& dst_name) { |
| ASSERT(!dst_type.IsNull()); |
| ASSERT(dst_type.IsFinalized()); |
| |
| // If the destination type is malformed or malbounded, a dynamic type error |
| // must be thrown at run time. |
| if (dst_type.IsMalformedOrMalbounded()) { |
| return false; |
| } |
| |
| // Any type is more specific than the dynamic type and than the Object type. |
| if (dst_type.IsDynamicType() || dst_type.IsObjectType()) { |
| return true; |
| } |
| |
| // Do not perform type check elimination if this optimization is turned off. |
| if (!FLAG_eliminate_type_checks) { |
| return false; |
| } |
| |
| // If nothing is known about the value, as is the case for passed-in |
| // parameters, and since dst_type is not one of the tested cases above, then |
| // the type test cannot be eliminated. |
| if (value == NULL) { |
| return false; |
| } |
| |
| const bool eliminated = value->Type()->IsAssignableTo(dst_type); |
| if (FLAG_trace_type_check_elimination) { |
| FlowGraphPrinter::PrintTypeCheck(*owner()->parsed_function(), |
| token_pos, |
| value, |
| dst_type, |
| dst_name, |
| eliminated); |
| } |
| return eliminated; |
| } |
| |
| |
| // <Expression> :: Assignable { expr: <Expression> |
| // type: AbstractType |
| // dst_name: String } |
| void EffectGraphVisitor::VisitAssignableNode(AssignableNode* node) { |
| ValueGraphVisitor for_value(owner()); |
| node->expr()->Visit(&for_value); |
| Append(for_value); |
| Definition* checked_value; |
| if (CanSkipTypeCheck(node->expr()->token_pos(), |
| for_value.value(), |
| node->type(), |
| node->dst_name())) { |
| // Drop the value and 0 additional temporaries. |
| checked_value = new DropTempsInstr(0, for_value.value()); |
| } else { |
| checked_value = BuildAssertAssignable(node->expr()->token_pos(), |
| for_value.value(), |
| node->type(), |
| node->dst_name()); |
| } |
| ReturnDefinition(checked_value); |
| } |
| |
| |
| void ValueGraphVisitor::VisitAssignableNode(AssignableNode* node) { |
| ValueGraphVisitor for_value(owner()); |
| node->expr()->Visit(&for_value); |
| Append(for_value); |
| ReturnValue(BuildAssignableValue(node->expr()->token_pos(), |
| for_value.value(), |
| node->type(), |
| node->dst_name())); |
| } |
| |
| |
| // <Expression> :: BinaryOp { kind: Token::Kind |
| // left: <Expression> |
| // right: <Expression> } |
| void EffectGraphVisitor::VisitBinaryOpNode(BinaryOpNode* node) { |
| // Operators "&&" and "||" cannot be overloaded therefore do not call |
| // operator. |
| if ((node->kind() == Token::kAND) || (node->kind() == Token::kOR)) { |
| // See ValueGraphVisitor::VisitBinaryOpNode. |
| TestGraphVisitor for_left(owner(), node->left()->token_pos()); |
| node->left()->Visit(&for_left); |
| EffectGraphVisitor empty(owner()); |
| if (FLAG_enable_type_checks) { |
| ValueGraphVisitor for_right(owner()); |
| node->right()->Visit(&for_right); |
| Value* right_value = for_right.value(); |
| for_right.Do(new AssertBooleanInstr(node->right()->token_pos(), |
| right_value)); |
| if (node->kind() == Token::kAND) { |
| Join(for_left, for_right, empty); |
| } else { |
| Join(for_left, empty, for_right); |
| } |
| } else { |
| EffectGraphVisitor for_right(owner()); |
| node->right()->Visit(&for_right); |
| if (node->kind() == Token::kAND) { |
| Join(for_left, for_right, empty); |
| } else { |
| Join(for_left, empty, for_right); |
| } |
| } |
| return; |
| } |
| ValueGraphVisitor for_left_value(owner()); |
| node->left()->Visit(&for_left_value); |
| Append(for_left_value); |
| PushArgumentInstr* push_left = PushArgument(for_left_value.value()); |
| |
| ValueGraphVisitor for_right_value(owner()); |
| node->right()->Visit(&for_right_value); |
| Append(for_right_value); |
| PushArgumentInstr* push_right = PushArgument(for_right_value.value()); |
| |
| ZoneGrowableArray<PushArgumentInstr*>* arguments = |
| new ZoneGrowableArray<PushArgumentInstr*>(2); |
| arguments->Add(push_left); |
| arguments->Add(push_right); |
| const String& name = String::ZoneHandle(Symbols::New(node->TokenName())); |
| const intptr_t kNumArgsChecked = 2; |
| InstanceCallInstr* call = new InstanceCallInstr(node->token_pos(), |
| name, |
| node->kind(), |
| arguments, |
| Object::null_array(), |
| kNumArgsChecked, |
| owner()->ic_data_array()); |
| ReturnDefinition(call); |
| } |
| |
| |
| // Special handling for AND/OR. |
| void ValueGraphVisitor::VisitBinaryOpNode(BinaryOpNode* node) { |
| // Operators "&&" and "||" cannot be overloaded therefore do not call |
| // operator. |
| if ((node->kind() == Token::kAND) || (node->kind() == Token::kOR)) { |
| // Implement short-circuit logic: do not evaluate right if evaluation |
| // of left is sufficient. |
| // AND: left ? right === true : false; |
| // OR: left ? true : right === true; |
| |
| TestGraphVisitor for_test(owner(), node->left()->token_pos()); |
| node->left()->Visit(&for_test); |
| |
| ValueGraphVisitor for_right(owner()); |
| node->right()->Visit(&for_right); |
| Value* right_value = for_right.value(); |
| if (FLAG_enable_type_checks) { |
| right_value = |
| for_right.Bind(new AssertBooleanInstr(node->right()->token_pos(), |
| right_value)); |
| } |
| Value* constant_true = for_right.Bind(new ConstantInstr(Bool::True())); |
| Value* compare = |
| for_right.Bind(new StrictCompareInstr(node->token_pos(), |
| Token::kEQ_STRICT, |
| right_value, |
| constant_true, |
| false)); // No number check. |
| for_right.Do(BuildStoreExprTemp(compare)); |
| |
| if (node->kind() == Token::kAND) { |
| ValueGraphVisitor for_false(owner()); |
| Value* constant_false = for_false.Bind(new ConstantInstr(Bool::False())); |
| for_false.Do(BuildStoreExprTemp(constant_false)); |
| Join(for_test, for_right, for_false); |
| } else { |
| ASSERT(node->kind() == Token::kOR); |
| ValueGraphVisitor for_true(owner()); |
| Value* constant_true = for_true.Bind(new ConstantInstr(Bool::True())); |
| for_true.Do(BuildStoreExprTemp(constant_true)); |
| Join(for_test, for_true, for_right); |
| } |
| ReturnDefinition(BuildLoadExprTemp()); |
| return; |
| } |
| EffectGraphVisitor::VisitBinaryOpNode(node); |
| } |
| |
| |
| static const String& BinaryOpAndMaskName(BinaryOpNode* node) { |
| if (node->kind() == Token::kSHL) { |
| return Library::PrivateCoreLibName(Symbols::_leftShiftWithMask32()); |
| } |
| UNIMPLEMENTED(); |
| return String::ZoneHandle(); |
| } |
| |
| |
| // <Expression> :: BinaryOp { kind: Token::Kind |
| // left: <Expression> |
| // right: <Expression> |
| // mask32: constant } |
| void EffectGraphVisitor::VisitBinaryOpWithMask32Node( |
| BinaryOpWithMask32Node* node) { |
| ASSERT((node->kind() != Token::kAND) && (node->kind() != Token::kOR)); |
| ValueGraphVisitor for_left_value(owner()); |
| node->left()->Visit(&for_left_value); |
| Append(for_left_value); |
| PushArgumentInstr* push_left = PushArgument(for_left_value.value()); |
| |
| ValueGraphVisitor for_right_value(owner()); |
| node->right()->Visit(&for_right_value); |
| Append(for_right_value); |
| PushArgumentInstr* push_right = PushArgument(for_right_value.value()); |
| |
| Value* mask_value = Bind(new ConstantInstr( |
| Integer::ZoneHandle(Integer::New(node->mask32(), Heap::kOld)))); |
| PushArgumentInstr* push_mask = PushArgument(mask_value); |
| |
| ZoneGrowableArray<PushArgumentInstr*>* arguments = |
| new ZoneGrowableArray<PushArgumentInstr*>(3); |
| arguments->Add(push_left); |
| arguments->Add(push_right); |
| // Call to special method 'BinaryOpAndMaskName(node)'. |
| arguments->Add(push_mask); |
| const intptr_t kNumArgsChecked = 2; |
| InstanceCallInstr* call = new InstanceCallInstr(node->token_pos(), |
| BinaryOpAndMaskName(node), |
| Token::kILLEGAL, |
| arguments, |
| Object::null_array(), |
| kNumArgsChecked, |
| owner()->ic_data_array()); |
| ReturnDefinition(call); |
| } |
| |
| |
| void EffectGraphVisitor::BuildTypecheckPushArguments( |
| intptr_t token_pos, |
| PushArgumentInstr** push_instantiator_result, |
| PushArgumentInstr** push_instantiator_type_arguments_result) { |
| const Class& instantiator_class = Class::Handle( |
| owner()->parsed_function()->function().Owner()); |
| // Since called only when type tested against is not instantiated. |
| ASSERT(instantiator_class.NumTypeParameters() > 0); |
| Value* instantiator_type_arguments = NULL; |
| Value* instantiator = BuildInstantiator(instantiator_class); |
| if (instantiator == NULL) { |
| // No instantiator when inside factory. |
| *push_instantiator_result = PushArgument(BuildNullValue()); |
| instantiator_type_arguments = |
| BuildInstantiatorTypeArguments(token_pos, instantiator_class, NULL); |
| } else { |
| instantiator = Bind(BuildStoreExprTemp(instantiator)); |
| *push_instantiator_result = PushArgument(instantiator); |
| Value* loaded = Bind(BuildLoadExprTemp()); |
| instantiator_type_arguments = |
| BuildInstantiatorTypeArguments(token_pos, instantiator_class, loaded); |
| } |
| *push_instantiator_type_arguments_result = |
| PushArgument(instantiator_type_arguments); |
| } |
| |
| |
| |
| void EffectGraphVisitor::BuildTypecheckArguments( |
| intptr_t token_pos, |
| Value** instantiator_result, |
| Value** instantiator_type_arguments_result) { |
| Value* instantiator = NULL; |
| Value* instantiator_type_arguments = NULL; |
| const Class& instantiator_class = Class::Handle( |
| owner()->parsed_function()->function().Owner()); |
| // Since called only when type tested against is not instantiated. |
| ASSERT(instantiator_class.NumTypeParameters() > 0); |
| instantiator = BuildInstantiator(instantiator_class); |
| if (instantiator == NULL) { |
| // No instantiator when inside factory. |
| instantiator = BuildNullValue(); |
| instantiator_type_arguments = |
| BuildInstantiatorTypeArguments(token_pos, instantiator_class, NULL); |
| } else { |
| // Preserve instantiator. |
| instantiator = Bind(BuildStoreExprTemp(instantiator)); |
| Value* loaded = Bind(BuildLoadExprTemp()); |
| instantiator_type_arguments = |
| BuildInstantiatorTypeArguments(token_pos, instantiator_class, loaded); |
| } |
| *instantiator_result = instantiator; |
| *instantiator_type_arguments_result = instantiator_type_arguments; |
| } |
| |
| |
| Value* EffectGraphVisitor::BuildNullValue() { |
| return Bind(new ConstantInstr(Object::ZoneHandle())); |
| } |
| |
| |
| // Used for testing incoming arguments. |
| AssertAssignableInstr* EffectGraphVisitor::BuildAssertAssignable( |
| intptr_t token_pos, |
| Value* value, |
| const AbstractType& dst_type, |
| const String& dst_name) { |
| // Build the type check computation. |
| Value* instantiator = NULL; |
| Value* instantiator_type_arguments = NULL; |
| if (dst_type.IsInstantiated()) { |
| instantiator = BuildNullValue(); |
| instantiator_type_arguments = BuildNullValue(); |
| } else { |
| BuildTypecheckArguments(token_pos, |
| &instantiator, |
| &instantiator_type_arguments); |
| } |
| return new AssertAssignableInstr(token_pos, |
| value, |
| instantiator, |
| instantiator_type_arguments, |
| dst_type, |
| dst_name); |
| } |
| |
| |
| // Used for type casts and to test assignments. |
| Value* EffectGraphVisitor::BuildAssignableValue(intptr_t token_pos, |
| Value* value, |
| const AbstractType& dst_type, |
| const String& dst_name) { |
| if (CanSkipTypeCheck(token_pos, value, dst_type, dst_name)) { |
| return value; |
| } |
| return Bind(BuildAssertAssignable(token_pos, value, dst_type, dst_name)); |
| } |
| |
| |
| void EffectGraphVisitor::BuildTypeTest(ComparisonNode* node) { |
| ASSERT(Token::IsTypeTestOperator(node->kind())); |
| const AbstractType& type = node->right()->AsTypeNode()->type(); |
| ASSERT(type.IsFinalized() && !type.IsMalformedOrMalbounded()); |
| const bool negate_result = (node->kind() == Token::kISNOT); |
| // All objects are instances of type T if Object type is a subtype of type T. |
| const Type& object_type = Type::Handle(Type::ObjectType()); |
| if (type.IsInstantiated() && object_type.IsSubtypeOf(type, NULL)) { |
| // Must evaluate left side. |
| EffectGraphVisitor for_left_value(owner()); |
| node->left()->Visit(&for_left_value); |
| Append(for_left_value); |
| ReturnDefinition(new ConstantInstr(Bool::Get(!negate_result))); |
| return; |
| } |
| ValueGraphVisitor for_left_value(owner()); |
| node->left()->Visit(&for_left_value); |
| Append(for_left_value); |
| PushArgumentInstr* push_left = PushArgument(for_left_value.value()); |
| PushArgumentInstr* push_instantiator = NULL; |
| PushArgumentInstr* push_type_args = NULL; |
| if (type.IsInstantiated()) { |
| push_instantiator = PushArgument(BuildNullValue()); |
| push_type_args = PushArgument(BuildNullValue()); |
| } else { |
| BuildTypecheckPushArguments(node->token_pos(), |
| &push_instantiator, |
| &push_type_args); |
| } |
| ZoneGrowableArray<PushArgumentInstr*>* arguments = |
| new ZoneGrowableArray<PushArgumentInstr*>(5); |
| arguments->Add(push_left); |
| arguments->Add(push_instantiator); |
| arguments->Add(push_type_args); |
| ASSERT(!node->right()->AsTypeNode()->type().IsNull()); |
| Value* type_arg = Bind( |
| new ConstantInstr(node->right()->AsTypeNode()->type())); |
| arguments->Add(PushArgument(type_arg)); |
| const Bool& negate = Bool::Get(node->kind() == Token::kISNOT); |
| Value* negate_arg = Bind(new ConstantInstr(negate)); |
| arguments->Add(PushArgument(negate_arg)); |
| const intptr_t kNumArgsChecked = 1; |
| InstanceCallInstr* call = new InstanceCallInstr( |
| node->token_pos(), |
| Library::PrivateCoreLibName(Symbols::_instanceOf()), |
| node->kind(), |
| arguments, |
| Object::null_array(), // No argument names. |
| kNumArgsChecked, |
| owner()->ic_data_array()); |
| ReturnDefinition(call); |
| } |
| |
| |
| void EffectGraphVisitor::BuildTypeCast(ComparisonNode* node) { |
| ASSERT(Token::IsTypeCastOperator(node->kind())); |
| ASSERT(!node->right()->AsTypeNode()->type().IsNull()); |
| const AbstractType& type = node->right()->AsTypeNode()->type(); |
| ASSERT(type.IsFinalized() && !type.IsMalformed() && !type.IsMalbounded()); |
| ValueGraphVisitor for_value(owner()); |
| node->left()->Visit(&for_value); |
| Append(for_value); |
| const String& dst_name = String::ZoneHandle( |
| Symbols::New(Exceptions::kCastErrorDstName)); |
| if (CanSkipTypeCheck(node->token_pos(), |
| for_value.value(), |
| type, |
| dst_name)) { |
| ReturnValue(for_value.value()); |
| return; |
| } |
| PushArgumentInstr* push_left = PushArgument(for_value.value()); |
| PushArgumentInstr* push_instantiator = NULL; |
| PushArgumentInstr* push_type_args = NULL; |
| if (type.IsInstantiated()) { |
| push_instantiator = PushArgument(BuildNullValue()); |
| push_type_args = PushArgument(BuildNullValue()); |
| } else { |
| BuildTypecheckPushArguments(node->token_pos(), |
| &push_instantiator, |
| &push_type_args); |
| } |
| ZoneGrowableArray<PushArgumentInstr*>* arguments = |
| new ZoneGrowableArray<PushArgumentInstr*>(4); |
| arguments->Add(push_left); |
| arguments->Add(push_instantiator); |
| arguments->Add(push_type_args); |
| Value* type_arg = Bind(new ConstantInstr(type)); |
| arguments->Add(PushArgument(type_arg)); |
| const intptr_t kNumArgsChecked = 1; |
| InstanceCallInstr* call = new InstanceCallInstr( |
| node->token_pos(), |
| Library::PrivateCoreLibName(Symbols::_as()), |
| node->kind(), |
| arguments, |
| Object::null_array(), // No argument names. |
| kNumArgsChecked, |
| owner()->ic_data_array()); |
| ReturnDefinition(call); |
| } |
| |
| |
| StrictCompareInstr* EffectGraphVisitor::BuildStrictCompare(AstNode* left, |
| AstNode* right, |
| Token::Kind kind, |
| intptr_t token_pos) { |
| ValueGraphVisitor for_left_value(owner()); |
| left->Visit(&for_left_value); |
| Append(for_left_value); |
| ValueGraphVisitor for_right_value(owner()); |
| right->Visit(&for_right_value); |
| Append(for_right_value); |
| StrictCompareInstr* comp = new StrictCompareInstr(token_pos, |
| kind, |
| for_left_value.value(), |
| for_right_value.value(), |
| true); // Number check. |
| return comp; |
| } |
| |
| |
| // <Expression> :: Comparison { kind: Token::Kind |
| // left: <Expression> |
| // right: <Expression> } |
| // TODO(srdjan): Implement new equality. |
| void EffectGraphVisitor::VisitComparisonNode(ComparisonNode* node) { |
| if (Token::IsTypeTestOperator(node->kind())) { |
| BuildTypeTest(node); |
| return; |
| } |
| if (Token::IsTypeCastOperator(node->kind())) { |
| BuildTypeCast(node); |
| return; |
| } |
| |
| if ((node->kind() == Token::kEQ_STRICT) || |
| (node->kind() == Token::kNE_STRICT)) { |
| ReturnDefinition(BuildStrictCompare(node->left(), node->right(), |
| node->kind(), node->token_pos())); |
| return; |
| } |
| |
| if ((node->kind() == Token::kEQ) || (node->kind() == Token::kNE)) { |
| // Eagerly fold null-comparisons. |
| LiteralNode* left_lit = node->left()->AsLiteralNode(); |
| LiteralNode* right_lit = node->right()->AsLiteralNode(); |
| if (((left_lit != NULL) && left_lit->literal().IsNull()) || |
| ((right_lit != NULL) && right_lit->literal().IsNull())) { |
| Token::Kind kind = |
| (node->kind() == Token::kEQ) ? Token::kEQ_STRICT : Token::kNE_STRICT; |
| StrictCompareInstr* compare = |
| BuildStrictCompare(node->left(), node->right(), |
| kind, node->token_pos()); |
| ReturnDefinition(compare); |
| return; |
| } |
| |
| ZoneGrowableArray<PushArgumentInstr*>* arguments = |
| new ZoneGrowableArray<PushArgumentInstr*>(2); |
| |
| ValueGraphVisitor for_left_value(owner()); |
| node->left()->Visit(&for_left_value); |
| Append(for_left_value); |
| PushArgumentInstr* push_left = PushArgument(for_left_value.value()); |
| arguments->Add(push_left); |
| |
| ValueGraphVisitor for_right_value(owner()); |
| node->right()->Visit(&for_right_value); |
| Append(for_right_value); |
| PushArgumentInstr* push_right = PushArgument(for_right_value.value()); |
| arguments->Add(push_right); |
| |
| Definition* result = |
| new InstanceCallInstr(node->token_pos(), |
| Symbols::EqualOperator(), |
| Token::kEQ, // Result is negated later for kNE. |
| arguments, |
| Object::null_array(), |
| 2, |
| owner()->ic_data_array()); |
| if (node->kind() == Token::kNE) { |
| if (FLAG_enable_type_checks) { |
| Value* value = Bind(result); |
| result = new AssertBooleanInstr(node->token_pos(), value); |
| } |
| Value* value = Bind(result); |
| result = new BooleanNegateInstr(value); |
| } |
| ReturnDefinition(result); |
| return; |
| } |
| |
| ZoneGrowableArray<PushArgumentInstr*>* arguments = |
| new ZoneGrowableArray<PushArgumentInstr*>(2); |
| |
| ValueGraphVisitor for_left_value(owner()); |
| node->left()->Visit(&for_left_value); |
| Append(for_left_value); |
| PushArgumentInstr* push_left = PushArgument(for_left_value.value()); |
| arguments->Add(push_left); |
| |
| ValueGraphVisitor for_right_value(owner()); |
| node->right()->Visit(&for_right_value); |
| Append(for_right_value); |
| PushArgumentInstr* push_right = PushArgument(for_right_value.value()); |
| arguments->Add(push_right); |
| |
| ASSERT(Token::IsRelationalOperator(node->kind())); |
| InstanceCallInstr* comp = |
| new InstanceCallInstr(node->token_pos(), |
| String::ZoneHandle(Symbols::New(node->TokenName())), |
| node->kind(), |
| arguments, |
| Object::null_array(), |
| 2, |
| owner()->ic_data_array()); |
| ReturnDefinition(comp); |
| } |
| |
| |
| void EffectGraphVisitor::VisitUnaryOpNode(UnaryOpNode* node) { |
| // "!" cannot be overloaded, therefore do not call operator. |
| if (node->kind() == Token::kNOT) { |
| ValueGraphVisitor for_value(owner()); |
| node->operand()->Visit(&for_value); |
| Append(for_value); |
| Value* value = for_value.value(); |
| if (FLAG_enable_type_checks) { |
| value = |
| Bind(new AssertBooleanInstr(node->operand()->token_pos(), value)); |
| } |
| BooleanNegateInstr* negate = new BooleanNegateInstr(value); |
| ReturnDefinition(negate); |
| return; |
| } |
| |
| ValueGraphVisitor for_value(owner()); |
| node->operand()->Visit(&for_value); |
| Append(for_value); |
| PushArgumentInstr* push_value = PushArgument(for_value.value()); |
| ZoneGrowableArray<PushArgumentInstr*>* arguments = |
| new ZoneGrowableArray<PushArgumentInstr*>(1); |
| arguments->Add(push_value); |
| InstanceCallInstr* call = |
| new InstanceCallInstr(node->token_pos(), |
| String::ZoneHandle(Symbols::New(node->TokenName())), |
| node->kind(), |
| arguments, |
| Object::null_array(), |
| 1, |
| owner()->ic_data_array()); |
| ReturnDefinition(call); |
| } |
| |
| |
| void EffectGraphVisitor::VisitConditionalExprNode(ConditionalExprNode* node) { |
| TestGraphVisitor for_test(owner(), node->condition()->token_pos()); |
| node->condition()->Visit(&for_test); |
| |
| // Translate the subexpressions for their effects. |
| EffectGraphVisitor for_true(owner()); |
| node->true_expr()->Visit(&for_true); |
| EffectGraphVisitor for_false(owner()); |
| node->false_expr()->Visit(&for_false); |
| |
| Join(for_test, for_true, for_false); |
| } |
| |
| |
| void ValueGraphVisitor::VisitConditionalExprNode(ConditionalExprNode* node) { |
| TestGraphVisitor for_test(owner(), node->condition()->token_pos()); |
| node->condition()->Visit(&for_test); |
| |
| ValueGraphVisitor for_true(owner()); |
| node->true_expr()->Visit(&for_true); |
| ASSERT(for_true.is_open()); |
| for_true.Do(BuildStoreExprTemp(for_true.value())); |
| |
| ValueGraphVisitor for_false(owner()); |
| node->false_expr()->Visit(&for_false); |
| ASSERT(for_false.is_open()); |
| for_false.Do(BuildStoreExprTemp(for_false.value())); |
| |
| Join(for_test, for_true, for_false); |
| ReturnDefinition(BuildLoadExprTemp()); |
| } |
| |
| |
| // <Statement> ::= If { condition: <Expression> |
| // true_branch: <Sequence> |
| // false_branch: <Sequence> } |
| void EffectGraphVisitor::VisitIfNode(IfNode* node) { |
| TestGraphVisitor for_test(owner(), node->condition()->token_pos()); |
| node->condition()->Visit(&for_test); |
| |
| EffectGraphVisitor for_true(owner()); |
| EffectGraphVisitor for_false(owner()); |
| |
| node->true_branch()->Visit(&for_true); |
| // The for_false graph fragment will be empty (default graph fragment) if |
| // we do not call Visit. |
| if (node->false_branch() != NULL) node->false_branch()->Visit(&for_false); |
| Join(for_test, for_true, for_false); |
| } |
| |
| |
| void EffectGraphVisitor::VisitSwitchNode(SwitchNode* node) { |
| NestedSwitch nested_switch(owner(), node); |
| EffectGraphVisitor switch_body(owner()); |
| node->body()->Visit(&switch_body); |
| Append(switch_body); |
| if (nested_switch.break_target() != NULL) { |
| if (is_open()) Goto(nested_switch.break_target()); |
| exit_ = nested_switch.break_target(); |
| } |
| } |
| |
| |
| // A case node contains zero or more case expressions, can contain default |
| // and a case statement body. |
| // Compose fragment as follows: |
| // - if no case expressions, must have default: |
| // a) target |
| // b) [ case-statements ] |
| // |
| // - if has 1 or more case statements |
| // a) target-0 |
| // b) [ case-expression-0 ] -> (true-target-0, target-1) |
| // c) target-1 |
| // d) [ case-expression-1 ] -> (true-target-1, exit-target) |
| // e) true-target-0 -> case-statements-join |
| // f) true-target-1 -> case-statements-join |
| // g) case-statements-join |
| // h) [ case-statements ] -> exit-join |
| // i) exit-target -> exit-join |
| // j) exit-join |
| // |
| // Note: The specification of switch/case is under discussion and may change |
| // drastically. |
| void EffectGraphVisitor::VisitCaseNode(CaseNode* node) { |
| const intptr_t len = node->case_expressions()->length(); |
| // Create case statements instructions. |
| EffectGraphVisitor for_case_statements(owner()); |
| // Compute the start of the statements fragment. |
| JoinEntryInstr* statement_start = NULL; |
| if (node->label() == NULL) { |
| statement_start = new JoinEntryInstr(owner()->AllocateBlockId(), |
| owner()->try_index()); |
| } else { |
| // The case nodes are nested inside a SequenceNode that is the body of a |
| // SwitchNode. The SwitchNode on the nesting stack contains the |
| // continue labels for all the case clauses. |
| statement_start = |
| owner()->nesting_stack()->outer()->ContinueTargetFor(node->label()); |
| } |
| ASSERT(statement_start != NULL); |
| node->statements()->Visit(&for_case_statements); |
| Instruction* statement_exit = |
| AppendFragment(statement_start, for_case_statements); |
| if (is_open() && (len == 0)) { |
| ASSERT(node->contains_default()); |
| // Default only case node. |
| Goto(statement_start); |
| exit_ = statement_exit; |
| return; |
| } |
| |
| // Generate instructions for all case expressions. |
| TargetEntryInstr* next_target = NULL; |
| for (intptr_t i = 0; i < len; i++) { |
| AstNode* case_expr = node->case_expressions()->NodeAt(i); |
| TestGraphVisitor for_case_expression(owner(), case_expr->token_pos()); |
| case_expr->Visit(&for_case_expression); |
| if (i == 0) { |
| // Append only the first one, everything else is connected from it. |
| Append(for_case_expression); |
| } else { |
| ASSERT(next_target != NULL); |
| AppendFragment(next_target, for_case_expression); |
| } |
| for_case_expression.IfTrueGoto(statement_start); |
| next_target = for_case_expression.CreateFalseSuccessor()->AsTargetEntry(); |
| } |
| |
| // Once a test fragment has been added, this fragment is closed. |
| ASSERT(!is_open()); |
| |
| Instruction* exit_instruction = NULL; |
| // Handle last (or only) case: false goes to exit or to statement if this |
| // node contains default. |
| if (len > 0) { |
| ASSERT(next_target != NULL); |
| if (node->contains_default()) { |
| // True and false go to statement start. |
| next_target->Goto(statement_start); |
| exit_instruction = statement_exit; |
| } else { |
| if (statement_exit != NULL) { |
| JoinEntryInstr* join = new JoinEntryInstr(owner()->AllocateBlockId(), |
| owner()->try_index()); |
| statement_exit->Goto(join); |
| next_target->Goto(join); |
| exit_instruction = join; |
| } else { |
| exit_instruction = next_target; |
| } |
| } |
| } else { |
| // A CaseNode without case expressions must contain default. |
| ASSERT(node->contains_default()); |
| Goto(statement_start); |
| exit_instruction = statement_exit; |
| } |
| |
| ASSERT(!is_open()); |
| exit_ = exit_instruction; |
| } |
| |
| |
| // <Statement> ::= While { label: SourceLabel |
| // condition: <Expression> |
| // body: <Sequence> } |
| // The fragment is composed as follows: |
| // a) loop-join |
| // b) [ test ] -> (body-entry-target, loop-exit-target) |
| // c) body-entry-target |
| // d) [ body ] -> (continue-join) |
| // e) continue-join -> (loop-join) |
| // f) loop-exit-target |
| // g) break-join (optional) |
| void EffectGraphVisitor::VisitWhileNode(WhileNode* node) { |
| NestedLoop nested_loop(owner(), node->label()); |
| |
| TestGraphVisitor for_test(owner(), node->condition()->token_pos()); |
| node->condition()->Visit(&for_test); |
| ASSERT(!for_test.is_empty()); // Language spec. |
| |
| EffectGraphVisitor for_body(owner()); |
| node->body()->Visit(&for_body); |
| |
| // Labels are set after body traversal. |
| JoinEntryInstr* join = nested_loop.continue_target(); |
| if (join != NULL) { |
| if (for_body.is_open()) for_body.Goto(join); |
| for_body.exit_ = join; |
| } |
| TieLoop(node->token_pos(), for_test, for_body); |
| join = nested_loop.break_target(); |
| if (join != NULL) { |
| Goto(join); |
| exit_ = join; |
| } |
| } |
| |
| |
| // The fragment is composed as follows: |
| // a) body-entry-join |
| // b) [ body ] |
| // c) test-entry (continue-join or body-exit-target) |
| // d) [ test-entry ] -> (back-target, loop-exit-target) |
| // e) back-target -> (body-entry-join) |
| // f) loop-exit-target |
| // g) break-join |
| void EffectGraphVisitor::VisitDoWhileNode(DoWhileNode* node) { |
| NestedLoop nested_loop(owner(), node->label()); |
| |
| // Traverse the body first in order to generate continue and break labels. |
| EffectGraphVisitor for_body(owner()); |
| node->body()->Visit(&for_body); |
| |
| TestGraphVisitor for_test(owner(), node->condition()->token_pos()); |
| node->condition()->Visit(&for_test); |
| ASSERT(is_open()); |
| |
| // Tie do-while loop (test is after the body). |
| JoinEntryInstr* body_entry_join = |
| new JoinEntryInstr(owner()->AllocateBlockId(), |
| owner()->try_index()); |
| Goto(body_entry_join); |
| Instruction* body_exit = AppendFragment(body_entry_join, for_body); |
| |
| JoinEntryInstr* join = nested_loop.continue_target(); |
| if ((body_exit != NULL) || (join != NULL)) { |
| if (join == NULL) { |
| join = new JoinEntryInstr(owner()->AllocateBlockId(), |
| owner()->try_index()); |
| } |
| CheckStackOverflowInstr* check = |
| new CheckStackOverflowInstr(node->token_pos(), owner()->loop_depth()); |
| join->LinkTo(check); |
| check->LinkTo(for_test.entry()); |
| if (body_exit != NULL) { |
| body_exit->Goto(join); |
| } |
| } |
| |
| for_test.IfTrueGoto(body_entry_join); |
| join = nested_loop.break_target(); |
| if (join == NULL) { |
| exit_ = for_test.CreateFalseSuccessor(); |
| } else { |
| for_test.IfFalseGoto(join); |
| exit_ = join; |
| } |
| } |
| |
| |
| // A ForNode can contain break and continue jumps. 'break' joins to |
| // ForNode exit, 'continue' joins at increment entry. The fragment is composed |
| // as follows: |
| // a) [ initializer ] |
| // b) loop-join |
| // c) [ test ] -> (body-entry-target, loop-exit-target) |
| // d) body-entry-target |
| // e) [ body ] |
| // f) continue-join (optional) |
| // g) [ increment ] -> (loop-join) |
| // h) loop-exit-target |
| // i) break-join |
| void EffectGraphVisitor::VisitForNode(ForNode* node) { |
| EffectGraphVisitor for_initializer(owner()); |
| node->initializer()->Visit(&for_initializer); |
| Append(for_initializer); |
| ASSERT(is_open()); |
| |
| NestedLoop nested_loop(owner(), node->label()); |
| // Compose body to set any jump labels. |
| EffectGraphVisitor for_body(owner()); |
| node->body()->Visit(&for_body); |
| |
| EffectGraphVisitor for_increment(owner()); |
| node->increment()->Visit(&for_increment); |
| |
| // Join the loop body and increment and then tie the loop. |
| JoinEntryInstr* continue_join = nested_loop.continue_target(); |
| if ((continue_join != NULL) || for_body.is_open()) { |
| JoinEntryInstr* loop_entry = |
| new JoinEntryInstr(owner()->AllocateBlockId(), owner()->try_index()); |
| if (continue_join != NULL) { |
| if (for_body.is_open()) for_body.Goto(continue_join); |
| Instruction* current = AppendFragment(continue_join, for_increment); |
| current->Goto(loop_entry); |
| } else { |
| for_body.Append(for_increment); |
| for_body.Goto(loop_entry); |
| } |
| Goto(loop_entry); |
| exit_ = loop_entry; |
| AddInstruction( |
| new CheckStackOverflowInstr(node->token_pos(), owner()->loop_depth())); |
| } |
| |
| if (node->condition() == NULL) { |
| // Endless loop, no test. |
| Append(for_body); |
| exit_ = nested_loop.break_target(); // May be NULL. |
| } else { |
| TestGraphVisitor for_test(owner(), node->condition()->token_pos()); |
| node->condition()->Visit(&for_test); |
| Append(for_test); |
| |
| BlockEntryInstr* body_entry = for_test.CreateTrueSuccessor(); |
| AppendFragment(body_entry, for_body); |
| |
| if (nested_loop.break_target() == NULL) { |
| exit_ = for_test.CreateFalseSuccessor(); |
| } else { |
| for_test.IfFalseGoto(nested_loop.break_target()); |
| exit_ = nested_loop.break_target(); |
| } |
| } |
| } |
| |
| |
| void EffectGraphVisitor::VisitJumpNode(JumpNode* node) { |
| for (intptr_t i = 0; i < node->inlined_finally_list_length(); i++) { |
| EffectGraphVisitor for_effect(owner()); |
| node->InlinedFinallyNodeAt(i)->Visit(&for_effect); |
| Append(for_effect); |
| if (!is_open()) return; |
| } |
| |
| // Unchain the context(s) up to the outer context level of the scope which |
| // contains the destination label. |
| SourceLabel* label = node->label(); |
| ASSERT(label->owner() != NULL); |
| int target_context_level = 0; |
| LocalScope* target_scope = label->owner(); |
| if (target_scope->num_context_variables() > 0) { |
| // The scope of the target label allocates a context, therefore its outer |
| // scope is at a lower context level. |
| target_context_level = target_scope->context_level() - 1; |
| } else { |
| // The scope of the target label does not allocate a context, so its outer |
| // scope is at the same context level. Find it. |
| while ((target_scope != NULL) && |
| (target_scope->num_context_variables() == 0)) { |
| target_scope = target_scope->parent(); |
| } |
| if (target_scope != NULL) { |
| target_context_level = target_scope->context_level(); |
| } |
| } |
| ASSERT(target_context_level >= 0); |
| intptr_t current_context_level = owner()->context_level(); |
| ASSERT(current_context_level >= target_context_level); |
| UnchainContexts(current_context_level - target_context_level); |
| |
| JoinEntryInstr* jump_target = NULL; |
| NestedStatement* current = owner()->nesting_stack(); |
| while (current != NULL) { |
| jump_target = (node->kind() == Token::kBREAK) |
| ? current->BreakTargetFor(node->label()) |
| : current->ContinueTargetFor(node->label()); |
| if (jump_target != NULL) break; |
| current = current->outer(); |
| } |
| ASSERT(jump_target != NULL); |
| Goto(jump_target); |
| } |
| |
| |
| void EffectGraphVisitor::VisitArgumentListNode(ArgumentListNode* node) { |
| UNREACHABLE(); |
| } |
| |
| |
| intptr_t EffectGraphVisitor::GetCurrentTempLocalIndex() const { |
| return kFirstLocalSlotFromFp |
| - owner()->num_stack_locals() |
| - owner()->num_copied_params() |
| - owner()->args_pushed() |
| - owner()->temp_count() + 1; |
| } |
| |
| |
| LocalVariable* EffectGraphVisitor::EnterTempLocalScope(Value* value) { |
| Do(new PushTempInstr(value)); |
| owner()->AllocateTemp(); |
| |
| ASSERT(value->definition()->temp_index() == (owner()->temp_count() - 1)); |
| intptr_t index = GetCurrentTempLocalIndex(); |
| char name[64]; |
| OS::SNPrint(name, 64, ":tmp_local%" Pd, index); |
| LocalVariable* var = |
| new LocalVariable(0, |
| String::ZoneHandle(Symbols::New(name)), |
| *value->Type()->ToAbstractType()); |
| var->set_index(index); |
| return var; |
| } |
| |
| |
| Definition* EffectGraphVisitor::ExitTempLocalScope(LocalVariable* var) { |
| Value* tmp = Bind(new LoadLocalInstr(*var)); |
| owner()->DeallocateTemps(1); |
| ASSERT(GetCurrentTempLocalIndex() == var->index()); |
| return new DropTempsInstr(1, tmp); |
| } |
| |
| |
| void EffectGraphVisitor::BuildLetTempExpressions(LetNode* node) { |
| intptr_t num_temps = node->num_temps(); |
| for (intptr_t i = 0; i < num_temps; ++i) { |
| ValueGraphVisitor for_value(owner()); |
| node->InitializerAt(i)->Visit(&for_value); |
| Append(for_value); |
| Value* temp_val = for_value.value(); |
| node->TempAt(i)->set_index(GetCurrentTempLocalIndex()); |
| Do(new PushTempInstr(temp_val)); |
| owner()->AllocateTemp(); |
| } |
| } |
| |
| |
| void EffectGraphVisitor::VisitLetNode(LetNode* node) { |
| BuildLetTempExpressions(node); |
| |
| // Visit body. |
| for (intptr_t i = 0; i < node->nodes().length(); ++i) { |
| EffectGraphVisitor for_effect(owner()); |
| node->nodes()[i]->Visit(&for_effect); |
| Append(for_effect); |
| } |
| |
| intptr_t num_temps = node->num_temps(); |
| if (num_temps > 0) { |
| owner()->DeallocateTemps(num_temps); |
| Do(new DropTempsInstr(num_temps)); |
| } |
| } |
| |
| |
| void ValueGraphVisitor::VisitLetNode(LetNode* node) { |
| BuildLetTempExpressions(node); |
| |
| // Visit body. |
| for (intptr_t i = 0; i < node->nodes().length() - 1; ++i) { |
| EffectGraphVisitor for_effect(owner()); |
| node->nodes()[i]->Visit(&for_effect); |
| Append(for_effect); |
| } |
| // Visit the last body expression for value. |
| ValueGraphVisitor for_value(owner()); |
| node->nodes().Last()->Visit(&for_value); |
| Append(for_value); |
| Value* result_value = for_value.value(); |
| |
| intptr_t num_temps = node->num_temps(); |
| if (num_temps > 0) { |
| owner()->DeallocateTemps(num_temps); |
| ReturnDefinition(new DropTempsInstr(num_temps, result_value)); |
| } else { |
| ReturnValue(result_value); |
| } |
| } |
| |
| |
| void EffectGraphVisitor::VisitArrayNode(ArrayNode* node) { |
| const TypeArguments& type_args = |
| TypeArguments::ZoneHandle(node->type().arguments()); |
| Value* element_type = BuildInstantiatedTypeArguments(node->token_pos(), |
| type_args); |
| Value* num_elements = |
| Bind(new ConstantInstr(Smi::ZoneHandle(Smi::New(node->length())))); |
| CreateArrayInstr* create = new CreateArrayInstr(node->token_pos(), |
| element_type, |
| num_elements); |
| Value* array_val = Bind(create); |
| |
| { LocalVariable* tmp_var = EnterTempLocalScope(array_val); |
| const intptr_t class_id = kArrayCid; |
| const intptr_t deopt_id = Isolate::kNoDeoptId; |
| for (int i = 0; i < node->length(); ++i) { |
| Value* array = Bind(new LoadLocalInstr(*tmp_var)); |
| Value* index = Bind(new ConstantInstr(Smi::ZoneHandle(Smi::New(i)))); |
| ValueGraphVisitor for_value(owner()); |
| node->ElementAt(i)->Visit(&for_value); |
| Append(for_value); |
| // No store barrier needed for constants. |
| const StoreBarrierType emit_store_barrier = |
| for_value.value()->BindsToConstant() |
| ? kNoStoreBarrier |
| : kEmitStoreBarrier; |
| intptr_t index_scale = FlowGraphCompiler::ElementSizeFor(class_id); |
| StoreIndexedInstr* store = new StoreIndexedInstr( |
| array, index, for_value.value(), emit_store_barrier, |
| index_scale, class_id, deopt_id, node->token_pos()); |
| Do(store); |
| } |
| ReturnDefinition(ExitTempLocalScope(tmp_var)); |
| } |
| } |
| |
| |
| void EffectGraphVisitor::VisitStringInterpolateNode( |
| StringInterpolateNode* node) { |
| ValueGraphVisitor for_argument(owner()); |
| node->value()->Visit(&for_argument); |
| Append(for_argument); |
| StringInterpolateInstr* instr = |
| new StringInterpolateInstr(for_argument.value(), node->token_pos()); |
| ReturnDefinition(instr); |
| } |
| |
| |
| void EffectGraphVisitor::VisitClosureNode(ClosureNode* node) { |
| const Function& function = node->function(); |
| |
| if (function.IsImplicitStaticClosureFunction()) { |
| const Instance& closure = |
| Instance::ZoneHandle(function.ImplicitStaticClosure()); |
| ReturnDefinition(new ConstantInstr(closure)); |
| return; |
| } |
| const bool is_implicit = function.IsImplicitInstanceClosureFunction(); |
| ASSERT(is_implicit || function.IsNonImplicitClosureFunction()); |
| // The context scope may have already been set by the non-optimizing |
| // compiler. If it was not, set it here. |
| if (function.context_scope() == ContextScope::null()) { |
| ASSERT(!is_implicit); |
| const ContextScope& context_scope = ContextScope::ZoneHandle( |
| node->scope()->PreserveOuterScope(owner()->context_level())); |
| ASSERT(!function.HasCode()); |
| ASSERT(function.context_scope() == ContextScope::null()); |
| function.set_context_scope(context_scope); |
| const Class& cls = Class::Handle( |
| owner()->parsed_function()->function().Owner()); |
| // The closure is now properly setup, add it to the lookup table. |
| // It is possible that the compiler creates more than one function |
| // object for the same closure, e.g. when inlining nodes from |
| // finally clauses. If we already have a function object for the |
| // same closure, do not add a second one. We compare the origin |
| // class, token position, and parent function to detect duplicates. |
| // Note that we can have two different closure object for the same |
| // source text represntation of the closure: one with a non-closurized |
| // parent, and one with a closurized parent function. |
| |
| const Function& found_func = Function::Handle( |
| cls.LookupClosureFunction(function.token_pos())); |
| |
| if (found_func.IsNull() || |
| (found_func.token_pos() != function.token_pos()) || |
| (found_func.script() != function.script()) || |
| (found_func.parent_function() != function.parent_function())) { |
| cls.AddClosureFunction(function); |
| } |
| } |
| ZoneGrowableArray<PushArgumentInstr*>* arguments = |
| new ZoneGrowableArray<PushArgumentInstr*>(1); |
| ASSERT(function.context_scope() != ContextScope::null()); |
| |
| // The function type of a closure may have type arguments. In that case, |
| // pass the type arguments of the instantiator. |
| const Class& cls = Class::ZoneHandle(function.signature_class()); |
| ASSERT(!cls.IsNull()); |
| const bool requires_type_arguments = cls.NumTypeArguments() > 0; |
| Value* type_arguments = NULL; |
| if (requires_type_arguments) { |
| ASSERT(cls.type_arguments_field_offset() == |
| Closure::type_arguments_offset()); |
| ASSERT(cls.instance_size() == Closure::InstanceSize()); |
| const Class& instantiator_class = Class::Handle( |
| owner()->parsed_function()->function().Owner()); |
| type_arguments = BuildInstantiatorTypeArguments(node->token_pos(), |
| instantiator_class, |
| NULL); |
| arguments->Add(PushArgument(type_arguments)); |
| } |
| AllocateObjectInstr* alloc = new AllocateObjectInstr(node->token_pos(), |
| cls, |
| arguments); |
| alloc->set_closure_function(function); |
| |
| Value* closure_val = Bind(alloc); |
| { LocalVariable* closure_tmp_var = EnterTempLocalScope(closure_val); |
| // Store function. |
| Value* closure_tmp_val = Bind(new LoadLocalInstr(*closure_tmp_var)); |
| Value* func_val = |
| Bind(new ConstantInstr(Function::ZoneHandle(function.raw()))); |
| Do(new StoreInstanceFieldInstr(Closure::function_offset(), |
| closure_tmp_val, |
| func_val, |
| kEmitStoreBarrier, |
| node->token_pos())); |
| if (is_implicit) { |
| // Create new context containing the receiver. |
| const intptr_t kNumContextVariables = 1; // The receiver. |
| Value* allocated_context = |
| Bind(new AllocateContextInstr(node->token_pos(), |
| kNumContextVariables)); |
| { LocalVariable* context_tmp_var = EnterTempLocalScope(allocated_context); |
| // Store receiver in context. |
| Value* context_tmp_val = Bind(new LoadLocalInstr(*context_tmp_var)); |
| ValueGraphVisitor for_receiver(owner()); |
| node->receiver()->Visit(&for_receiver); |
| Append(for_receiver); |
| Value* receiver = for_receiver.value(); |
| Do(new StoreInstanceFieldInstr(Context::variable_offset(0), |
| context_tmp_val, |
| receiver, |
| kEmitStoreBarrier, |
| node->token_pos())); |
| // Store new context in closure. |
| closure_tmp_val = Bind(new LoadLocalInstr(*closure_tmp_var)); |
| context_tmp_val = Bind(new LoadLocalInstr(*context_tmp_var)); |
| Do(new StoreInstanceFieldInstr(Closure::context_offset(), |
| closure_tmp_val, |
| context_tmp_val, |
| kEmitStoreBarrier, |
| node->token_pos())); |
| Do(ExitTempLocalScope(context_tmp_var)); |
| } |
| } else { |
| // Store current context in closure. |
| closure_tmp_val = Bind(new LoadLocalInstr(*closure_tmp_var)); |
| Value* context = Bind(new CurrentContextInstr()); |
| Do(new StoreInstanceFieldInstr(Closure::context_offset(), |
| closure_tmp_val, |
| context, |
| kEmitStoreBarrier, |
| node->token_pos())); |
| } |
| ReturnDefinition(ExitTempLocalScope(closure_tmp_var)); |
| } |
| } |
| |
| |
| void EffectGraphVisitor::BuildPushArguments( |
| const ArgumentListNode& node, |
| ZoneGrowableArray<PushArgumentInstr*>* values) { |
| for (intptr_t i = 0; i < node.length(); ++i) { |
| ValueGraphVisitor for_argument(owner()); |
| node.NodeAt(i)->Visit(&for_argument); |
| Append(for_argument); |
| PushArgumentInstr* push_arg = PushArgument(for_argument.value()); |
| values->Add(push_arg); |
| } |
| } |
| |
| |
| void EffectGraphVisitor::VisitInstanceCallNode(InstanceCallNode* node) { |
| ValueGraphVisitor for_receiver(owner()); |
| node->receiver()->Visit(&for_receiver); |
| Append(for_receiver); |
| PushArgumentInstr* push_receiver = PushArgument(for_receiver.value()); |
| ZoneGrowableArray<PushArgumentInstr*>* arguments = |
| new ZoneGrowableArray<PushArgumentInstr*>( |
| node->arguments()->length() + 1); |
| arguments->Add(push_receiver); |
| |
| BuildPushArguments(*node->arguments(), arguments); |
| InstanceCallInstr* call = new InstanceCallInstr( |
| node->token_pos(), |
| node->function_name(), |
| Token::kILLEGAL, |
| arguments, |
| node->arguments()->names(), |
| 1, |
| owner()->ic_data_array()); |
| ReturnDefinition(call); |
| } |
| |
| |
| static intptr_t GetResultCidOfNativeFactory(const Function& function) { |
| const Class& function_class = Class::Handle(function.Owner()); |
| if (function_class.library() == Library::TypedDataLibrary()) { |
| const String& function_name = String::Handle(function.name()); |
| if (!String::EqualsIgnoringPrivateKey(function_name, Symbols::_New())) { |
| return kDynamicCid; |
| } |
| switch (function_class.id()) { |
| case kTypedDataInt8ArrayCid: |
| case kTypedDataUint8ArrayCid: |
| case kTypedDataUint8ClampedArrayCid: |
| case kTypedDataInt16ArrayCid: |
| case kTypedDataUint16ArrayCid: |
| case kTypedDataInt32ArrayCid: |
| case kTypedDataUint32ArrayCid: |
| case kTypedDataInt64ArrayCid: |
| case kTypedDataUint64ArrayCid: |
| case kTypedDataFloat32ArrayCid: |
| case kTypedDataFloat64ArrayCid: |
| case kTypedDataFloat32x4ArrayCid: |
| case kTypedDataInt32x4ArrayCid: |
| return function_class.id(); |
| default: |
| return kDynamicCid; // Unknown. |
| } |
| } |
| return kDynamicCid; |
| } |
| |
| |
| // <Expression> ::= StaticCall { function: Function |
| // arguments: <ArgumentList> } |
| void EffectGraphVisitor::VisitStaticCallNode(StaticCallNode* node) { |
| ZoneGrowableArray<PushArgumentInstr*>* arguments = |
| new ZoneGrowableArray<PushArgumentInstr*>(node->arguments()->length()); |
| BuildPushArguments(*node->arguments(), arguments); |
| StaticCallInstr* call = |
| new StaticCallInstr(node->token_pos(), |
| node->function(), |
| node->arguments()->names(), |
| arguments, |
| owner()->ic_data_array()); |
| if (node->function().is_native()) { |
| const intptr_t result_cid = GetResultCidOfNativeFactory(node->function()); |
| if (result_cid != kDynamicCid) { |
| call->set_result_cid(result_cid); |
| call->set_is_native_list_factory(true); |
| } |
| } |
| ReturnDefinition(call); |
| } |
| |
| |
| void EffectGraphVisitor::BuildClosureCall( |
| ClosureCallNode* node, bool result_needed) { |
| ValueGraphVisitor for_closure(owner()); |
| node->closure()->Visit(&for_closure); |
| Append(for_closure); |
| |
| LocalVariable* tmp_var = EnterTempLocalScope(for_closure.value()); |
| |
| ZoneGrowableArray<PushArgumentInstr*>* arguments = |
| new ZoneGrowableArray<PushArgumentInstr*>(node->arguments()->length()); |
| Value* closure_val = Bind(new LoadLocalInstr(*tmp_var)); |
| PushArgumentInstr* push_closure = PushArgument(closure_val); |
| arguments->Add(push_closure); |
| BuildPushArguments(*node->arguments(), arguments); |
| |
| // Save context around the call. |
| ASSERT(owner()->parsed_function()->saved_current_context_var() != NULL); |
| BuildSaveContext(*owner()->parsed_function()->saved_current_context_var()); |
| closure_val = Bind(new LoadLocalInstr(*tmp_var)); |
| LoadFieldInstr* context_load = new LoadFieldInstr(closure_val, |
| Closure::context_offset(), |
| AbstractType::ZoneHandle(), |
| node->token_pos()); |
| context_load->set_is_immutable(true); |
| Value* context_val = Bind(context_load); |
| AddInstruction(new StoreContextInstr(context_val)); |
| closure_val = Bind(new LoadLocalInstr(*tmp_var)); |
| LoadFieldInstr* function_load = |
| new LoadFieldInstr(closure_val, |
| Closure::function_offset(), |
| AbstractType::ZoneHandle(), |
| node->token_pos()); |
| function_load->set_is_immutable(true); |
| Value* function_val = Bind(function_load); |
| Definition* closure_call = |
| new ClosureCallInstr(function_val, node, arguments); |
| if (result_needed) { |
| Value* result = Bind(closure_call); |
| Do(new StoreLocalInstr(*tmp_var, result)); |
| // Restore context from temp. |
| BuildRestoreContext( |
| *owner()->parsed_function()->saved_current_context_var()); |
| ReturnDefinition(ExitTempLocalScope(tmp_var)); |
| } else { |
| Do(closure_call); |
| // Restore context from saved location. |
| BuildRestoreContext( |
| *owner()->parsed_function()->saved_current_context_var()); |
| Do(ExitTempLocalScope(tmp_var)); |
| } |
| } |
| |
| |
| void EffectGraphVisitor::VisitClosureCallNode(ClosureCallNode* node) { |
| BuildClosureCall(node, false); |
| } |
| |
| |
| void ValueGraphVisitor::VisitClosureCallNode(ClosureCallNode* node) { |
| BuildClosureCall(node, true); |
| } |
| |
| |
| void EffectGraphVisitor::VisitCloneContextNode(CloneContextNode* node) { |
| Value* context = Bind(new CurrentContextInstr()); |
| Value* clone = Bind(new CloneContextInstr(node->token_pos(), context)); |
| AddInstruction(new StoreContextInstr(clone)); |
| } |
| |
| |
| Value* EffectGraphVisitor::BuildObjectAllocation(ConstructorCallNode* node) { |
| const Class& cls = Class::ZoneHandle(node->constructor().Owner()); |
| const bool cls_is_parameterized = cls.NumTypeArguments() > 0; |
| |
| ZoneGrowableArray<PushArgumentInstr*>* allocate_arguments = |
| new ZoneGrowableArray<PushArgumentInstr*>(cls_is_parameterized ? 1 : 0); |
| if (cls_is_parameterized) { |
| Value* type_args = BuildInstantiatedTypeArguments(node->token_pos(), |
| node->type_arguments()); |
| allocate_arguments->Add(PushArgument(type_args)); |
| } |
| |
| Definition* allocation = new AllocateObjectInstr( |
| node->token_pos(), |
| Class::ZoneHandle(node->constructor().Owner()), |
| allocate_arguments); |
| |
| return Bind(allocation); |
| } |
| |
| |
| void EffectGraphVisitor::BuildConstructorCall( |
| ConstructorCallNode* node, |
| PushArgumentInstr* push_alloc_value) { |
| Value* ctor_arg = Bind( |
| new ConstantInstr(Smi::ZoneHandle(Smi::New(Function::kCtorPhaseAll)))); |
| PushArgumentInstr* push_ctor_arg = PushArgument(ctor_arg); |
| |
| ZoneGrowableArray<PushArgumentInstr*>* arguments = |
| new ZoneGrowableArray<PushArgumentInstr*>(2); |
| arguments->Add(push_alloc_value); |
| arguments->Add(push_ctor_arg); |
| |
| BuildPushArguments(*node->arguments(), arguments); |
| Do(new StaticCallInstr(node->token_pos(), |
| node->constructor(), |
| node->arguments()->names(), |
| arguments, |
| owner()->ic_data_array())); |
| } |
| |
| |
| static intptr_t GetResultCidOfListFactory(ConstructorCallNode* node) { |
| const Function& function = node->constructor(); |
| const Class& function_class = Class::Handle(function.Owner()); |
| |
| if ((function_class.library() != Library::CoreLibrary()) && |
| (function_class.library() != Library::TypedDataLibrary())) { |
| return kDynamicCid; |
| } |
| |
| if (node->constructor().IsFactory()) { |
| if ((function_class.Name() == Symbols::List().raw()) && |
| (function.name() == Symbols::ListFactory().raw())) { |
| // Special recognition of 'new List()' vs 'new List(n)'. |
| if (node->arguments()->length() == 0) { |
| return kGrowableObjectArrayCid; |
| } |
| return kArrayCid; |
| } |
| return FactoryRecognizer::ResultCid(function); |
| } |
| return kDynamicCid; // Not a known list constructor. |
| } |
| |
| |
| void EffectGraphVisitor::VisitConstructorCallNode(ConstructorCallNode* node) { |
| if (node->constructor().IsFactory()) { |
| ZoneGrowableArray<PushArgumentInstr*>* arguments = |
| new ZoneGrowableArray<PushArgumentInstr*>(); |
| PushArgumentInstr* push_type_arguments = PushArgument( |
| BuildInstantiatedTypeArguments(node->token_pos(), |
| node->type_arguments())); |
| arguments->Add(push_type_arguments); |
| ASSERT(arguments->length() == 1); |
| BuildPushArguments(*node->arguments(), arguments); |
| StaticCallInstr* call = |
| new StaticCallInstr(node->token_pos(), |
| node->constructor(), |
| node->arguments()->names(), |
| arguments, |
| owner()->ic_data_array()); |
| const intptr_t result_cid = GetResultCidOfListFactory(node); |
| if (result_cid != kDynamicCid) { |
| call->set_result_cid(result_cid); |
| call->set_is_known_list_constructor(true); |
| // Recognized fixed length array factory must have two arguments: |
| // (0) type-arguments, (1) length. |
| ASSERT(!LoadFieldInstr::IsFixedLengthArrayCid(result_cid) || |
| arguments->length() == 2); |
| } |
| ReturnDefinition(call); |
| return; |
| } |
| // t_n contains the allocated and initialized object. |
| // t_n <- AllocateObject(class) |
| // t_n+1 <- ctor-arg |
| // t_n+2... <- constructor arguments start here |
| // StaticCall(constructor, t_n+1, t_n+2, ...) |
| // No need to preserve allocated value (simpler than in ValueGraphVisitor). |
| Value* allocated_value = BuildObjectAllocation(node); |
| PushArgumentInstr* push_allocated_value = PushArgument(allocated_value); |
| BuildConstructorCall(node, push_allocated_value); |
| } |
| |
| |
| Value* EffectGraphVisitor::BuildInstantiator(const Class& instantiator_class) { |
| ASSERT(instantiator_class.NumTypeParameters() > 0); |
| Function& outer_function = |
| Function::Handle(owner()->parsed_function()->function().raw()); |
| while (outer_function.IsLocalFunction()) { |
| outer_function = outer_function.parent_function(); |
| } |
| if (outer_function.IsFactory()) { |
| return NULL; |
| } |
| |
| LocalVariable* instantiator = owner()->parsed_function()->instantiator(); |
| ASSERT(instantiator != NULL); |
| Value* result = Bind(BuildLoadLocal(*instantiator)); |
| return result; |
| } |
| |
| |
| // 'expression_temp_var' may not be used inside this method if 'instantiator' |
| // is not NULL. |
| Value* EffectGraphVisitor::BuildInstantiatorTypeArguments( |
| intptr_t token_pos, |
| const Class& instantiator_class, |
| Value* instantiator) { |
| if (instantiator_class.NumTypeParameters() == 0) { |
| // The type arguments are compile time constants. |
| TypeArguments& type_arguments = TypeArguments::ZoneHandle(); |
| // Type is temporary. Only its type arguments are preserved. |
| Type& type = Type::Handle( |
| Type::New(instantiator_class, type_arguments, token_pos, Heap::kNew)); |
| type ^= ClassFinalizer::FinalizeType( |
| instantiator_class, type, ClassFinalizer::kFinalize); |
| ASSERT(!type.IsMalformedOrMalbounded()); |
| type_arguments = type.arguments(); |
| type_arguments = type_arguments.Canonicalize(); |
| return Bind(new ConstantInstr(type_arguments)); |
| } |
| Function& outer_function = |
| Function::Handle(owner()->parsed_function()->function().raw()); |
| while (outer_function.IsLocalFunction()) { |
| outer_function = outer_function.parent_function(); |
| } |
| if (outer_function.IsFactory()) { |
| // No instantiator for factories. |
| ASSERT(instantiator == NULL); |
| LocalVariable* instantiator_var = |
| owner()->parsed_function()->instantiator(); |
| ASSERT(instantiator_var != NULL); |
| return Bind(BuildLoadLocal(*instantiator_var)); |
| } |
| if (instantiator == NULL) { |
| instantiator = BuildInstantiator(instantiator_class); |
| } |
| // The instantiator is the receiver of the caller, which is not a factory. |
| // The receiver cannot be null; extract its TypeArguments object. |
| // Note that in the factory case, the instantiator is the first parameter |
| // of the factory, i.e. already a TypeArguments object. |
| intptr_t type_arguments_field_offset = |
| instantiator_class.type_arguments_field_offset(); |
| ASSERT(type_arguments_field_offset != Class::kNoTypeArguments); |
| |
| return Bind(new LoadFieldInstr( |
| instantiator, |
| type_arguments_field_offset, |
| Type::ZoneHandle(), // Not an instance, no type. |
| Scanner::kNoSourcePos)); |
| } |
| |
| |
| Value* EffectGraphVisitor::BuildInstantiatedTypeArguments( |
| intptr_t token_pos, |
| const TypeArguments& type_arguments) { |
| if (type_arguments.IsNull() || type_arguments.IsInstantiated()) { |
| return Bind(new ConstantInstr(type_arguments)); |
| } |
| // The type arguments are uninstantiated. |
| const Class& instantiator_class = Class::ZoneHandle( |
| owner()->parsed_function()->function().Owner()); |
| Value* instantiator_value = |
| BuildInstantiatorTypeArguments(token_pos, instantiator_class, NULL); |
| const bool use_instantiator_type_args = |
| type_arguments.IsUninstantiatedIdentity() || |
| type_arguments.CanShareInstantiatorTypeArguments(instantiator_class); |
| if (use_instantiator_type_args) { |
| return instantiator_value; |
| } else { |
| return Bind(new InstantiateTypeArgumentsInstr(token_pos, |
| type_arguments, |
| instantiator_class, |
| instantiator_value)); |
| } |
| } |
| |
| |
| void ValueGraphVisitor::VisitConstructorCallNode(ConstructorCallNode* node) { |
| if (node->constructor().IsFactory()) { |
| EffectGraphVisitor::VisitConstructorCallNode(node); |
| return; |
| } |
| |
| // t_n contains the allocated and initialized object. |
| // t_n <- AllocateObject(class) |
| // t_n <- StoreLocal(temp, t_n); |
| // t_n+1 <- ctor-arg |
| // t_n+2... <- constructor arguments start here |
| // StaticCall(constructor, t_n, t_n+1, ...) |
| // tn <- LoadLocal(temp) |
| |
| Value* allocate = BuildObjectAllocation(node); |
| { LocalVariable* tmp_var = EnterTempLocalScope(allocate); |
| Value* allocated_tmp = Bind(new LoadLocalInstr(*tmp_var)); |
| PushArgumentInstr* push_allocated_value = PushArgument(allocated_tmp); |
| BuildConstructorCall(node, push_allocated_value); |
| ReturnDefinition(ExitTempLocalScope(tmp_var)); |
| } |
| } |
| |
| |
| void EffectGraphVisitor::VisitInstanceGetterNode(InstanceGetterNode* node) { |
| ValueGraphVisitor for_receiver(owner()); |
| node->receiver()->Visit(&for_receiver); |
| Append(for_receiver); |
| PushArgumentInstr* push_receiver = PushArgument(for_receiver.value()); |
| ZoneGrowableArray<PushArgumentInstr*>* arguments = |
| new ZoneGrowableArray<PushArgumentInstr*>(1); |
| arguments->Add(push_receiver); |
| const String& name = |
| String::ZoneHandle(Field::GetterSymbol(node->field_name())); |
| InstanceCallInstr* call = new InstanceCallInstr( |
| node->token_pos(), |
| name, |
| Token::kGET, |
| arguments, Object::null_array(), |
| 1, |
| owner()->ic_data_array()); |
| ReturnDefinition(call); |
| } |
| |
| |
| void EffectGraphVisitor::BuildInstanceSetterArguments( |
| InstanceSetterNode* node, |
| ZoneGrowableArray<PushArgumentInstr*>* arguments, |
| bool result_is_needed) { |
| ValueGraphVisitor for_receiver(owner()); |
| node->receiver()->Visit(&for_receiver); |
| Append(for_receiver); |
| arguments->Add(PushArgument(for_receiver.value())); |
| |
| ValueGraphVisitor for_value(owner()); |
| node->value()->Visit(&for_value); |
| Append(for_value); |
| |
| Value* value = NULL; |
| if (result_is_needed) { |
| value = Bind(BuildStoreExprTemp(for_value.value())); |
| } else { |
| value = for_value.value(); |
| } |
| arguments->Add(PushArgument(value)); |
| } |
| |
| |
| void EffectGraphVisitor::VisitInstanceSetterNode(InstanceSetterNode* node) { |
| ZoneGrowableArray<PushArgumentInstr*>* arguments = |
| new ZoneGrowableArray<PushArgumentInstr*>(2); |
| BuildInstanceSetterArguments(node, arguments, kResultNotNeeded); |
| const String& name = |
| String::ZoneHandle(Field::SetterSymbol(node->field_name())); |
| InstanceCallInstr* call = new InstanceCallInstr(node->token_pos(), |
| name, |
| Token::kSET, |
| arguments, |
| Object::null_array(), |
| 2, // Checked arg count. |
| owner()->ic_data_array()); |
| ReturnDefinition(call); |
| } |
| |
| |
| void ValueGraphVisitor::VisitInstanceSetterNode(InstanceSetterNode* node) { |
| ZoneGrowableArray<PushArgumentInstr*>* arguments = |
| new ZoneGrowableArray<PushArgumentInstr*>(2); |
| BuildInstanceSetterArguments(node, arguments, kResultNeeded); |
| const String& name = |
| String::ZoneHandle(Field::SetterSymbol(node->field_name())); |
| Do(new InstanceCallInstr(node->token_pos(), |
| name, |
| Token::kSET, |
| arguments, |
| Object::null_array(), |
| 2, // Checked argument count. |
| owner()->ic_data_array())); |
| ReturnDefinition(BuildLoadExprTemp()); |
| } |
| |
| |
| void EffectGraphVisitor::VisitStaticGetterNode(StaticGetterNode* node) { |
| const String& getter_name = |
| String::ZoneHandle(Field::GetterSymbol(node->field_name())); |
| ZoneGrowableArray<PushArgumentInstr*>* arguments = |
| new ZoneGrowableArray<PushArgumentInstr*>(); |
| Function& getter_function = Function::ZoneHandle(); |
| if (node->is_super_getter()) { |
| // Statically resolved instance getter, i.e. "super getter". |
| ASSERT(node->receiver() != NULL); |
| getter_function = Resolver::ResolveDynamicAnyArgs(node->cls(), getter_name); |
| if (getter_function.IsNull()) { |
| // Resolve and call noSuchMethod. |
| ArgumentListNode* arguments = new ArgumentListNode(node->token_pos()); |
| arguments->Add(node->receiver()); |
| StaticCallInstr* call = |
| BuildStaticNoSuchMethodCall(node->cls(), |
| node->receiver(), |
| getter_name, |
| arguments, |
| false, // Don't save last argument. |
| true); // Super invocation. |
| ReturnDefinition(call); |
| return; |
| } else { |
| ValueGraphVisitor receiver_value(owner()); |
| node->receiver()->Visit(&receiver_value); |
| Append(receiver_value); |
| arguments->Add(PushArgument(receiver_value.value())); |
| } |
| } else { |
| getter_function = node->cls().LookupStaticFunction(getter_name); |
| if (getter_function.IsNull()) { |
| // When the parser encounters a reference to a static field materialized |
| // only by a static setter, but no corresponding static getter, it creates |
| // a StaticGetterNode ast node referring to the non-existing static getter |
| // for the case this field reference appears in a left hand side |
| // expression (the parser has not distinguished between left and right |
| // hand side yet at this stage). If the parser establishes later that the |
| // field access is part of a left hand side expression, the |
| // StaticGetterNode is transformed into a StaticSetterNode referring to |
| // the existing static setter. |
| // However, if the field reference appears in a right hand side |
| // expression, no such transformation occurs and we land here with a |
| // StaticGetterNode missing a getter function, so we throw a |
| // NoSuchMethodError. |
| |
| // Throw a NoSuchMethodError. |
| StaticCallInstr* call = BuildThrowNoSuchMethodError( |
| node->token_pos(), |
| node->cls(), |
| getter_name, |
| NULL, // No Arguments to getter. |
| InvocationMirror::EncodeType( |
| node->cls().IsTopLevel() ? |
| InvocationMirror::kTopLevel : |
| InvocationMirror::kStatic, |
| InvocationMirror::kGetter)); |
| ReturnDefinition(call); |
| return; |
| } |
| } |
| ASSERT(!getter_function.IsNull()); |
| StaticCallInstr* call = new StaticCallInstr(node->token_pos(), |
| getter_function, |
| Object::null_array(), // No names |
| arguments, |
| owner()->ic_data_array()); |
| ReturnDefinition(call); |
| } |
| |
| |
| void EffectGraphVisitor::BuildStaticSetter(StaticSetterNode* node, |
| bool result_is_needed) { |
| const String& setter_name = |
| String::ZoneHandle(Field::SetterSymbol(node->field_name())); |
| ZoneGrowableArray<PushArgumentInstr*>* arguments = |
| new ZoneGrowableArray<PushArgumentInstr*>(1); |
| // A super setter is an instance setter whose setter function is |
| // resolved at compile time (in the caller instance getter's super class). |
| // Unlike a static getter, a super getter has a receiver parameter. |
| const bool is_super_setter = (node->receiver() != NULL); |
| Function& setter_function = |
| Function::ZoneHandle(is_super_setter |
| ? Resolver::ResolveDynamicAnyArgs(node->cls(), setter_name) |
| : node->cls().LookupStaticFunction(setter_name)); |
| StaticCallInstr* call; |
| if (setter_function.IsNull()) { |
| if (is_super_setter) { |
| ASSERT(node->receiver() |