| Index: src/hydrogen.cc
|
| diff --git a/src/hydrogen.cc b/src/hydrogen.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..d03991b5e07993c413f9334edc3493b756d6dc75
|
| --- /dev/null
|
| +++ b/src/hydrogen.cc
|
| @@ -0,0 +1,5540 @@
|
| +// Copyright 2010 the V8 project authors. All rights reserved.
|
| +// Redistribution and use in source and binary forms, with or without
|
| +// modification, are permitted provided that the following conditions are
|
| +// met:
|
| +//
|
| +// * Redistributions of source code must retain the above copyright
|
| +// notice, this list of conditions and the following disclaimer.
|
| +// * Redistributions in binary form must reproduce the above
|
| +// copyright notice, this list of conditions and the following
|
| +// disclaimer in the documentation and/or other materials provided
|
| +// with the distribution.
|
| +// * Neither the name of Google Inc. nor the names of its
|
| +// contributors may be used to endorse or promote products derived
|
| +// from this software without specific prior written permission.
|
| +//
|
| +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
| +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
| +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
| +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
| +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
| +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
| +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
| +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
| +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
| +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
| +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
| +
|
| +#include "hydrogen.h"
|
| +
|
| +#include "codegen.h"
|
| +#include "data-flow.h"
|
| +#include "full-codegen.h"
|
| +#include "hashmap.h"
|
| +#include "lithium-allocator.h"
|
| +#include "parser.h"
|
| +#include "scopes.h"
|
| +
|
| +#if V8_TARGET_ARCH_IA32
|
| +#include "ia32/lithium-codegen-ia32.h"
|
| +#elif V8_TARGET_ARCH_X64
|
| +#include "x64/lithium-codegen-x64.h"
|
| +#elif V8_TARGET_ARCH_ARM
|
| +#include "arm/lithium-codegen-arm.h"
|
| +#else
|
| +#error Unsupported target architecture.
|
| +#endif
|
| +
|
| +namespace v8 {
|
| +namespace internal {
|
| +
|
| +HBasicBlock::HBasicBlock(HGraph* graph)
|
| + : block_id_(graph->GetNextBlockID()),
|
| + graph_(graph),
|
| + phis_(4),
|
| + first_(NULL),
|
| + last_(NULL),
|
| + end_(NULL),
|
| + loop_information_(NULL),
|
| + predecessors_(2),
|
| + dominator_(NULL),
|
| + dominated_blocks_(4),
|
| + last_environment_(NULL),
|
| + argument_count_(-1),
|
| + first_instruction_index_(-1),
|
| + last_instruction_index_(-1),
|
| + deleted_phis_(4),
|
| + is_inline_return_target_(false),
|
| + inverted_(false),
|
| + deopt_predecessor_(NULL) {
|
| +}
|
| +
|
| +
|
| +void HBasicBlock::AttachLoopInformation() {
|
| + ASSERT(!IsLoopHeader());
|
| + loop_information_ = new HLoopInformation(this);
|
| +}
|
| +
|
| +
|
| +void HBasicBlock::DetachLoopInformation() {
|
| + ASSERT(IsLoopHeader());
|
| + loop_information_ = NULL;
|
| +}
|
| +
|
| +
|
| +void HBasicBlock::AddPhi(HPhi* phi) {
|
| + ASSERT(!IsStartBlock());
|
| + phis_.Add(phi);
|
| + phi->SetBlock(this);
|
| +}
|
| +
|
| +
|
| +void HBasicBlock::RemovePhi(HPhi* phi) {
|
| + ASSERT(phi->block() == this);
|
| + ASSERT(phis_.Contains(phi));
|
| + ASSERT(phi->HasNoUses());
|
| + phi->ClearOperands();
|
| + phis_.RemoveElement(phi);
|
| + phi->SetBlock(NULL);
|
| +}
|
| +
|
| +
|
| +void HBasicBlock::AddInstruction(HInstruction* instr) {
|
| + ASSERT(!IsStartBlock() || !IsFinished());
|
| + ASSERT(!instr->IsLinked());
|
| + ASSERT(!IsFinished());
|
| + if (first_ == NULL) {
|
| + HBlockEntry* entry = new HBlockEntry();
|
| + entry->InitializeAsFirst(this);
|
| + first_ = entry;
|
| + }
|
| + instr->InsertAfter(GetLastInstruction());
|
| +}
|
| +
|
| +
|
| +HInstruction* HBasicBlock::GetLastInstruction() {
|
| + if (end_ != NULL) return end_->previous();
|
| + if (first_ == NULL) return NULL;
|
| + if (last_ == NULL) last_ = first_;
|
| + while (last_->next() != NULL) last_ = last_->next();
|
| + return last_;
|
| +}
|
| +
|
| +
|
| +HSimulate* HBasicBlock::CreateSimulate(int id) {
|
| + ASSERT(HasEnvironment());
|
| + HEnvironment* environment = last_environment();
|
| + ASSERT(id == AstNode::kNoNumber ||
|
| + environment->closure()->shared()->VerifyBailoutId(id));
|
| +
|
| + int push_count = environment->push_count();
|
| + int pop_count = environment->pop_count();
|
| +
|
| + int length = environment->values()->length();
|
| + HSimulate* instr = new HSimulate(id, pop_count, length);
|
| + for (int i = push_count - 1; i >= 0; --i) {
|
| + instr->AddPushedValue(environment->ExpressionStackAt(i));
|
| + }
|
| + for (int i = 0; i < environment->assigned_variables()->length(); ++i) {
|
| + int index = environment->assigned_variables()->at(i);
|
| + instr->AddAssignedValue(index, environment->Lookup(index));
|
| + }
|
| + environment->ClearHistory();
|
| + return instr;
|
| +}
|
| +
|
| +
|
| +void HBasicBlock::Finish(HControlInstruction* end) {
|
| + ASSERT(!IsFinished());
|
| + AddInstruction(end);
|
| + end_ = end;
|
| + if (end->FirstSuccessor() != NULL) {
|
| + end->FirstSuccessor()->RegisterPredecessor(this);
|
| + if (end->SecondSuccessor() != NULL) {
|
| + end->SecondSuccessor()->RegisterPredecessor(this);
|
| + }
|
| + }
|
| +}
|
| +
|
| +
|
| +void HBasicBlock::Goto(HBasicBlock* block, bool include_stack_check) {
|
| + AddSimulate(AstNode::kNoNumber);
|
| + HGoto* instr = new HGoto(block);
|
| + instr->set_include_stack_check(include_stack_check);
|
| + Finish(instr);
|
| +}
|
| +
|
| +
|
| +void HBasicBlock::SetInitialEnvironment(HEnvironment* env) {
|
| + ASSERT(!HasEnvironment());
|
| + ASSERT(first() == NULL);
|
| + UpdateEnvironment(env);
|
| +}
|
| +
|
| +
|
| +void HBasicBlock::SetJoinId(int id) {
|
| + int length = predecessors_.length();
|
| + ASSERT(length > 0);
|
| + for (int i = 0; i < length; i++) {
|
| + HBasicBlock* predecessor = predecessors_[i];
|
| + ASSERT(predecessor->end()->IsGoto());
|
| + HSimulate* simulate = HSimulate::cast(predecessor->GetLastInstruction());
|
| + // We only need to verify the ID once.
|
| + ASSERT(i != 0 ||
|
| + predecessor->last_environment()->closure()->shared()
|
| + ->VerifyBailoutId(id));
|
| + simulate->set_ast_id(id);
|
| + }
|
| +}
|
| +
|
| +
|
| +bool HBasicBlock::Dominates(HBasicBlock* other) const {
|
| + HBasicBlock* current = other->dominator();
|
| + while (current != NULL) {
|
| + if (current == this) return true;
|
| + current = current->dominator();
|
| + }
|
| + return false;
|
| +}
|
| +
|
| +
|
| +void HBasicBlock::PostProcessLoopHeader(IterationStatement* stmt) {
|
| + ASSERT(IsLoopHeader());
|
| +
|
| + SetJoinId(stmt->EntryId());
|
| + if (predecessors()->length() == 1) {
|
| + // This is a degenerated loop.
|
| + DetachLoopInformation();
|
| + return;
|
| + }
|
| +
|
| + // Only the first entry into the loop is from outside the loop. All other
|
| + // entries must be back edges.
|
| + for (int i = 1; i < predecessors()->length(); ++i) {
|
| + loop_information()->RegisterBackEdge(predecessors()->at(i));
|
| + }
|
| +}
|
| +
|
| +
|
| +void HBasicBlock::RegisterPredecessor(HBasicBlock* pred) {
|
| + if (!predecessors_.is_empty()) {
|
| + // Only loop header blocks can have a predecessor added after
|
| + // instructions have been added to the block (they have phis for all
|
| + // values in the environment, these phis may be eliminated later).
|
| + ASSERT(IsLoopHeader() || first_ == NULL);
|
| + HEnvironment* incoming_env = pred->last_environment();
|
| + if (IsLoopHeader()) {
|
| + ASSERT(phis()->length() == incoming_env->values()->length());
|
| + for (int i = 0; i < phis_.length(); ++i) {
|
| + phis_[i]->AddInput(incoming_env->values()->at(i));
|
| + }
|
| + } else {
|
| + last_environment()->AddIncomingEdge(this, pred->last_environment());
|
| + }
|
| + } else if (!HasEnvironment() && !IsFinished()) {
|
| + ASSERT(!IsLoopHeader());
|
| + SetInitialEnvironment(pred->last_environment()->Copy());
|
| + }
|
| +
|
| + predecessors_.Add(pred);
|
| +}
|
| +
|
| +
|
| +void HBasicBlock::AddDominatedBlock(HBasicBlock* block) {
|
| + ASSERT(!dominated_blocks_.Contains(block));
|
| + // Keep the list of dominated blocks sorted such that if there is two
|
| + // succeeding block in this list, the predecessor is before the successor.
|
| + int index = 0;
|
| + while (index < dominated_blocks_.length() &&
|
| + dominated_blocks_[index]->block_id() < block->block_id()) {
|
| + ++index;
|
| + }
|
| + dominated_blocks_.InsertAt(index, block);
|
| +}
|
| +
|
| +
|
| +void HBasicBlock::AssignCommonDominator(HBasicBlock* other) {
|
| + if (dominator_ == NULL) {
|
| + dominator_ = other;
|
| + other->AddDominatedBlock(this);
|
| + } else if (other->dominator() != NULL) {
|
| + HBasicBlock* first = dominator_;
|
| + HBasicBlock* second = other;
|
| +
|
| + while (first != second) {
|
| + if (first->block_id() > second->block_id()) {
|
| + first = first->dominator();
|
| + } else {
|
| + second = second->dominator();
|
| + }
|
| + ASSERT(first != NULL && second != NULL);
|
| + }
|
| +
|
| + if (dominator_ != first) {
|
| + ASSERT(dominator_->dominated_blocks_.Contains(this));
|
| + dominator_->dominated_blocks_.RemoveElement(this);
|
| + dominator_ = first;
|
| + first->AddDominatedBlock(this);
|
| + }
|
| + }
|
| +}
|
| +
|
| +
|
| +int HBasicBlock::PredecessorIndexOf(HBasicBlock* predecessor) const {
|
| + for (int i = 0; i < predecessors_.length(); ++i) {
|
| + if (predecessors_[i] == predecessor) return i;
|
| + }
|
| + UNREACHABLE();
|
| + return -1;
|
| +}
|
| +
|
| +
|
| +#ifdef DEBUG
|
| +void HBasicBlock::Verify() {
|
| + // Check that every block is finished.
|
| + ASSERT(IsFinished());
|
| + ASSERT(block_id() >= 0);
|
| +
|
| + // Verify that all blocks targetting a branch target, have the same boolean
|
| + // value on top of their expression stack.
|
| + if (!cond().is_null()) {
|
| + ASSERT(predecessors()->length() > 0);
|
| + for (int i = 1; i < predecessors()->length(); i++) {
|
| + HBasicBlock* pred = predecessors()->at(i);
|
| + HValue* top = pred->last_environment()->Top();
|
| + ASSERT(top->IsConstant());
|
| + Object* a = *HConstant::cast(top)->handle();
|
| + Object* b = *cond();
|
| + ASSERT(a == b);
|
| + }
|
| + }
|
| +}
|
| +#endif
|
| +
|
| +
|
| +void HLoopInformation::RegisterBackEdge(HBasicBlock* block) {
|
| + this->back_edges_.Add(block);
|
| + AddBlock(block);
|
| +}
|
| +
|
| +
|
| +HBasicBlock* HLoopInformation::GetLastBackEdge() const {
|
| + int max_id = -1;
|
| + HBasicBlock* result = NULL;
|
| + for (int i = 0; i < back_edges_.length(); ++i) {
|
| + HBasicBlock* cur = back_edges_[i];
|
| + if (cur->block_id() > max_id) {
|
| + max_id = cur->block_id();
|
| + result = cur;
|
| + }
|
| + }
|
| + return result;
|
| +}
|
| +
|
| +
|
| +void HLoopInformation::AddBlock(HBasicBlock* block) {
|
| + if (block == loop_header()) return;
|
| + if (block->parent_loop_header() == loop_header()) return;
|
| + if (block->parent_loop_header() != NULL) {
|
| + AddBlock(block->parent_loop_header());
|
| + } else {
|
| + block->set_parent_loop_header(loop_header());
|
| + blocks_.Add(block);
|
| + for (int i = 0; i < block->predecessors()->length(); ++i) {
|
| + AddBlock(block->predecessors()->at(i));
|
| + }
|
| + }
|
| +}
|
| +
|
| +
|
| +#ifdef DEBUG
|
| +
|
| +// Checks reachability of the blocks in this graph and stores a bit in
|
| +// the BitVector "reachable()" for every block that can be reached
|
| +// from the start block of the graph. If "dont_visit" is non-null, the given
|
| +// block is treated as if it would not be part of the graph. "visited_count()"
|
| +// returns the number of reachable blocks.
|
| +class ReachabilityAnalyzer BASE_EMBEDDED {
|
| + public:
|
| + ReachabilityAnalyzer(HBasicBlock* entry_block,
|
| + int block_count,
|
| + HBasicBlock* dont_visit)
|
| + : visited_count_(0),
|
| + stack_(16),
|
| + reachable_(block_count),
|
| + dont_visit_(dont_visit) {
|
| + PushBlock(entry_block);
|
| + Analyze();
|
| + }
|
| +
|
| + int visited_count() const { return visited_count_; }
|
| + const BitVector* reachable() const { return &reachable_; }
|
| +
|
| + private:
|
| + void PushBlock(HBasicBlock* block) {
|
| + if (block != NULL && block != dont_visit_ &&
|
| + !reachable_.Contains(block->block_id())) {
|
| + reachable_.Add(block->block_id());
|
| + stack_.Add(block);
|
| + visited_count_++;
|
| + }
|
| + }
|
| +
|
| + void Analyze() {
|
| + while (!stack_.is_empty()) {
|
| + HControlInstruction* end = stack_.RemoveLast()->end();
|
| + PushBlock(end->FirstSuccessor());
|
| + PushBlock(end->SecondSuccessor());
|
| + }
|
| + }
|
| +
|
| + int visited_count_;
|
| + ZoneList<HBasicBlock*> stack_;
|
| + BitVector reachable_;
|
| + HBasicBlock* dont_visit_;
|
| +};
|
| +
|
| +
|
| +void HGraph::Verify() const {
|
| + for (int i = 0; i < blocks_.length(); i++) {
|
| + HBasicBlock* block = blocks_.at(i);
|
| +
|
| + block->Verify();
|
| +
|
| + // Check that every block contains at least one node and that only the last
|
| + // node is a control instruction.
|
| + HInstruction* current = block->first();
|
| + ASSERT(current != NULL && current->IsBlockEntry());
|
| + while (current != NULL) {
|
| + ASSERT((current->next() == NULL) == current->IsControlInstruction());
|
| + ASSERT(current->block() == block);
|
| + current->Verify();
|
| + current = current->next();
|
| + }
|
| +
|
| + // Check that successors are correctly set.
|
| + HBasicBlock* first = block->end()->FirstSuccessor();
|
| + HBasicBlock* second = block->end()->SecondSuccessor();
|
| + ASSERT(second == NULL || first != NULL);
|
| +
|
| + // Check that the predecessor array is correct.
|
| + if (first != NULL) {
|
| + ASSERT(first->predecessors()->Contains(block));
|
| + if (second != NULL) {
|
| + ASSERT(second->predecessors()->Contains(block));
|
| + }
|
| + }
|
| +
|
| + // Check that phis have correct arguments.
|
| + for (int j = 0; j < block->phis()->length(); j++) {
|
| + HPhi* phi = block->phis()->at(j);
|
| + phi->Verify();
|
| + }
|
| +
|
| + // Check that all join blocks have predecessors that end with an
|
| + // unconditional goto and agree on their environment node id.
|
| + if (block->predecessors()->length() >= 2) {
|
| + int id = block->predecessors()->first()->last_environment()->ast_id();
|
| + for (int k = 0; k < block->predecessors()->length(); k++) {
|
| + HBasicBlock* predecessor = block->predecessors()->at(k);
|
| + ASSERT(predecessor->end()->IsGoto());
|
| + ASSERT(predecessor->last_environment()->ast_id() == id);
|
| + }
|
| + }
|
| + }
|
| +
|
| + // Check special property of first block to have no predecessors.
|
| + ASSERT(blocks_.at(0)->predecessors()->is_empty());
|
| +
|
| + // Check that the graph is fully connected.
|
| + ReachabilityAnalyzer analyzer(entry_block_, blocks_.length(), NULL);
|
| + ASSERT(analyzer.visited_count() == blocks_.length());
|
| +
|
| + // Check that entry block dominator is NULL.
|
| + ASSERT(entry_block_->dominator() == NULL);
|
| +
|
| + // Check dominators.
|
| + for (int i = 0; i < blocks_.length(); ++i) {
|
| + HBasicBlock* block = blocks_.at(i);
|
| + if (block->dominator() == NULL) {
|
| + // Only start block may have no dominator assigned to.
|
| + ASSERT(i == 0);
|
| + } else {
|
| + // Assert that block is unreachable if dominator must not be visited.
|
| + ReachabilityAnalyzer dominator_analyzer(entry_block_,
|
| + blocks_.length(),
|
| + block->dominator());
|
| + ASSERT(!dominator_analyzer.reachable()->Contains(block->block_id()));
|
| + }
|
| + }
|
| +}
|
| +
|
| +#endif
|
| +
|
| +
|
| +HConstant* HGraph::GetConstant(SetOncePointer<HConstant>* pointer,
|
| + Object* value) {
|
| + if (!pointer->is_set()) {
|
| + HConstant* constant = new HConstant(Handle<Object>(value),
|
| + Representation::Tagged());
|
| + constant->InsertAfter(GetConstantUndefined());
|
| + pointer->set(constant);
|
| + }
|
| + return pointer->get();
|
| +}
|
| +
|
| +
|
| +HConstant* HGraph::GetConstant1() {
|
| + return GetConstant(&constant_1_, Smi::FromInt(1));
|
| +}
|
| +
|
| +
|
| +HConstant* HGraph::GetConstantMinus1() {
|
| + return GetConstant(&constant_minus1_, Smi::FromInt(-1));
|
| +}
|
| +
|
| +
|
| +HConstant* HGraph::GetConstantTrue() {
|
| + return GetConstant(&constant_true_, HEAP->true_value());
|
| +}
|
| +
|
| +
|
| +HConstant* HGraph::GetConstantFalse() {
|
| + return GetConstant(&constant_false_, HEAP->false_value());
|
| +}
|
| +
|
| +
|
| +void HSubgraph::AppendOptional(HSubgraph* graph,
|
| + bool on_true_branch,
|
| + HValue* boolean_value) {
|
| + ASSERT(HasExit() && graph->HasExit());
|
| + HBasicBlock* other_block = graph_->CreateBasicBlock();
|
| + HBasicBlock* join_block = graph_->CreateBasicBlock();
|
| +
|
| + HBasicBlock* true_branch = other_block;
|
| + HBasicBlock* false_branch = graph->entry_block();
|
| + if (on_true_branch) {
|
| + true_branch = graph->entry_block();
|
| + false_branch = other_block;
|
| + }
|
| +
|
| + exit_block_->Finish(new HBranch(true_branch, false_branch, boolean_value));
|
| + other_block->Goto(join_block);
|
| + graph->exit_block()->Goto(join_block);
|
| + exit_block_ = join_block;
|
| +}
|
| +
|
| +
|
| +void HSubgraph::AppendJoin(HSubgraph* then_graph,
|
| + HSubgraph* else_graph,
|
| + AstNode* node) {
|
| + if (then_graph->HasExit() && else_graph->HasExit()) {
|
| + // We need to merge, create new merge block.
|
| + HBasicBlock* join_block = graph_->CreateBasicBlock();
|
| + then_graph->exit_block()->Goto(join_block);
|
| + else_graph->exit_block()->Goto(join_block);
|
| + join_block->SetJoinId(node->id());
|
| + exit_block_ = join_block;
|
| + } else if (then_graph->HasExit()) {
|
| + exit_block_ = then_graph->exit_block_;
|
| + } else if (else_graph->HasExit()) {
|
| + exit_block_ = else_graph->exit_block_;
|
| + } else {
|
| + exit_block_ = NULL;
|
| + }
|
| +}
|
| +
|
| +
|
| +void HSubgraph::ResolveContinue(IterationStatement* statement) {
|
| + HBasicBlock* continue_block = BundleContinue(statement);
|
| + if (continue_block != NULL) {
|
| + exit_block_ = JoinBlocks(exit_block(),
|
| + continue_block,
|
| + statement->ContinueId());
|
| + }
|
| +}
|
| +
|
| +
|
| +HBasicBlock* HSubgraph::BundleBreak(BreakableStatement* statement) {
|
| + return BundleBreakContinue(statement, false, statement->ExitId());
|
| +}
|
| +
|
| +
|
| +HBasicBlock* HSubgraph::BundleContinue(IterationStatement* statement) {
|
| + return BundleBreakContinue(statement, true, statement->ContinueId());
|
| +}
|
| +
|
| +
|
| +HBasicBlock* HSubgraph::BundleBreakContinue(BreakableStatement* statement,
|
| + bool is_continue,
|
| + int join_id) {
|
| + HBasicBlock* result = NULL;
|
| + const ZoneList<BreakContinueInfo*>* infos = break_continue_info();
|
| + for (int i = 0; i < infos->length(); ++i) {
|
| + BreakContinueInfo* info = infos->at(i);
|
| + if (info->is_continue() == is_continue &&
|
| + info->target() == statement &&
|
| + !info->IsResolved()) {
|
| + if (result == NULL) {
|
| + result = graph_->CreateBasicBlock();
|
| + }
|
| + info->block()->Goto(result);
|
| + info->Resolve();
|
| + }
|
| + }
|
| +
|
| + if (result != NULL) result->SetJoinId(join_id);
|
| +
|
| + return result;
|
| +}
|
| +
|
| +
|
| +HBasicBlock* HSubgraph::JoinBlocks(HBasicBlock* a, HBasicBlock* b, int id) {
|
| + if (a == NULL) return b;
|
| + if (b == NULL) return a;
|
| + HBasicBlock* target = graph_->CreateBasicBlock();
|
| + a->Goto(target);
|
| + b->Goto(target);
|
| + target->SetJoinId(id);
|
| + return target;
|
| +}
|
| +
|
| +
|
| +void HSubgraph::AppendEndless(HSubgraph* body, IterationStatement* statement) {
|
| + ConnectExitTo(body->entry_block());
|
| + body->ResolveContinue(statement);
|
| + body->ConnectExitTo(body->entry_block(), true);
|
| + exit_block_ = body->BundleBreak(statement);
|
| + body->entry_block()->PostProcessLoopHeader(statement);
|
| +}
|
| +
|
| +
|
| +void HSubgraph::AppendDoWhile(HSubgraph* body,
|
| + IterationStatement* statement,
|
| + HSubgraph* go_back,
|
| + HSubgraph* exit) {
|
| + ConnectExitTo(body->entry_block());
|
| + go_back->ConnectExitTo(body->entry_block(), true);
|
| +
|
| + HBasicBlock* break_block = body->BundleBreak(statement);
|
| + exit_block_ =
|
| + JoinBlocks(exit->exit_block(), break_block, statement->ExitId());
|
| + body->entry_block()->PostProcessLoopHeader(statement);
|
| +}
|
| +
|
| +
|
| +void HSubgraph::AppendWhile(HSubgraph* condition,
|
| + HSubgraph* body,
|
| + IterationStatement* statement,
|
| + HSubgraph* continue_subgraph,
|
| + HSubgraph* exit) {
|
| + ConnectExitTo(condition->entry_block());
|
| +
|
| + HBasicBlock* break_block = body->BundleBreak(statement);
|
| + exit_block_ =
|
| + JoinBlocks(exit->exit_block(), break_block, statement->ExitId());
|
| +
|
| + if (continue_subgraph != NULL) {
|
| + body->ConnectExitTo(continue_subgraph->entry_block(), true);
|
| + continue_subgraph->entry_block()->SetJoinId(statement->EntryId());
|
| + exit_block_ = JoinBlocks(exit_block_,
|
| + continue_subgraph->exit_block(),
|
| + statement->ExitId());
|
| + } else {
|
| + body->ConnectExitTo(condition->entry_block(), true);
|
| + }
|
| + condition->entry_block()->PostProcessLoopHeader(statement);
|
| +}
|
| +
|
| +
|
| +void HSubgraph::Append(HSubgraph* next, BreakableStatement* stmt) {
|
| + exit_block_->Goto(next->entry_block());
|
| + exit_block_ = next->exit_block_;
|
| +
|
| + if (stmt != NULL) {
|
| + next->entry_block()->SetJoinId(stmt->EntryId());
|
| + HBasicBlock* break_block = next->BundleBreak(stmt);
|
| + exit_block_ = JoinBlocks(exit_block(), break_block, stmt->ExitId());
|
| + }
|
| +}
|
| +
|
| +
|
| +void HSubgraph::FinishExit(HControlInstruction* instruction) {
|
| + ASSERT(HasExit());
|
| + exit_block_->Finish(instruction);
|
| + exit_block_->ClearEnvironment();
|
| + exit_block_ = NULL;
|
| +}
|
| +
|
| +
|
| +void HSubgraph::FinishBreakContinue(BreakableStatement* target,
|
| + bool is_continue) {
|
| + ASSERT(!exit_block_->IsFinished());
|
| + BreakContinueInfo* info = new BreakContinueInfo(target, exit_block_,
|
| + is_continue);
|
| + break_continue_info_.Add(info);
|
| + exit_block_ = NULL;
|
| +}
|
| +
|
| +
|
| +HGraph::HGraph(CompilationInfo* info)
|
| + : HSubgraph(this),
|
| + next_block_id_(0),
|
| + info_(info),
|
| + blocks_(8),
|
| + values_(16),
|
| + phi_list_(NULL) {
|
| + start_environment_ = new HEnvironment(NULL, info->scope(), info->closure());
|
| + start_environment_->set_ast_id(info->function()->id());
|
| +}
|
| +
|
| +
|
| +Handle<Code> HGraph::Compile() {
|
| + int values = GetMaximumValueID();
|
| + if (values > LAllocator::max_initial_value_ids()) {
|
| + if (FLAG_trace_bailout) PrintF("Function is too big\n");
|
| + return Handle<Code>::null();
|
| + }
|
| +
|
| + LAllocator allocator(values, this);
|
| + LChunkBuilder builder(this, &allocator);
|
| + LChunk* chunk = builder.Build();
|
| + if (chunk == NULL) return Handle<Code>::null();
|
| +
|
| + if (!FLAG_alloc_lithium) return Handle<Code>::null();
|
| +
|
| + allocator.Allocate(chunk);
|
| +
|
| + if (!FLAG_use_lithium) return Handle<Code>::null();
|
| +
|
| + MacroAssembler assembler(NULL, 0);
|
| + LCodeGen generator(chunk, &assembler, info());
|
| +
|
| + if (FLAG_eliminate_empty_blocks) {
|
| + chunk->MarkEmptyBlocks();
|
| + }
|
| +
|
| + if (generator.GenerateCode()) {
|
| + if (FLAG_trace_codegen) {
|
| + PrintF("Crankshaft Compiler - ");
|
| + }
|
| + CodeGenerator::MakeCodePrologue(info());
|
| + Code::Flags flags =
|
| + Code::ComputeFlags(Code::OPTIMIZED_FUNCTION, NOT_IN_LOOP);
|
| + Handle<Code> code =
|
| + CodeGenerator::MakeCodeEpilogue(&assembler, flags, info());
|
| + generator.FinishCode(code);
|
| + CodeGenerator::PrintCode(code, info());
|
| + return code;
|
| + }
|
| + return Handle<Code>::null();
|
| +}
|
| +
|
| +
|
| +HBasicBlock* HGraph::CreateBasicBlock() {
|
| + HBasicBlock* result = new HBasicBlock(this);
|
| + blocks_.Add(result);
|
| + return result;
|
| +}
|
| +
|
| +
|
| +void HGraph::Canonicalize() {
|
| + HPhase phase("Canonicalize", this);
|
| + if (FLAG_use_canonicalizing) {
|
| + for (int i = 0; i < blocks()->length(); ++i) {
|
| + HBasicBlock* b = blocks()->at(i);
|
| + for (HInstruction* insn = b->first(); insn != NULL; insn = insn->next()) {
|
| + HValue* value = insn->Canonicalize();
|
| + if (value != insn) {
|
| + if (value != NULL) {
|
| + insn->ReplaceAndDelete(value);
|
| + } else {
|
| + insn->Delete();
|
| + }
|
| + }
|
| + }
|
| + }
|
| + }
|
| +}
|
| +
|
| +
|
| +void HGraph::OrderBlocks() {
|
| + HPhase phase("Block ordering");
|
| + BitVector visited(blocks_.length());
|
| +
|
| + ZoneList<HBasicBlock*> reverse_result(8);
|
| + HBasicBlock* start = blocks_[0];
|
| + Postorder(start, &visited, &reverse_result, NULL);
|
| +
|
| + blocks_.Clear();
|
| + int index = 0;
|
| + for (int i = reverse_result.length() - 1; i >= 0; --i) {
|
| + HBasicBlock* b = reverse_result[i];
|
| + blocks_.Add(b);
|
| + b->set_block_id(index++);
|
| + }
|
| +}
|
| +
|
| +
|
| +void HGraph::PostorderLoopBlocks(HLoopInformation* loop,
|
| + BitVector* visited,
|
| + ZoneList<HBasicBlock*>* order,
|
| + HBasicBlock* loop_header) {
|
| + for (int i = 0; i < loop->blocks()->length(); ++i) {
|
| + HBasicBlock* b = loop->blocks()->at(i);
|
| + Postorder(b->end()->SecondSuccessor(), visited, order, loop_header);
|
| + Postorder(b->end()->FirstSuccessor(), visited, order, loop_header);
|
| + if (b->IsLoopHeader() && b != loop->loop_header()) {
|
| + PostorderLoopBlocks(b->loop_information(), visited, order, loop_header);
|
| + }
|
| + }
|
| +}
|
| +
|
| +
|
| +void HGraph::Postorder(HBasicBlock* block,
|
| + BitVector* visited,
|
| + ZoneList<HBasicBlock*>* order,
|
| + HBasicBlock* loop_header) {
|
| + if (block == NULL || visited->Contains(block->block_id())) return;
|
| + if (block->parent_loop_header() != loop_header) return;
|
| + visited->Add(block->block_id());
|
| + if (block->IsLoopHeader()) {
|
| + PostorderLoopBlocks(block->loop_information(), visited, order, loop_header);
|
| + Postorder(block->end()->SecondSuccessor(), visited, order, block);
|
| + Postorder(block->end()->FirstSuccessor(), visited, order, block);
|
| + } else {
|
| + Postorder(block->end()->SecondSuccessor(), visited, order, loop_header);
|
| + Postorder(block->end()->FirstSuccessor(), visited, order, loop_header);
|
| + }
|
| + ASSERT(block->end()->FirstSuccessor() == NULL ||
|
| + order->Contains(block->end()->FirstSuccessor()) ||
|
| + block->end()->FirstSuccessor()->IsLoopHeader());
|
| + ASSERT(block->end()->SecondSuccessor() == NULL ||
|
| + order->Contains(block->end()->SecondSuccessor()) ||
|
| + block->end()->SecondSuccessor()->IsLoopHeader());
|
| + order->Add(block);
|
| +}
|
| +
|
| +
|
| +void HGraph::AssignDominators() {
|
| + HPhase phase("Assign dominators", this);
|
| + for (int i = 0; i < blocks_.length(); ++i) {
|
| + if (blocks_[i]->IsLoopHeader()) {
|
| + blocks_[i]->AssignCommonDominator(blocks_[i]->predecessors()->first());
|
| + } else {
|
| + for (int j = 0; j < blocks_[i]->predecessors()->length(); ++j) {
|
| + blocks_[i]->AssignCommonDominator(blocks_[i]->predecessors()->at(j));
|
| + }
|
| + }
|
| + }
|
| +}
|
| +
|
| +
|
| +void HGraph::EliminateRedundantPhis() {
|
| + HPhase phase("Phi elimination", this);
|
| + ZoneList<HValue*> uses_to_replace(2);
|
| +
|
| + // Worklist of phis that can potentially be eliminated. Initialized
|
| + // with all phi nodes. When elimination of a phi node modifies
|
| + // another phi node the modified phi node is added to the worklist.
|
| + ZoneList<HPhi*> worklist(blocks_.length());
|
| + for (int i = 0; i < blocks_.length(); ++i) {
|
| + worklist.AddAll(*blocks_[i]->phis());
|
| + }
|
| +
|
| + while (!worklist.is_empty()) {
|
| + HPhi* phi = worklist.RemoveLast();
|
| + HBasicBlock* block = phi->block();
|
| +
|
| + // Skip phi node if it was already replaced.
|
| + if (block == NULL) continue;
|
| +
|
| + // Get replacement value if phi is redundant.
|
| + HValue* value = phi->GetRedundantReplacement();
|
| +
|
| + if (value != NULL) {
|
| + // Iterate through uses finding the ones that should be
|
| + // replaced.
|
| + const ZoneList<HValue*>* uses = phi->uses();
|
| + for (int i = 0; i < uses->length(); ++i) {
|
| + HValue* use = uses->at(i);
|
| + if (!use->block()->IsStartBlock()) {
|
| + uses_to_replace.Add(use);
|
| + }
|
| + }
|
| + // Replace the uses and add phis modified to the work list.
|
| + for (int i = 0; i < uses_to_replace.length(); ++i) {
|
| + HValue* use = uses_to_replace[i];
|
| + phi->ReplaceAtUse(use, value);
|
| + if (use->IsPhi()) worklist.Add(HPhi::cast(use));
|
| + }
|
| + uses_to_replace.Rewind(0);
|
| + block->RemovePhi(phi);
|
| + } else if (phi->HasNoUses() &&
|
| + !phi->HasReceiverOperand() &&
|
| + FLAG_eliminate_dead_phis) {
|
| + // We can't eliminate phis that have the receiver as an operand
|
| + // because in case of throwing an error we need the correct
|
| + // receiver value in the environment to construct a corrent
|
| + // stack trace.
|
| + block->RemovePhi(phi);
|
| + block->RecordDeletedPhi(phi->merged_index());
|
| + }
|
| + }
|
| +}
|
| +
|
| +
|
| +bool HGraph::CollectPhis() {
|
| + const ZoneList<HBasicBlock*>* blocks = graph_->blocks();
|
| + phi_list_ = new ZoneList<HPhi*>(blocks->length());
|
| + for (int i = 0; i < blocks->length(); ++i) {
|
| + for (int j = 0; j < blocks->at(i)->phis()->length(); j++) {
|
| + HPhi* phi = blocks->at(i)->phis()->at(j);
|
| + phi_list_->Add(phi);
|
| + // We don't support phi uses of arguments for now.
|
| + if (phi->CheckFlag(HValue::kIsArguments)) return false;
|
| + }
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +
|
| +void HGraph::InferTypes(ZoneList<HValue*>* worklist) {
|
| + BitVector in_worklist(GetMaximumValueID());
|
| + for (int i = 0; i < worklist->length(); ++i) {
|
| + ASSERT(!in_worklist.Contains(worklist->at(i)->id()));
|
| + in_worklist.Add(worklist->at(i)->id());
|
| + }
|
| +
|
| + while (!worklist->is_empty()) {
|
| + HValue* current = worklist->RemoveLast();
|
| + in_worklist.Remove(current->id());
|
| + if (current->UpdateInferredType()) {
|
| + for (int j = 0; j < current->uses()->length(); j++) {
|
| + HValue* use = current->uses()->at(j);
|
| + if (!in_worklist.Contains(use->id())) {
|
| + in_worklist.Add(use->id());
|
| + worklist->Add(use);
|
| + }
|
| + }
|
| + }
|
| + }
|
| +}
|
| +
|
| +
|
| +class HRangeAnalysis BASE_EMBEDDED {
|
| + public:
|
| + explicit HRangeAnalysis(HGraph* graph) : graph_(graph), changed_ranges_(16) {}
|
| +
|
| + void Analyze();
|
| +
|
| + private:
|
| + void TraceRange(const char* msg, ...);
|
| + void Analyze(HBasicBlock* block);
|
| + void InferControlFlowRange(HBranch* branch, HBasicBlock* dest);
|
| + void InferControlFlowRange(Token::Value op, HValue* value, HValue* other);
|
| + void InferPhiRange(HPhi* phi);
|
| + void InferRange(HValue* value);
|
| + void RollBackTo(int index);
|
| + void AddRange(HValue* value, Range* range);
|
| +
|
| + HGraph* graph_;
|
| + ZoneList<HValue*> changed_ranges_;
|
| +};
|
| +
|
| +
|
| +void HRangeAnalysis::TraceRange(const char* msg, ...) {
|
| + if (FLAG_trace_range) {
|
| + va_list arguments;
|
| + va_start(arguments, msg);
|
| + OS::VPrint(msg, arguments);
|
| + va_end(arguments);
|
| + }
|
| +}
|
| +
|
| +
|
| +void HRangeAnalysis::Analyze() {
|
| + HPhase phase("Range analysis", graph_);
|
| + Analyze(graph_->blocks()->at(0));
|
| +}
|
| +
|
| +
|
| +void HRangeAnalysis::Analyze(HBasicBlock* block) {
|
| + TraceRange("Analyzing block B%d\n", block->block_id());
|
| +
|
| + int last_changed_range = changed_ranges_.length() - 1;
|
| +
|
| + // Infer range based on control flow.
|
| + if (block->predecessors()->length() == 1) {
|
| + HBasicBlock* pred = block->predecessors()->first();
|
| + if (pred->end()->IsBranch()) {
|
| + InferControlFlowRange(HBranch::cast(pred->end()), block);
|
| + }
|
| + }
|
| +
|
| + // Process phi instructions.
|
| + for (int i = 0; i < block->phis()->length(); ++i) {
|
| + HPhi* phi = block->phis()->at(i);
|
| + InferPhiRange(phi);
|
| + }
|
| +
|
| + // Go through all instructions of the current block.
|
| + HInstruction* instr = block->first();
|
| + while (instr != block->end()) {
|
| + InferRange(instr);
|
| + instr = instr->next();
|
| + }
|
| +
|
| + // Continue analysis in all dominated blocks.
|
| + for (int i = 0; i < block->dominated_blocks()->length(); ++i) {
|
| + Analyze(block->dominated_blocks()->at(i));
|
| + }
|
| +
|
| + RollBackTo(last_changed_range);
|
| +}
|
| +
|
| +
|
| +void HRangeAnalysis::InferControlFlowRange(HBranch* branch, HBasicBlock* dest) {
|
| + ASSERT(branch->FirstSuccessor() == dest || branch->SecondSuccessor() == dest);
|
| + ASSERT(branch->FirstSuccessor() != dest || branch->SecondSuccessor() != dest);
|
| +
|
| + if (branch->value()->IsCompare()) {
|
| + HCompare* compare = HCompare::cast(branch->value());
|
| + Token::Value op = compare->token();
|
| + if (branch->SecondSuccessor() == dest) {
|
| + op = Token::NegateCompareOp(op);
|
| + }
|
| + Token::Value inverted_op = Token::InvertCompareOp(op);
|
| + InferControlFlowRange(op, compare->left(), compare->right());
|
| + InferControlFlowRange(inverted_op, compare->right(), compare->left());
|
| + }
|
| +}
|
| +
|
| +
|
| +// We know that value [op] other. Use this information to update the range on
|
| +// value.
|
| +void HRangeAnalysis::InferControlFlowRange(Token::Value op,
|
| + HValue* value,
|
| + HValue* other) {
|
| + Range* range = other->range();
|
| + if (range == NULL) range = new Range();
|
| + Range* new_range = NULL;
|
| +
|
| + TraceRange("Control flow range infer %d %s %d\n",
|
| + value->id(),
|
| + Token::Name(op),
|
| + other->id());
|
| +
|
| + if (op == Token::EQ || op == Token::EQ_STRICT) {
|
| + // The same range has to apply for value.
|
| + new_range = range->Copy();
|
| + } else if (op == Token::LT || op == Token::LTE) {
|
| + new_range = range->CopyClearLower();
|
| + if (op == Token::LT) {
|
| + new_range->Add(-1);
|
| + }
|
| + } else if (op == Token::GT || op == Token::GTE) {
|
| + new_range = range->CopyClearUpper();
|
| + if (op == Token::GT) {
|
| + new_range->Add(1);
|
| + }
|
| + }
|
| +
|
| + if (new_range != NULL && !new_range->IsMostGeneric()) {
|
| + AddRange(value, new_range);
|
| + }
|
| +}
|
| +
|
| +
|
| +void HRangeAnalysis::InferPhiRange(HPhi* phi) {
|
| + // TODO(twuerthinger): Infer loop phi ranges.
|
| + InferRange(phi);
|
| +}
|
| +
|
| +
|
| +void HRangeAnalysis::InferRange(HValue* value) {
|
| + ASSERT(!value->HasRange());
|
| + if (!value->representation().IsNone()) {
|
| + value->ComputeInitialRange();
|
| + Range* range = value->range();
|
| + TraceRange("Initial inferred range of %d (%s) set to [%d,%d]\n",
|
| + value->id(),
|
| + value->Mnemonic(),
|
| + range->lower(),
|
| + range->upper());
|
| + }
|
| +}
|
| +
|
| +
|
| +void HRangeAnalysis::RollBackTo(int index) {
|
| + for (int i = index + 1; i < changed_ranges_.length(); ++i) {
|
| + changed_ranges_[i]->RemoveLastAddedRange();
|
| + }
|
| + changed_ranges_.Rewind(index + 1);
|
| +}
|
| +
|
| +
|
| +void HRangeAnalysis::AddRange(HValue* value, Range* range) {
|
| + Range* original_range = value->range();
|
| + value->AddNewRange(range);
|
| + changed_ranges_.Add(value);
|
| + Range* new_range = value->range();
|
| + TraceRange("Updated range of %d set to [%d,%d]\n",
|
| + value->id(),
|
| + new_range->lower(),
|
| + new_range->upper());
|
| + if (original_range != NULL) {
|
| + TraceRange("Original range was [%d,%d]\n",
|
| + original_range->lower(),
|
| + original_range->upper());
|
| + }
|
| + TraceRange("New information was [%d,%d]\n",
|
| + range->lower(),
|
| + range->upper());
|
| +}
|
| +
|
| +
|
| +void TraceGVN(const char* msg, ...) {
|
| + if (FLAG_trace_gvn) {
|
| + va_list arguments;
|
| + va_start(arguments, msg);
|
| + OS::VPrint(msg, arguments);
|
| + va_end(arguments);
|
| + }
|
| +}
|
| +
|
| +
|
| +HValueMap::HValueMap(const HValueMap* other)
|
| + : array_size_(other->array_size_),
|
| + lists_size_(other->lists_size_),
|
| + count_(other->count_),
|
| + present_flags_(other->present_flags_),
|
| + array_(ZONE->NewArray<HValueMapListElement>(other->array_size_)),
|
| + lists_(ZONE->NewArray<HValueMapListElement>(other->lists_size_)),
|
| + free_list_head_(other->free_list_head_) {
|
| + memcpy(array_, other->array_, array_size_ * sizeof(HValueMapListElement));
|
| + memcpy(lists_, other->lists_, lists_size_ * sizeof(HValueMapListElement));
|
| +}
|
| +
|
| +
|
| +void HValueMap::Kill(int flags) {
|
| + int depends_flags = HValue::ConvertChangesToDependsFlags(flags);
|
| + if ((present_flags_ & depends_flags) == 0) return;
|
| + present_flags_ = 0;
|
| + for (int i = 0; i < array_size_; ++i) {
|
| + HValue* value = array_[i].value;
|
| + if (value != NULL) {
|
| + // Clear list of collisions first, so we know if it becomes empty.
|
| + int kept = kNil; // List of kept elements.
|
| + int next;
|
| + for (int current = array_[i].next; current != kNil; current = next) {
|
| + next = lists_[current].next;
|
| + if ((lists_[current].value->flags() & depends_flags) != 0) {
|
| + // Drop it.
|
| + count_--;
|
| + lists_[current].next = free_list_head_;
|
| + free_list_head_ = current;
|
| + } else {
|
| + // Keep it.
|
| + lists_[current].next = kept;
|
| + kept = current;
|
| + present_flags_ |= lists_[current].value->flags();
|
| + }
|
| + }
|
| + array_[i].next = kept;
|
| +
|
| + // Now possibly drop directly indexed element.
|
| + if ((array_[i].value->flags() & depends_flags) != 0) { // Drop it.
|
| + count_--;
|
| + int head = array_[i].next;
|
| + if (head == kNil) {
|
| + array_[i].value = NULL;
|
| + } else {
|
| + array_[i].value = lists_[head].value;
|
| + array_[i].next = lists_[head].next;
|
| + lists_[head].next = free_list_head_;
|
| + free_list_head_ = head;
|
| + }
|
| + } else {
|
| + present_flags_ |= array_[i].value->flags(); // Keep it.
|
| + }
|
| + }
|
| + }
|
| +}
|
| +
|
| +
|
| +HValue* HValueMap::Lookup(HValue* value) const {
|
| + uint32_t hash = value->Hashcode();
|
| + uint32_t pos = Bound(hash);
|
| + if (array_[pos].value != NULL) {
|
| + if (array_[pos].value->Equals(value)) return array_[pos].value;
|
| + int next = array_[pos].next;
|
| + while (next != kNil) {
|
| + if (lists_[next].value->Equals(value)) return lists_[next].value;
|
| + next = lists_[next].next;
|
| + }
|
| + }
|
| + return NULL;
|
| +}
|
| +
|
| +
|
| +void HValueMap::Resize(int new_size) {
|
| + ASSERT(new_size > count_);
|
| + // Hashing the values into the new array has no more collisions than in the
|
| + // old hash map, so we can use the existing lists_ array, if we are careful.
|
| +
|
| + // Make sure we have at least one free element.
|
| + if (free_list_head_ == kNil) {
|
| + ResizeLists(lists_size_ << 1);
|
| + }
|
| +
|
| + HValueMapListElement* new_array =
|
| + ZONE->NewArray<HValueMapListElement>(new_size);
|
| + memset(new_array, 0, sizeof(HValueMapListElement) * new_size);
|
| +
|
| + HValueMapListElement* old_array = array_;
|
| + int old_size = array_size_;
|
| +
|
| + int old_count = count_;
|
| + count_ = 0;
|
| + // Do not modify present_flags_. It is currently correct.
|
| + array_size_ = new_size;
|
| + array_ = new_array;
|
| +
|
| + if (old_array != NULL) {
|
| + // Iterate over all the elements in lists, rehashing them.
|
| + for (int i = 0; i < old_size; ++i) {
|
| + if (old_array[i].value != NULL) {
|
| + int current = old_array[i].next;
|
| + while (current != kNil) {
|
| + Insert(lists_[current].value);
|
| + int next = lists_[current].next;
|
| + lists_[current].next = free_list_head_;
|
| + free_list_head_ = current;
|
| + current = next;
|
| + }
|
| + // Rehash the directly stored value.
|
| + Insert(old_array[i].value);
|
| + }
|
| + }
|
| + }
|
| + USE(old_count);
|
| + ASSERT(count_ == old_count);
|
| +}
|
| +
|
| +
|
| +void HValueMap::ResizeLists(int new_size) {
|
| + ASSERT(new_size > lists_size_);
|
| +
|
| + HValueMapListElement* new_lists =
|
| + ZONE->NewArray<HValueMapListElement>(new_size);
|
| + memset(new_lists, 0, sizeof(HValueMapListElement) * new_size);
|
| +
|
| + HValueMapListElement* old_lists = lists_;
|
| + int old_size = lists_size_;
|
| +
|
| + lists_size_ = new_size;
|
| + lists_ = new_lists;
|
| +
|
| + if (old_lists != NULL) {
|
| + memcpy(lists_, old_lists, old_size * sizeof(HValueMapListElement));
|
| + }
|
| + for (int i = old_size; i < lists_size_; ++i) {
|
| + lists_[i].next = free_list_head_;
|
| + free_list_head_ = i;
|
| + }
|
| +}
|
| +
|
| +
|
| +void HValueMap::Insert(HValue* value) {
|
| + ASSERT(value != NULL);
|
| + // Resizing when half of the hashtable is filled up.
|
| + if (count_ >= array_size_ >> 1) Resize(array_size_ << 1);
|
| + ASSERT(count_ < array_size_);
|
| + count_++;
|
| + uint32_t pos = Bound(value->Hashcode());
|
| + if (array_[pos].value == NULL) {
|
| + array_[pos].value = value;
|
| + array_[pos].next = kNil;
|
| + } else {
|
| + if (free_list_head_ == kNil) {
|
| + ResizeLists(lists_size_ << 1);
|
| + }
|
| + int new_element_pos = free_list_head_;
|
| + ASSERT(new_element_pos != kNil);
|
| + free_list_head_ = lists_[free_list_head_].next;
|
| + lists_[new_element_pos].value = value;
|
| + lists_[new_element_pos].next = array_[pos].next;
|
| + ASSERT(array_[pos].next == kNil || lists_[array_[pos].next].value != NULL);
|
| + array_[pos].next = new_element_pos;
|
| + }
|
| +}
|
| +
|
| +
|
| +class HStackCheckEliminator BASE_EMBEDDED {
|
| + public:
|
| + explicit HStackCheckEliminator(HGraph* graph) : graph_(graph) { }
|
| +
|
| + void Process();
|
| +
|
| + private:
|
| + void RemoveStackCheck(HBasicBlock* block);
|
| +
|
| + HGraph* graph_;
|
| +};
|
| +
|
| +
|
| +void HStackCheckEliminator::Process() {
|
| + // For each loop block walk the dominator tree from the backwards branch to
|
| + // the loop header. If a call instruction is encountered the backwards branch
|
| + // is dominated by a call and the stack check in the backwards branch can be
|
| + // removed.
|
| + for (int i = 0; i < graph_->blocks()->length(); i++) {
|
| + HBasicBlock* block = graph_->blocks()->at(i);
|
| + if (block->IsLoopHeader()) {
|
| + HBasicBlock* backedge = block->loop_information()->GetLastBackEdge();
|
| + HBasicBlock* dominator = backedge;
|
| + bool backedge_dominated_by_call = false;
|
| + while (dominator != block && !backedge_dominated_by_call) {
|
| + HInstruction* instr = dominator->first();
|
| + while (instr != NULL && !backedge_dominated_by_call) {
|
| + if (instr->IsCall()) {
|
| + RemoveStackCheck(backedge);
|
| + backedge_dominated_by_call = true;
|
| + }
|
| + instr = instr->next();
|
| + }
|
| + dominator = dominator->dominator();
|
| + }
|
| + }
|
| + }
|
| +}
|
| +
|
| +
|
| +void HStackCheckEliminator::RemoveStackCheck(HBasicBlock* block) {
|
| + HInstruction* instr = block->first();
|
| + while (instr != NULL) {
|
| + if (instr->IsGoto()) {
|
| + HGoto::cast(instr)->set_include_stack_check(false);
|
| + return;
|
| + }
|
| + instr = instr->next();
|
| + }
|
| +}
|
| +
|
| +
|
| +class HGlobalValueNumberer BASE_EMBEDDED {
|
| + public:
|
| + explicit HGlobalValueNumberer(HGraph* graph)
|
| + : graph_(graph),
|
| + block_side_effects_(graph_->blocks()->length()),
|
| + loop_side_effects_(graph_->blocks()->length()) {
|
| + ASSERT(HEAP->allow_allocation(false));
|
| + block_side_effects_.AddBlock(0, graph_->blocks()->length());
|
| + loop_side_effects_.AddBlock(0, graph_->blocks()->length());
|
| + }
|
| + ~HGlobalValueNumberer() {
|
| + ASSERT(!HEAP->allow_allocation(true));
|
| + }
|
| +
|
| + void Analyze();
|
| +
|
| + private:
|
| + void AnalyzeBlock(HBasicBlock* block, HValueMap* map);
|
| + void ComputeBlockSideEffects();
|
| + void LoopInvariantCodeMotion();
|
| + void ProcessLoopBlock(HBasicBlock* block,
|
| + HBasicBlock* before_loop,
|
| + int loop_kills);
|
| + bool ShouldMove(HInstruction* instr, HBasicBlock* loop_header);
|
| +
|
| + HGraph* graph_;
|
| +
|
| + // A map of block IDs to their side effects.
|
| + ZoneList<int> block_side_effects_;
|
| +
|
| + // A map of loop header block IDs to their loop's side effects.
|
| + ZoneList<int> loop_side_effects_;
|
| +};
|
| +
|
| +
|
| +void HGlobalValueNumberer::Analyze() {
|
| + ComputeBlockSideEffects();
|
| + if (FLAG_loop_invariant_code_motion) {
|
| + LoopInvariantCodeMotion();
|
| + }
|
| + HValueMap* map = new HValueMap();
|
| + AnalyzeBlock(graph_->blocks()->at(0), map);
|
| +}
|
| +
|
| +
|
| +void HGlobalValueNumberer::ComputeBlockSideEffects() {
|
| + for (int i = graph_->blocks()->length() - 1; i >= 0; --i) {
|
| + // Compute side effects for the block.
|
| + HBasicBlock* block = graph_->blocks()->at(i);
|
| + HInstruction* instr = block->first();
|
| + int id = block->block_id();
|
| + int side_effects = 0;
|
| + while (instr != NULL) {
|
| + side_effects |= (instr->flags() & HValue::ChangesFlagsMask());
|
| + instr = instr->next();
|
| + }
|
| + block_side_effects_[id] |= side_effects;
|
| +
|
| + // Loop headers are part of their loop.
|
| + if (block->IsLoopHeader()) {
|
| + loop_side_effects_[id] |= side_effects;
|
| + }
|
| +
|
| + // Propagate loop side effects upwards.
|
| + if (block->HasParentLoopHeader()) {
|
| + int header_id = block->parent_loop_header()->block_id();
|
| + loop_side_effects_[header_id] |=
|
| + block->IsLoopHeader() ? loop_side_effects_[id] : side_effects;
|
| + }
|
| + }
|
| +}
|
| +
|
| +
|
| +void HGlobalValueNumberer::LoopInvariantCodeMotion() {
|
| + for (int i = graph_->blocks()->length() - 1; i >= 0; --i) {
|
| + HBasicBlock* block = graph_->blocks()->at(i);
|
| + if (block->IsLoopHeader()) {
|
| + int side_effects = loop_side_effects_[block->block_id()];
|
| + TraceGVN("Try loop invariant motion for block B%d effects=0x%x\n",
|
| + block->block_id(),
|
| + side_effects);
|
| +
|
| + HBasicBlock* last = block->loop_information()->GetLastBackEdge();
|
| + for (int j = block->block_id(); j <= last->block_id(); ++j) {
|
| + ProcessLoopBlock(graph_->blocks()->at(j), block, side_effects);
|
| + }
|
| + }
|
| + }
|
| +}
|
| +
|
| +
|
| +void HGlobalValueNumberer::ProcessLoopBlock(HBasicBlock* block,
|
| + HBasicBlock* loop_header,
|
| + int loop_kills) {
|
| + HBasicBlock* pre_header = loop_header->predecessors()->at(0);
|
| + int depends_flags = HValue::ConvertChangesToDependsFlags(loop_kills);
|
| + TraceGVN("Loop invariant motion for B%d depends_flags=0x%x\n",
|
| + block->block_id(),
|
| + depends_flags);
|
| + HInstruction* instr = block->first();
|
| + while (instr != NULL) {
|
| + HInstruction* next = instr->next();
|
| + if (instr->CheckFlag(HValue::kUseGVN) &&
|
| + (instr->flags() & depends_flags) == 0) {
|
| + TraceGVN("Checking instruction %d (%s)\n",
|
| + instr->id(),
|
| + instr->Mnemonic());
|
| + bool inputs_loop_invariant = true;
|
| + for (int i = 0; i < instr->OperandCount(); ++i) {
|
| + if (instr->OperandAt(i)->IsDefinedAfter(pre_header)) {
|
| + inputs_loop_invariant = false;
|
| + }
|
| + }
|
| +
|
| + if (inputs_loop_invariant && ShouldMove(instr, loop_header)) {
|
| + TraceGVN("Found loop invariant instruction %d\n", instr->id());
|
| + // Move the instruction out of the loop.
|
| + instr->Unlink();
|
| + instr->InsertBefore(pre_header->end());
|
| + }
|
| + }
|
| + instr = next;
|
| + }
|
| +}
|
| +
|
| +// Only move instructions that postdominate the loop header (i.e. are
|
| +// always executed inside the loop). This is to avoid unnecessary
|
| +// deoptimizations assuming the loop is executed at least once.
|
| +// TODO(fschneider): Better type feedback should give us information
|
| +// about code that was never executed.
|
| +bool HGlobalValueNumberer::ShouldMove(HInstruction* instr,
|
| + HBasicBlock* loop_header) {
|
| + if (!instr->IsChange() &&
|
| + FLAG_aggressive_loop_invariant_motion) return true;
|
| + HBasicBlock* block = instr->block();
|
| + bool result = true;
|
| + if (block != loop_header) {
|
| + for (int i = 1; i < loop_header->predecessors()->length(); ++i) {
|
| + bool found = false;
|
| + HBasicBlock* pred = loop_header->predecessors()->at(i);
|
| + while (pred != loop_header) {
|
| + if (pred == block) found = true;
|
| + pred = pred->dominator();
|
| + }
|
| + if (!found) {
|
| + result = false;
|
| + break;
|
| + }
|
| + }
|
| + }
|
| + return result;
|
| +}
|
| +
|
| +
|
| +void HGlobalValueNumberer::AnalyzeBlock(HBasicBlock* block, HValueMap* map) {
|
| + TraceGVN("Analyzing block B%d\n", block->block_id());
|
| +
|
| + // If this is a loop header kill everything killed by the loop.
|
| + if (block->IsLoopHeader()) {
|
| + map->Kill(loop_side_effects_[block->block_id()]);
|
| + }
|
| +
|
| + // Go through all instructions of the current block.
|
| + HInstruction* instr = block->first();
|
| + while (instr != NULL) {
|
| + HInstruction* next = instr->next();
|
| + int flags = (instr->flags() & HValue::ChangesFlagsMask());
|
| + if (flags != 0) {
|
| + ASSERT(!instr->CheckFlag(HValue::kUseGVN));
|
| + // Clear all instructions in the map that are affected by side effects.
|
| + map->Kill(flags);
|
| + TraceGVN("Instruction %d kills\n", instr->id());
|
| + } else if (instr->CheckFlag(HValue::kUseGVN)) {
|
| + HValue* other = map->Lookup(instr);
|
| + if (other != NULL) {
|
| + ASSERT(instr->Equals(other) && other->Equals(instr));
|
| + TraceGVN("Replacing value %d (%s) with value %d (%s)\n",
|
| + instr->id(),
|
| + instr->Mnemonic(),
|
| + other->id(),
|
| + other->Mnemonic());
|
| + instr->ReplaceValue(other);
|
| + instr->Delete();
|
| + } else {
|
| + map->Add(instr);
|
| + }
|
| + }
|
| + instr = next;
|
| + }
|
| +
|
| + // Recursively continue analysis for all immediately dominated blocks.
|
| + int length = block->dominated_blocks()->length();
|
| + for (int i = 0; i < length; ++i) {
|
| + HBasicBlock* dominated = block->dominated_blocks()->at(i);
|
| + // No need to copy the map for the last child in the dominator tree.
|
| + HValueMap* successor_map = (i == length - 1) ? map : map->Copy();
|
| +
|
| + // If the dominated block is not a successor to this block we have to
|
| + // kill everything killed on any path between this block and the
|
| + // dominated block. Note we rely on the block ordering.
|
| + bool is_successor = false;
|
| + int predecessor_count = dominated->predecessors()->length();
|
| + for (int j = 0; !is_successor && j < predecessor_count; ++j) {
|
| + is_successor = (dominated->predecessors()->at(j) == block);
|
| + }
|
| +
|
| + if (!is_successor) {
|
| + int side_effects = 0;
|
| + for (int j = block->block_id() + 1; j < dominated->block_id(); ++j) {
|
| + side_effects |= block_side_effects_[j];
|
| + }
|
| + successor_map->Kill(side_effects);
|
| + }
|
| +
|
| + AnalyzeBlock(dominated, successor_map);
|
| + }
|
| +}
|
| +
|
| +
|
| +class HInferRepresentation BASE_EMBEDDED {
|
| + public:
|
| + explicit HInferRepresentation(HGraph* graph)
|
| + : graph_(graph), worklist_(8), in_worklist_(graph->GetMaximumValueID()) {}
|
| +
|
| + void Analyze();
|
| +
|
| + private:
|
| + Representation TryChange(HValue* current);
|
| + void AddToWorklist(HValue* current);
|
| + void InferBasedOnInputs(HValue* current);
|
| + void AddDependantsToWorklist(HValue* current);
|
| + void InferBasedOnUses(HValue* current);
|
| +
|
| + HGraph* graph_;
|
| + ZoneList<HValue*> worklist_;
|
| + BitVector in_worklist_;
|
| +};
|
| +
|
| +
|
| +void HInferRepresentation::AddToWorklist(HValue* current) {
|
| + if (current->representation().IsSpecialization()) return;
|
| + if (!current->CheckFlag(HValue::kFlexibleRepresentation)) return;
|
| + if (in_worklist_.Contains(current->id())) return;
|
| + worklist_.Add(current);
|
| + in_worklist_.Add(current->id());
|
| +}
|
| +
|
| +
|
| +// This method tries to specialize the representation type of the value
|
| +// given as a parameter. The value is asked to infer its representation type
|
| +// based on its inputs. If the inferred type is more specialized, then this
|
| +// becomes the new representation type of the node.
|
| +void HInferRepresentation::InferBasedOnInputs(HValue* current) {
|
| + Representation r = current->representation();
|
| + if (r.IsSpecialization()) return;
|
| + ASSERT(current->CheckFlag(HValue::kFlexibleRepresentation));
|
| + Representation inferred = current->InferredRepresentation();
|
| + if (inferred.IsSpecialization()) {
|
| + current->ChangeRepresentation(inferred);
|
| + AddDependantsToWorklist(current);
|
| + }
|
| +}
|
| +
|
| +
|
| +void HInferRepresentation::AddDependantsToWorklist(HValue* current) {
|
| + for (int i = 0; i < current->uses()->length(); ++i) {
|
| + AddToWorklist(current->uses()->at(i));
|
| + }
|
| + for (int i = 0; i < current->OperandCount(); ++i) {
|
| + AddToWorklist(current->OperandAt(i));
|
| + }
|
| +}
|
| +
|
| +
|
| +// This method calculates whether specializing the representation of the value
|
| +// given as the parameter has a benefit in terms of less necessary type
|
| +// conversions. If there is a benefit, then the representation of the value is
|
| +// specialized.
|
| +void HInferRepresentation::InferBasedOnUses(HValue* current) {
|
| + Representation r = current->representation();
|
| + if (r.IsSpecialization() || current->HasNoUses()) return;
|
| + ASSERT(current->CheckFlag(HValue::kFlexibleRepresentation));
|
| + Representation new_rep = TryChange(current);
|
| + if (!new_rep.IsNone()) {
|
| + if (!current->representation().Equals(new_rep)) {
|
| + current->ChangeRepresentation(new_rep);
|
| + AddDependantsToWorklist(current);
|
| + }
|
| + }
|
| +}
|
| +
|
| +
|
| +Representation HInferRepresentation::TryChange(HValue* current) {
|
| + // Array of use counts for each representation.
|
| + int use_count[Representation::kNumRepresentations];
|
| + for (int i = 0; i < Representation::kNumRepresentations; i++) {
|
| + use_count[i] = 0;
|
| + }
|
| +
|
| + for (int i = 0; i < current->uses()->length(); ++i) {
|
| + HValue* use = current->uses()->at(i);
|
| + int index = use->LookupOperandIndex(0, current);
|
| + Representation req_rep = use->RequiredInputRepresentation(index);
|
| + if (req_rep.IsNone()) continue;
|
| + if (use->IsPhi()) {
|
| + HPhi* phi = HPhi::cast(use);
|
| + phi->AddIndirectUsesTo(&use_count[0]);
|
| + }
|
| + use_count[req_rep.kind()]++;
|
| + }
|
| + int tagged_count = use_count[Representation::kTagged];
|
| + int double_count = use_count[Representation::kDouble];
|
| + int int32_count = use_count[Representation::kInteger32];
|
| + int non_tagged_count = double_count + int32_count;
|
| +
|
| + // If a non-loop phi has tagged uses, don't convert it to untagged.
|
| + if (current->IsPhi() && !current->block()->IsLoopHeader()) {
|
| + if (tagged_count > 0) return Representation::None();
|
| + }
|
| +
|
| + if (non_tagged_count >= tagged_count) {
|
| + // More untagged than tagged.
|
| + if (double_count > 0) {
|
| + // There is at least one usage that is a double => guess that the
|
| + // correct representation is double.
|
| + return Representation::Double();
|
| + } else if (int32_count > 0) {
|
| + return Representation::Integer32();
|
| + }
|
| + }
|
| + return Representation::None();
|
| +}
|
| +
|
| +
|
| +void HInferRepresentation::Analyze() {
|
| + HPhase phase("Infer representations", graph_);
|
| +
|
| + // (1) Initialize bit vectors and count real uses. Each phi
|
| + // gets a bit-vector of length <number of phis>.
|
| + const ZoneList<HPhi*>* phi_list = graph_->phi_list();
|
| + int num_phis = phi_list->length();
|
| + ScopedVector<BitVector*> connected_phis(num_phis);
|
| + for (int i = 0; i < num_phis; i++) {
|
| + phi_list->at(i)->InitRealUses(i);
|
| + connected_phis[i] = new BitVector(num_phis);
|
| + connected_phis[i]->Add(i);
|
| + }
|
| +
|
| + // (2) Do a fixed point iteration to find the set of connected phis.
|
| + // A phi is connected to another phi if its value is used either
|
| + // directly or indirectly through a transitive closure of the def-use
|
| + // relation.
|
| + bool change = true;
|
| + while (change) {
|
| + change = false;
|
| + for (int i = 0; i < num_phis; i++) {
|
| + HPhi* phi = phi_list->at(i);
|
| + for (int j = 0; j < phi->uses()->length(); j++) {
|
| + HValue* use = phi->uses()->at(j);
|
| + if (use->IsPhi()) {
|
| + int phi_use = HPhi::cast(use)->phi_id();
|
| + if (connected_phis[i]->UnionIsChanged(*connected_phis[phi_use])) {
|
| + change = true;
|
| + }
|
| + }
|
| + }
|
| + }
|
| + }
|
| +
|
| + // (3) Sum up the non-phi use counts of all connected phis.
|
| + // Don't include the non-phi uses of the phi itself.
|
| + for (int i = 0; i < num_phis; i++) {
|
| + HPhi* phi = phi_list->at(i);
|
| + for (BitVector::Iterator it(connected_phis.at(i));
|
| + !it.Done();
|
| + it.Advance()) {
|
| + int index = it.Current();
|
| + if (index != i) {
|
| + HPhi* it_use = phi_list->at(it.Current());
|
| + phi->AddNonPhiUsesFrom(it_use);
|
| + }
|
| + }
|
| + }
|
| +
|
| + for (int i = 0; i < graph_->blocks()->length(); ++i) {
|
| + HBasicBlock* block = graph_->blocks()->at(i);
|
| + const ZoneList<HPhi*>* phis = block->phis();
|
| + for (int j = 0; j < phis->length(); ++j) {
|
| + AddToWorklist(phis->at(j));
|
| + }
|
| +
|
| + HInstruction* current = block->first();
|
| + while (current != NULL) {
|
| + AddToWorklist(current);
|
| + current = current->next();
|
| + }
|
| + }
|
| +
|
| + while (!worklist_.is_empty()) {
|
| + HValue* current = worklist_.RemoveLast();
|
| + in_worklist_.Remove(current->id());
|
| + InferBasedOnInputs(current);
|
| + InferBasedOnUses(current);
|
| + }
|
| +}
|
| +
|
| +
|
| +void HGraph::InitializeInferredTypes() {
|
| + HPhase phase("Inferring types", this);
|
| + InitializeInferredTypes(0, this->blocks_.length() - 1);
|
| +}
|
| +
|
| +
|
| +void HGraph::InitializeInferredTypes(int from_inclusive, int to_inclusive) {
|
| + for (int i = from_inclusive; i <= to_inclusive; ++i) {
|
| + HBasicBlock* block = blocks_[i];
|
| +
|
| + const ZoneList<HPhi*>* phis = block->phis();
|
| + for (int j = 0; j < phis->length(); j++) {
|
| + phis->at(j)->UpdateInferredType();
|
| + }
|
| +
|
| + HInstruction* current = block->first();
|
| + while (current != NULL) {
|
| + current->UpdateInferredType();
|
| + current = current->next();
|
| + }
|
| +
|
| + if (block->IsLoopHeader()) {
|
| + HBasicBlock* last_back_edge =
|
| + block->loop_information()->GetLastBackEdge();
|
| + InitializeInferredTypes(i + 1, last_back_edge->block_id());
|
| + // Skip all blocks already processed by the recursive call.
|
| + i = last_back_edge->block_id();
|
| + // Update phis of the loop header now after the whole loop body is
|
| + // guaranteed to be processed.
|
| + ZoneList<HValue*> worklist(block->phis()->length());
|
| + for (int j = 0; j < block->phis()->length(); ++j) {
|
| + worklist.Add(block->phis()->at(j));
|
| + }
|
| + InferTypes(&worklist);
|
| + }
|
| + }
|
| +}
|
| +
|
| +
|
| +void HGraph::PropagateMinusZeroChecks(HValue* value, BitVector* visited) {
|
| + HValue* current = value;
|
| + while (current != NULL) {
|
| + if (visited->Contains(current->id())) return;
|
| +
|
| + // For phis, we must propagate the check to all of its inputs.
|
| + if (current->IsPhi()) {
|
| + visited->Add(current->id());
|
| + HPhi* phi = HPhi::cast(current);
|
| + for (int i = 0; i < phi->OperandCount(); ++i) {
|
| + PropagateMinusZeroChecks(phi->OperandAt(i), visited);
|
| + }
|
| + break;
|
| + }
|
| +
|
| + // For multiplication and division, we must propagate to the left and
|
| + // the right side.
|
| + if (current->IsMul()) {
|
| + HMul* mul = HMul::cast(current);
|
| + mul->EnsureAndPropagateNotMinusZero(visited);
|
| + PropagateMinusZeroChecks(mul->left(), visited);
|
| + PropagateMinusZeroChecks(mul->right(), visited);
|
| + } else if (current->IsDiv()) {
|
| + HDiv* div = HDiv::cast(current);
|
| + div->EnsureAndPropagateNotMinusZero(visited);
|
| + PropagateMinusZeroChecks(div->left(), visited);
|
| + PropagateMinusZeroChecks(div->right(), visited);
|
| + }
|
| +
|
| + current = current->EnsureAndPropagateNotMinusZero(visited);
|
| + }
|
| +}
|
| +
|
| +
|
| +void HGraph::InsertRepresentationChangeForUse(HValue* value,
|
| + HValue* use,
|
| + Representation to,
|
| + bool is_truncating) {
|
| + // Propagate flags for negative zero checks upwards from conversions
|
| + // int32-to-tagged and int32-to-double.
|
| + Representation from = value->representation();
|
| + if (from.IsInteger32()) {
|
| + ASSERT(to.IsTagged() || to.IsDouble());
|
| + BitVector visited(GetMaximumValueID());
|
| + PropagateMinusZeroChecks(value, &visited);
|
| + }
|
| +
|
| + // Insert the representation change right before its use. For phi-uses we
|
| + // insert at the end of the corresponding predecessor.
|
| + HBasicBlock* insert_block = use->block();
|
| + if (use->IsPhi()) {
|
| + int index = 0;
|
| + while (use->OperandAt(index) != value) ++index;
|
| + insert_block = insert_block->predecessors()->at(index);
|
| + }
|
| +
|
| + HInstruction* next = (insert_block == use->block())
|
| + ? HInstruction::cast(use)
|
| + : insert_block->end();
|
| +
|
| + // For constants we try to make the representation change at compile
|
| + // time. When a representation change is not possible without loss of
|
| + // information we treat constants like normal instructions and insert the
|
| + // change instructions for them.
|
| + HInstruction* new_value = NULL;
|
| + if (value->IsConstant()) {
|
| + HConstant* constant = HConstant::cast(value);
|
| + // Try to create a new copy of the constant with the new representation.
|
| + new_value = is_truncating
|
| + ? constant->CopyToTruncatedInt32()
|
| + : constant->CopyToRepresentation(to);
|
| + }
|
| +
|
| + if (new_value == NULL) {
|
| + new_value = new HChange(value, value->representation(), to);
|
| + }
|
| +
|
| + new_value->InsertBefore(next);
|
| + value->ReplaceFirstAtUse(use, new_value, to);
|
| +}
|
| +
|
| +
|
| +int CompareConversionUses(HValue* a,
|
| + HValue* b,
|
| + Representation a_rep,
|
| + Representation b_rep) {
|
| + if (a_rep.kind() > b_rep.kind()) {
|
| + // Make sure specializations are separated in the result array.
|
| + return 1;
|
| + }
|
| + // Put truncating conversions before non-truncating conversions.
|
| + bool a_truncate = a->CheckFlag(HValue::kTruncatingToInt32);
|
| + bool b_truncate = b->CheckFlag(HValue::kTruncatingToInt32);
|
| + if (a_truncate != b_truncate) {
|
| + return a_truncate ? -1 : 1;
|
| + }
|
| + // Sort by increasing block ID.
|
| + return a->block()->block_id() - b->block()->block_id();
|
| +}
|
| +
|
| +
|
| +void HGraph::InsertRepresentationChanges(HValue* current) {
|
| + Representation r = current->representation();
|
| + if (r.IsNone()) return;
|
| + if (current->uses()->length() == 0) return;
|
| +
|
| + // Collect the representation changes in a sorted list. This allows
|
| + // us to avoid duplicate changes without searching the list.
|
| + ZoneList<HValue*> to_convert(2);
|
| + ZoneList<Representation> to_convert_reps(2);
|
| + for (int i = 0; i < current->uses()->length(); ++i) {
|
| + HValue* use = current->uses()->at(i);
|
| + // The occurrences index means the index within the operand array of "use"
|
| + // at which "current" is used. While iterating through the use array we
|
| + // also have to iterate over the different occurrence indices.
|
| + int occurrence_index = 0;
|
| + if (use->UsesMultipleTimes(current)) {
|
| + occurrence_index = current->uses()->CountOccurrences(use, 0, i - 1);
|
| + if (FLAG_trace_representation) {
|
| + PrintF("Instruction %d is used multiple times at %d; occurrence=%d\n",
|
| + current->id(),
|
| + use->id(),
|
| + occurrence_index);
|
| + }
|
| + }
|
| + int operand_index = use->LookupOperandIndex(occurrence_index, current);
|
| + Representation req = use->RequiredInputRepresentation(operand_index);
|
| + if (req.IsNone() || req.Equals(r)) continue;
|
| + int index = 0;
|
| + while (to_convert.length() > index &&
|
| + CompareConversionUses(to_convert[index],
|
| + use,
|
| + to_convert_reps[index],
|
| + req) < 0) {
|
| + ++index;
|
| + }
|
| + if (FLAG_trace_representation) {
|
| + PrintF("Inserting a representation change to %s of %d for use at %d\n",
|
| + req.Mnemonic(),
|
| + current->id(),
|
| + use->id());
|
| + }
|
| + to_convert.InsertAt(index, use);
|
| + to_convert_reps.InsertAt(index, req);
|
| + }
|
| +
|
| + for (int i = 0; i < to_convert.length(); ++i) {
|
| + HValue* use = to_convert[i];
|
| + Representation r_to = to_convert_reps[i];
|
| + bool is_truncating = use->CheckFlag(HValue::kTruncatingToInt32);
|
| + InsertRepresentationChangeForUse(current, use, r_to, is_truncating);
|
| + }
|
| +
|
| + if (current->uses()->is_empty()) {
|
| + ASSERT(current->IsConstant());
|
| + current->Delete();
|
| + }
|
| +}
|
| +
|
| +
|
| +void HGraph::InsertRepresentationChanges() {
|
| + HPhase phase("Insert representation changes", this);
|
| +
|
| +
|
| + // Compute truncation flag for phis: Initially assume that all
|
| + // int32-phis allow truncation and iteratively remove the ones that
|
| + // are used in an operation that does not allow a truncating
|
| + // conversion.
|
| + // TODO(fschneider): Replace this with a worklist-based iteration.
|
| + for (int i = 0; i < phi_list()->length(); i++) {
|
| + HPhi* phi = phi_list()->at(i);
|
| + if (phi->representation().IsInteger32()) {
|
| + phi->SetFlag(HValue::kTruncatingToInt32);
|
| + }
|
| + }
|
| + bool change = true;
|
| + while (change) {
|
| + change = false;
|
| + for (int i = 0; i < phi_list()->length(); i++) {
|
| + HPhi* phi = phi_list()->at(i);
|
| + if (!phi->CheckFlag(HValue::kTruncatingToInt32)) continue;
|
| + for (int j = 0; j < phi->uses()->length(); j++) {
|
| + HValue* use = phi->uses()->at(j);
|
| + if (!use->CheckFlag(HValue::kTruncatingToInt32)) {
|
| + phi->ClearFlag(HValue::kTruncatingToInt32);
|
| + change = true;
|
| + break;
|
| + }
|
| + }
|
| + }
|
| + }
|
| +
|
| + for (int i = 0; i < blocks_.length(); ++i) {
|
| + // Process phi instructions first.
|
| + for (int j = 0; j < blocks_[i]->phis()->length(); j++) {
|
| + HPhi* phi = blocks_[i]->phis()->at(j);
|
| + InsertRepresentationChanges(phi);
|
| + }
|
| +
|
| + // Process normal instructions.
|
| + HInstruction* current = blocks_[i]->first();
|
| + while (current != NULL) {
|
| + InsertRepresentationChanges(current);
|
| + current = current->next();
|
| + }
|
| + }
|
| +}
|
| +
|
| +
|
| +// Implementation of utility classes to represent an expression's context in
|
| +// the AST.
|
| +AstContext::AstContext(HGraphBuilder* owner, Expression::Context kind)
|
| + : owner_(owner), kind_(kind), outer_(owner->ast_context()) {
|
| + owner->set_ast_context(this); // Push.
|
| +}
|
| +
|
| +
|
| +AstContext::~AstContext() {
|
| + owner_->set_ast_context(outer_); // Pop.
|
| +}
|
| +
|
| +
|
| +
|
| +// HGraphBuilder infrastructure for bailing out and checking bailouts.
|
| +#define BAILOUT(reason) \
|
| + do { \
|
| + Bailout(reason); \
|
| + return; \
|
| + } while (false)
|
| +
|
| +
|
| +#define CHECK_BAILOUT \
|
| + do { \
|
| + if (HasStackOverflow()) return; \
|
| + } while (false)
|
| +
|
| +
|
| +#define VISIT_FOR_EFFECT(expr) \
|
| + do { \
|
| + VisitForEffect(expr); \
|
| + if (HasStackOverflow()) return; \
|
| + } while (false)
|
| +
|
| +
|
| +#define VISIT_FOR_VALUE(expr) \
|
| + do { \
|
| + VisitForValue(expr); \
|
| + if (HasStackOverflow()) return; \
|
| + } while (false)
|
| +
|
| +
|
| +// 'thing' could be an expression, statement, or list of statements.
|
| +#define ADD_TO_SUBGRAPH(graph, thing) \
|
| + do { \
|
| + AddToSubgraph(graph, thing); \
|
| + if (HasStackOverflow()) return; \
|
| + } while (false)
|
| +
|
| +
|
| +class HGraphBuilder::SubgraphScope BASE_EMBEDDED {
|
| + public:
|
| + SubgraphScope(HGraphBuilder* builder, HSubgraph* new_subgraph)
|
| + : builder_(builder) {
|
| + old_subgraph_ = builder_->current_subgraph_;
|
| + subgraph_ = new_subgraph;
|
| + builder_->current_subgraph_ = subgraph_;
|
| + }
|
| +
|
| + ~SubgraphScope() {
|
| + old_subgraph_->AddBreakContinueInfo(subgraph_);
|
| + builder_->current_subgraph_ = old_subgraph_;
|
| + }
|
| +
|
| + HSubgraph* subgraph() const { return subgraph_; }
|
| +
|
| + private:
|
| + HGraphBuilder* builder_;
|
| + HSubgraph* old_subgraph_;
|
| + HSubgraph* subgraph_;
|
| +};
|
| +
|
| +
|
| +void HGraphBuilder::Bailout(const char* reason) {
|
| + if (FLAG_trace_bailout) {
|
| + SmartPointer<char> debug_name = graph()->debug_name()->ToCString();
|
| + PrintF("Bailout in HGraphBuilder: @\"%s\": %s\n", *debug_name, reason);
|
| + }
|
| + SetStackOverflow();
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::VisitForEffect(Expression* expr) {
|
| +#ifdef DEBUG
|
| + int original_count = environment()->total_count();
|
| +#endif
|
| + BinaryOperation* binary_op = expr->AsBinaryOperation();
|
| +
|
| + // We use special casing for expression types not handled properly by our
|
| + // usual trick of pretending they're in a value context and cleaning up
|
| + // later.
|
| + if (binary_op != NULL && binary_op->op() == Token::COMMA) {
|
| + VISIT_FOR_EFFECT(binary_op->left());
|
| + VISIT_FOR_EFFECT(binary_op->right());
|
| + } else {
|
| + { EffectContext for_effect(this);
|
| + Visit(expr);
|
| + }
|
| + if (HasStackOverflow() || !subgraph()->HasExit()) return;
|
| + // Discard return value.
|
| + Pop();
|
| + // TODO(kasperl): Try to improve the way we compute the last added
|
| + // instruction. The NULL check makes me uncomfortable.
|
| + HValue* last = subgraph()->exit_block()->GetLastInstruction();
|
| + // We need to ensure we emit a simulate after inlined functions in an
|
| + // effect context, to avoid having a bailout target the fictional
|
| + // environment with the return value on top.
|
| + if ((last != NULL && last->HasSideEffects()) ||
|
| + subgraph()->exit_block()->IsInlineReturnTarget()) {
|
| + AddSimulate(expr->id());
|
| + }
|
| + }
|
| +
|
| + ASSERT(environment()->total_count() == original_count);
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::VisitForValue(Expression* expr) {
|
| +#ifdef DEBUG
|
| + int original_height = environment()->values()->length();
|
| +#endif
|
| + { ValueContext for_value(this);
|
| + Visit(expr);
|
| + }
|
| + if (HasStackOverflow() || !subgraph()->HasExit()) return;
|
| + // TODO(kasperl): Try to improve the way we compute the last added
|
| + // instruction. The NULL check makes me uncomfortable.
|
| + HValue* last = subgraph()->exit_block()->GetLastInstruction();
|
| + if (last != NULL && last->HasSideEffects()) {
|
| + AddSimulate(expr->id());
|
| + }
|
| + ASSERT(environment()->values()->length() == original_height + 1);
|
| +}
|
| +
|
| +
|
| +HValue* HGraphBuilder::VisitArgument(Expression* expr) {
|
| + VisitForValue(expr);
|
| + if (HasStackOverflow() || !subgraph()->HasExit()) return NULL;
|
| + return environment()->Top();
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::VisitArgumentList(ZoneList<Expression*>* arguments) {
|
| + for (int i = 0; i < arguments->length(); i++) {
|
| + VisitArgument(arguments->at(i));
|
| + if (HasStackOverflow() || !current_subgraph_->HasExit()) return;
|
| + }
|
| +}
|
| +
|
| +
|
| +HGraph* HGraphBuilder::CreateGraph(CompilationInfo* info) {
|
| + ASSERT(current_subgraph_ == NULL);
|
| + graph_ = new HGraph(info);
|
| +
|
| + {
|
| + HPhase phase("Block building");
|
| + graph_->Initialize(CreateBasicBlock(graph_->start_environment()));
|
| + current_subgraph_ = graph_;
|
| +
|
| + Scope* scope = info->scope();
|
| + SetupScope(scope);
|
| + VisitDeclarations(scope->declarations());
|
| +
|
| + AddInstruction(new HStackCheck());
|
| +
|
| + ZoneList<Statement*>* stmts = info->function()->body();
|
| + HSubgraph* body = CreateGotoSubgraph(environment());
|
| + AddToSubgraph(body, stmts);
|
| + if (HasStackOverflow()) return NULL;
|
| + current_subgraph_->Append(body, NULL);
|
| + body->entry_block()->SetJoinId(info->function()->id());
|
| +
|
| + if (graph_->HasExit()) {
|
| + graph_->FinishExit(new HReturn(graph_->GetConstantUndefined()));
|
| + }
|
| + }
|
| +
|
| + graph_->OrderBlocks();
|
| + graph_->AssignDominators();
|
| + graph_->EliminateRedundantPhis();
|
| + if (!graph_->CollectPhis()) {
|
| + Bailout("Phi-use of arguments object");
|
| + return NULL;
|
| + }
|
| +
|
| + HInferRepresentation rep(graph_);
|
| + rep.Analyze();
|
| +
|
| + if (FLAG_use_range) {
|
| + HRangeAnalysis rangeAnalysis(graph_);
|
| + rangeAnalysis.Analyze();
|
| + }
|
| +
|
| + graph_->InitializeInferredTypes();
|
| + graph_->Canonicalize();
|
| + graph_->InsertRepresentationChanges();
|
| +
|
| + // Eliminate redundant stack checks on backwards branches.
|
| + HStackCheckEliminator sce(graph_);
|
| + sce.Process();
|
| +
|
| + // Perform common subexpression elimination and loop-invariant code motion.
|
| + if (FLAG_use_gvn) {
|
| + HPhase phase("Global value numbering", graph_);
|
| + HGlobalValueNumberer gvn(graph_);
|
| + gvn.Analyze();
|
| + }
|
| +
|
| + return graph_;
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::AddToSubgraph(HSubgraph* graph, Statement* stmt) {
|
| + SubgraphScope scope(this, graph);
|
| + Visit(stmt);
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::AddToSubgraph(HSubgraph* graph, Expression* expr) {
|
| + SubgraphScope scope(this, graph);
|
| + VisitForValue(expr);
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::VisitCondition(Expression* expr,
|
| + HBasicBlock* true_block,
|
| + HBasicBlock* false_block,
|
| + bool invert_true,
|
| + bool invert_false) {
|
| + VisitForControl(expr, true_block, false_block, invert_true, invert_false);
|
| + CHECK_BAILOUT;
|
| +#ifdef DEBUG
|
| + HValue* value = true_block->predecessors()->at(0)->last_environment()->Top();
|
| + true_block->set_cond(HConstant::cast(value)->handle());
|
| +
|
| + value = false_block->predecessors()->at(0)->last_environment()->Top();
|
| + false_block->set_cond(HConstant::cast(value)->handle());
|
| +#endif
|
| +
|
| + true_block->SetJoinId(expr->id());
|
| + false_block->SetJoinId(expr->id());
|
| + true_block->last_environment()->Pop();
|
| + false_block->last_environment()->Pop();
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::AddConditionToSubgraph(HSubgraph* subgraph,
|
| + Expression* expr,
|
| + HSubgraph* true_graph,
|
| + HSubgraph* false_graph) {
|
| + SubgraphScope scope(this, subgraph);
|
| + VisitCondition(expr,
|
| + true_graph->entry_block(),
|
| + false_graph->entry_block(),
|
| + false,
|
| + false);
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::VisitForControl(Expression* expr,
|
| + HBasicBlock* true_block,
|
| + HBasicBlock* false_block,
|
| + bool invert_true,
|
| + bool invert_false) {
|
| + TestContext for_test(this, true_block, false_block,
|
| + invert_true, invert_false);
|
| + BinaryOperation* binary_op = expr->AsBinaryOperation();
|
| + UnaryOperation* unary_op = expr->AsUnaryOperation();
|
| +
|
| + if (unary_op != NULL && unary_op->op() == Token::NOT) {
|
| + VisitForControl(unary_op->expression(),
|
| + false_block,
|
| + true_block,
|
| + !invert_false,
|
| + !invert_true);
|
| + } else if (binary_op != NULL && binary_op->op() == Token::AND) {
|
| + // Translate left subexpression.
|
| + HBasicBlock* eval_right = graph()->CreateBasicBlock();
|
| + VisitForControl(binary_op->left(),
|
| + eval_right,
|
| + false_block,
|
| + false,
|
| + invert_false);
|
| + if (HasStackOverflow()) return;
|
| + eval_right->SetJoinId(binary_op->left()->id());
|
| +
|
| + // Translate right subexpression.
|
| + eval_right->last_environment()->Pop();
|
| + subgraph()->set_exit_block(eval_right);
|
| + VisitForControl(binary_op->right(),
|
| + true_block,
|
| + false_block,
|
| + invert_true,
|
| + invert_false);
|
| + } else if (binary_op != NULL && binary_op->op() == Token::OR) {
|
| + // Translate left subexpression.
|
| + HBasicBlock* eval_right = graph()->CreateBasicBlock();
|
| + VisitForControl(binary_op->left(),
|
| + true_block,
|
| + eval_right,
|
| + invert_true,
|
| + false);
|
| + if (HasStackOverflow()) return;
|
| + eval_right->SetJoinId(binary_op->left()->id());
|
| +
|
| + // Translate right subexpression
|
| + eval_right->last_environment()->Pop();
|
| + subgraph()->set_exit_block(eval_right);
|
| + VisitForControl(binary_op->right(),
|
| + true_block,
|
| + false_block,
|
| + invert_true,
|
| + invert_false);
|
| + } else {
|
| +#ifdef DEBUG
|
| + int original_length = environment()->values()->length();
|
| +#endif
|
| + // TODO(kmillikin): Refactor to avoid. This code is duplicated from
|
| + // VisitForValue, except without pushing a value context on the
|
| + // expression context stack.
|
| + Visit(expr);
|
| + if (HasStackOverflow() || !subgraph()->HasExit()) return;
|
| + HValue* last = subgraph()->exit_block()->GetLastInstruction();
|
| + if (last != NULL && last->HasSideEffects()) {
|
| + AddSimulate(expr->id());
|
| + }
|
| + ASSERT(environment()->values()->length() == original_length + 1);
|
| + HValue* value = Pop();
|
| + HBasicBlock* materialize_true = graph()->CreateBasicBlock();
|
| + HBasicBlock* materialize_false = graph()->CreateBasicBlock();
|
| + CurrentBlock()->Finish(new HBranch(materialize_true,
|
| + materialize_false,
|
| + value));
|
| + HValue* true_value = invert_true
|
| + ? graph()->GetConstantFalse()
|
| + : graph()->GetConstantTrue();
|
| + materialize_true->set_inverted(invert_true);
|
| + true_block->set_deopt_predecessor(materialize_true);
|
| +
|
| + if (true_block->IsInlineReturnTarget()) {
|
| + materialize_true->AddLeaveInlined(true_value, true_block);
|
| + } else {
|
| + materialize_true->last_environment()->Push(true_value);
|
| + materialize_true->Goto(true_block);
|
| + }
|
| + HValue* false_value = invert_false
|
| + ? graph()->GetConstantTrue()
|
| + : graph()->GetConstantFalse();
|
| + materialize_false->set_inverted(invert_false);
|
| + false_block->set_deopt_predecessor(materialize_false);
|
| +
|
| + if (false_block->IsInlineReturnTarget()) {
|
| + materialize_false->AddLeaveInlined(false_value, false_block);
|
| + } else {
|
| + materialize_false->last_environment()->Push(false_value);
|
| + materialize_false->Goto(false_block);
|
| + }
|
| + subgraph()->set_exit_block(NULL);
|
| + }
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::AddToSubgraph(HSubgraph* graph,
|
| + ZoneList<Statement*>* stmts) {
|
| + SubgraphScope scope(this, graph);
|
| + VisitStatements(stmts);
|
| +}
|
| +
|
| +
|
| +HInstruction* HGraphBuilder::AddInstruction(HInstruction* instr) {
|
| + ASSERT(current_subgraph_->HasExit());
|
| + current_subgraph_->exit_block()->AddInstruction(instr);
|
| + return instr;
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::AddSimulate(int id) {
|
| + ASSERT(current_subgraph_->HasExit());
|
| + current_subgraph_->exit_block()->AddSimulate(id);
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::AddPhi(HPhi* instr) {
|
| + ASSERT(current_subgraph_->HasExit());
|
| + current_subgraph_->exit_block()->AddPhi(instr);
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::PushAndAdd(HInstruction* instr) {
|
| + Push(instr);
|
| + AddInstruction(instr);
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::PushAndAdd(HInstruction* instr, int position) {
|
| + instr->set_position(position);
|
| + PushAndAdd(instr);
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::PushArgumentsForStubCall(int argument_count) {
|
| + const int kMaxStubArguments = 4;
|
| + ASSERT_GE(kMaxStubArguments, argument_count);
|
| + // Push the arguments on the stack.
|
| + HValue* arguments[kMaxStubArguments];
|
| + for (int i = argument_count - 1; i >= 0; i--) {
|
| + arguments[i] = Pop();
|
| + }
|
| + for (int i = 0; i < argument_count; i++) {
|
| + AddInstruction(new HPushArgument(arguments[i]));
|
| + }
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::ProcessCall(HCall* call, int source_position) {
|
| + for (int i = call->argument_count() - 1; i >= 0; --i) {
|
| + HValue* value = Pop();
|
| + HPushArgument* push = new HPushArgument(value);
|
| + call->SetArgumentAt(i, push);
|
| + }
|
| +
|
| + for (int i = 0; i < call->argument_count(); ++i) {
|
| + AddInstruction(call->PushArgumentAt(i));
|
| + }
|
| +
|
| + PushAndAdd(call, source_position);
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::SetupScope(Scope* scope) {
|
| + // We don't yet handle the function name for named function expressions.
|
| + if (scope->function() != NULL) BAILOUT("named function expression");
|
| +
|
| + // We can't handle heap-allocated locals.
|
| + if (scope->num_heap_slots() > 0) BAILOUT("heap allocated locals");
|
| +
|
| + HConstant* undefined_constant =
|
| + new HConstant(FACTORY->undefined_value(), Representation::Tagged());
|
| + AddInstruction(undefined_constant);
|
| + graph_->set_undefined_constant(undefined_constant);
|
| +
|
| + // Set the initial values of parameters including "this". "This" has
|
| + // parameter index 0.
|
| + int count = scope->num_parameters() + 1;
|
| + for (int i = 0; i < count; ++i) {
|
| + HInstruction* parameter = AddInstruction(new HParameter(i));
|
| + environment()->Bind(i, parameter);
|
| + }
|
| +
|
| + // Set the initial values of stack-allocated locals.
|
| + for (int i = count; i < environment()->values()->length(); ++i) {
|
| + environment()->Bind(i, undefined_constant);
|
| + }
|
| +
|
| + // Handle the arguments and arguments shadow variables specially (they do
|
| + // not have declarations).
|
| + if (scope->arguments() != NULL) {
|
| + HArgumentsObject* object = new HArgumentsObject;
|
| + AddInstruction(object);
|
| + graph()->SetArgumentsObject(object);
|
| + environment()->Bind(scope->arguments(), object);
|
| + environment()->Bind(scope->arguments_shadow(), object);
|
| + }
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::VisitStatements(ZoneList<Statement*>* statements) {
|
| + for (int i = 0; i < statements->length(); i++) {
|
| + Visit(statements->at(i));
|
| + if (HasStackOverflow() || !current_subgraph_->HasExit()) break;
|
| + }
|
| +}
|
| +
|
| +
|
| +HBasicBlock* HGraphBuilder::CreateBasicBlock(HEnvironment* env) {
|
| + HBasicBlock* b = graph()->CreateBasicBlock();
|
| + b->SetInitialEnvironment(env);
|
| + return b;
|
| +}
|
| +
|
| +
|
| +HSubgraph* HGraphBuilder::CreateInlinedSubgraph(HEnvironment* outer,
|
| + Handle<JSFunction> target,
|
| + FunctionLiteral* function) {
|
| + HConstant* undefined = graph()->GetConstantUndefined();
|
| + HEnvironment* inner =
|
| + outer->CopyForInlining(target, function, true, undefined);
|
| + HSubgraph* subgraph = new HSubgraph(graph());
|
| + subgraph->Initialize(CreateBasicBlock(inner));
|
| + return subgraph;
|
| +}
|
| +
|
| +
|
| +HSubgraph* HGraphBuilder::CreateGotoSubgraph(HEnvironment* env) {
|
| + HSubgraph* subgraph = new HSubgraph(graph());
|
| + HEnvironment* new_env = env->CopyWithoutHistory();
|
| + subgraph->Initialize(CreateBasicBlock(new_env));
|
| + return subgraph;
|
| +}
|
| +
|
| +
|
| +HSubgraph* HGraphBuilder::CreateEmptySubgraph() {
|
| + HSubgraph* subgraph = new HSubgraph(graph());
|
| + subgraph->Initialize(graph()->CreateBasicBlock());
|
| + return subgraph;
|
| +}
|
| +
|
| +
|
| +HSubgraph* HGraphBuilder::CreateBranchSubgraph(HEnvironment* env) {
|
| + HSubgraph* subgraph = new HSubgraph(graph());
|
| + HEnvironment* new_env = env->Copy();
|
| + subgraph->Initialize(CreateBasicBlock(new_env));
|
| + return subgraph;
|
| +}
|
| +
|
| +
|
| +HSubgraph* HGraphBuilder::CreateLoopHeaderSubgraph(HEnvironment* env) {
|
| + HSubgraph* subgraph = new HSubgraph(graph());
|
| + HBasicBlock* block = graph()->CreateBasicBlock();
|
| + HEnvironment* new_env = env->CopyAsLoopHeader(block);
|
| + block->SetInitialEnvironment(new_env);
|
| + subgraph->Initialize(block);
|
| + subgraph->entry_block()->AttachLoopInformation();
|
| + return subgraph;
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::VisitBlock(Block* stmt) {
|
| + if (stmt->labels() != NULL) {
|
| + HSubgraph* block_graph = CreateGotoSubgraph(environment());
|
| + ADD_TO_SUBGRAPH(block_graph, stmt->statements());
|
| + current_subgraph_->Append(block_graph, stmt);
|
| + } else {
|
| + VisitStatements(stmt->statements());
|
| + }
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::VisitExpressionStatement(ExpressionStatement* stmt) {
|
| + VisitForEffect(stmt->expression());
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::VisitEmptyStatement(EmptyStatement* stmt) {
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::VisitIfStatement(IfStatement* stmt) {
|
| + if (stmt->condition()->ToBooleanIsTrue()) {
|
| + Visit(stmt->then_statement());
|
| + } else if (stmt->condition()->ToBooleanIsFalse()) {
|
| + Visit(stmt->else_statement());
|
| + } else {
|
| + HSubgraph* then_graph = CreateEmptySubgraph();
|
| + HSubgraph* else_graph = CreateEmptySubgraph();
|
| + VisitCondition(stmt->condition(),
|
| + then_graph->entry_block(),
|
| + else_graph->entry_block(),
|
| + false, false);
|
| + if (HasStackOverflow()) return;
|
| + ADD_TO_SUBGRAPH(then_graph, stmt->then_statement());
|
| + ADD_TO_SUBGRAPH(else_graph, stmt->else_statement());
|
| + current_subgraph_->AppendJoin(then_graph, else_graph, stmt);
|
| + }
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::VisitContinueStatement(ContinueStatement* stmt) {
|
| + current_subgraph_->FinishBreakContinue(stmt->target(), true);
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::VisitBreakStatement(BreakStatement* stmt) {
|
| + current_subgraph_->FinishBreakContinue(stmt->target(), false);
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::VisitReturnStatement(ReturnStatement* stmt) {
|
| + AstContext* context = call_context();
|
| + if (context == NULL) {
|
| + // Not an inlined return, so an actual one.
|
| + VISIT_FOR_VALUE(stmt->expression());
|
| + HValue* result = environment()->Pop();
|
| + subgraph()->FinishExit(new HReturn(result));
|
| + } else {
|
| + // Return from an inlined function, visit the subexpression in the
|
| + // expression context of the call.
|
| + if (context->IsTest()) {
|
| + TestContext* test = TestContext::cast(context);
|
| + VisitForControl(stmt->expression(),
|
| + test->if_true(),
|
| + test->if_false(),
|
| + false,
|
| + false);
|
| + } else {
|
| + HValue* return_value = NULL;
|
| + if (context->IsEffect()) {
|
| + VISIT_FOR_EFFECT(stmt->expression());
|
| + return_value = graph()->GetConstantUndefined();
|
| + } else {
|
| + ASSERT(context->IsValue());
|
| + VISIT_FOR_VALUE(stmt->expression());
|
| + return_value = environment()->Pop();
|
| + }
|
| + subgraph()->exit_block()->AddLeaveInlined(return_value,
|
| + function_return_);
|
| + subgraph()->set_exit_block(NULL);
|
| + }
|
| + }
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::VisitWithEnterStatement(WithEnterStatement* stmt) {
|
| + BAILOUT("WithEnterStatement");
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::VisitWithExitStatement(WithExitStatement* stmt) {
|
| + BAILOUT("WithExitStatement");
|
| +}
|
| +
|
| +
|
| +HCompare* HGraphBuilder::BuildSwitchCompare(HSubgraph* subgraph,
|
| + HValue* switch_value,
|
| + CaseClause* clause) {
|
| + AddToSubgraph(subgraph, clause->label());
|
| + if (HasStackOverflow()) return NULL;
|
| + HValue* clause_value = subgraph->environment()->Pop();
|
| + HCompare* compare = new HCompare(switch_value,
|
| + clause_value,
|
| + Token::EQ_STRICT);
|
| + compare->SetInputRepresentation(Representation::Integer32());
|
| + subgraph->exit_block()->AddInstruction(compare);
|
| + return compare;
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) {
|
| + VISIT_FOR_VALUE(stmt->tag());
|
| + HValue* switch_value = Pop();
|
| +
|
| + ZoneList<CaseClause*>* clauses = stmt->cases();
|
| + int num_clauses = clauses->length();
|
| + if (num_clauses == 0) return;
|
| + if (num_clauses > 128) BAILOUT("SwitchStatement: too many clauses");
|
| +
|
| + for (int i = 0; i < num_clauses; i++) {
|
| + CaseClause* clause = clauses->at(i);
|
| + if (clause->is_default()) continue;
|
| + clause->RecordTypeFeedback(oracle());
|
| + if (!clause->IsSmiCompare()) BAILOUT("SwitchStatement: non-smi compare");
|
| + if (!clause->label()->IsSmiLiteral()) {
|
| + BAILOUT("SwitchStatement: non-literal switch label");
|
| + }
|
| + }
|
| +
|
| + // The single exit block of the whole switch statement.
|
| + HBasicBlock* single_exit_block = graph_->CreateBasicBlock();
|
| +
|
| + // Build a series of empty subgraphs for the comparisons.
|
| + // The default clause does not have a comparison subgraph.
|
| + ZoneList<HSubgraph*> compare_graphs(num_clauses);
|
| + for (int i = 0; i < num_clauses; i++) {
|
| + HSubgraph* subgraph = !clauses->at(i)->is_default()
|
| + ? CreateEmptySubgraph()
|
| + : NULL;
|
| + compare_graphs.Add(subgraph);
|
| + }
|
| +
|
| + HSubgraph* prev_graph = current_subgraph_;
|
| + HCompare* prev_compare_inst = NULL;
|
| + for (int i = 0; i < num_clauses; i++) {
|
| + CaseClause* clause = clauses->at(i);
|
| + if (clause->is_default()) continue;
|
| +
|
| + // Finish the previous graph by connecting it to the current.
|
| + HSubgraph* subgraph = compare_graphs.at(i);
|
| + if (prev_compare_inst == NULL) {
|
| + ASSERT(prev_graph == current_subgraph_);
|
| + prev_graph->exit_block()->Finish(new HGoto(subgraph->entry_block()));
|
| + } else {
|
| + HBasicBlock* empty = graph()->CreateBasicBlock();
|
| + prev_graph->exit_block()->Finish(new HBranch(empty,
|
| + subgraph->entry_block(),
|
| + prev_compare_inst));
|
| + }
|
| +
|
| + // Build instructions for current subgraph.
|
| + prev_compare_inst = BuildSwitchCompare(subgraph, switch_value, clause);
|
| + if (HasStackOverflow()) return;
|
| +
|
| + prev_graph = subgraph;
|
| + }
|
| +
|
| + // Finish last comparison if there was at least one comparison.
|
| + // last_false_block is the (empty) false-block of the last comparison. If
|
| + // there are no comparisons at all (a single default clause), it is just
|
| + // the last block of the current subgraph.
|
| + HBasicBlock* last_false_block = current_subgraph_->exit_block();
|
| + if (prev_graph != current_subgraph_) {
|
| + last_false_block = graph()->CreateBasicBlock();
|
| + HBasicBlock* empty = graph()->CreateBasicBlock();
|
| + prev_graph->exit_block()->Finish(new HBranch(empty,
|
| + last_false_block,
|
| + prev_compare_inst));
|
| + }
|
| +
|
| + // Build statement blocks, connect them to their comparison block and
|
| + // to the previous statement block, if there is a fall-through.
|
| + HSubgraph* previous_subgraph = NULL;
|
| + for (int i = 0; i < num_clauses; i++) {
|
| + CaseClause* clause = clauses->at(i);
|
| + HSubgraph* subgraph = CreateEmptySubgraph();
|
| +
|
| + if (clause->is_default()) {
|
| + // Default clause: Connect it to the last false block.
|
| + last_false_block->Finish(new HGoto(subgraph->entry_block()));
|
| + } else {
|
| + // Connect with the corresponding comparison.
|
| + HBasicBlock* empty =
|
| + compare_graphs.at(i)->exit_block()->end()->FirstSuccessor();
|
| + empty->Finish(new HGoto(subgraph->entry_block()));
|
| + }
|
| +
|
| + // Check for fall-through from previous statement block.
|
| + if (previous_subgraph != NULL && previous_subgraph->HasExit()) {
|
| + previous_subgraph->exit_block()->
|
| + Finish(new HGoto(subgraph->entry_block()));
|
| + }
|
| +
|
| + ADD_TO_SUBGRAPH(subgraph, clause->statements());
|
| + HBasicBlock* break_block = subgraph->BundleBreak(stmt);
|
| + if (break_block != NULL) {
|
| + break_block->Finish(new HGoto(single_exit_block));
|
| + }
|
| +
|
| + previous_subgraph = subgraph;
|
| + }
|
| +
|
| + // If the last statement block has a fall-through, connect it to the
|
| + // single exit block.
|
| + if (previous_subgraph->HasExit()) {
|
| + previous_subgraph->exit_block()->Finish(new HGoto(single_exit_block));
|
| + }
|
| +
|
| + // If there is no default clause finish the last comparison's false target.
|
| + if (!last_false_block->IsFinished()) {
|
| + last_false_block->Finish(new HGoto(single_exit_block));
|
| + }
|
| +
|
| + if (single_exit_block->HasPredecessor()) {
|
| + current_subgraph_->set_exit_block(single_exit_block);
|
| + } else {
|
| + current_subgraph_->set_exit_block(NULL);
|
| + }
|
| +}
|
| +
|
| +bool HGraph::HasOsrEntryAt(IterationStatement* statement) {
|
| + return statement->OsrEntryId() == info()->osr_ast_id();
|
| +}
|
| +
|
| +
|
| +void HSubgraph::PreProcessOsrEntry(IterationStatement* statement) {
|
| + if (!graph()->HasOsrEntryAt(statement)) return;
|
| +
|
| + HBasicBlock* non_osr_entry = graph()->CreateBasicBlock();
|
| + HBasicBlock* osr_entry = graph()->CreateBasicBlock();
|
| + HValue* true_value = graph()->GetConstantTrue();
|
| + HBranch* branch = new HBranch(non_osr_entry, osr_entry, true_value);
|
| + exit_block()->Finish(branch);
|
| +
|
| + HBasicBlock* loop_predecessor = graph()->CreateBasicBlock();
|
| + non_osr_entry->Goto(loop_predecessor);
|
| +
|
| + int osr_entry_id = statement->OsrEntryId();
|
| + // We want the correct environment at the OsrEntry instruction. Build
|
| + // it explicitly. The expression stack should be empty.
|
| + int count = osr_entry->last_environment()->total_count();
|
| + ASSERT(count == (osr_entry->last_environment()->parameter_count() +
|
| + osr_entry->last_environment()->local_count()));
|
| + for (int i = 0; i < count; ++i) {
|
| + HUnknownOSRValue* unknown = new HUnknownOSRValue;
|
| + osr_entry->AddInstruction(unknown);
|
| + osr_entry->last_environment()->Bind(i, unknown);
|
| + }
|
| +
|
| + osr_entry->AddSimulate(osr_entry_id);
|
| + osr_entry->AddInstruction(new HOsrEntry(osr_entry_id));
|
| + osr_entry->Goto(loop_predecessor);
|
| + loop_predecessor->SetJoinId(statement->EntryId());
|
| + set_exit_block(loop_predecessor);
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::VisitDoWhileStatement(DoWhileStatement* stmt) {
|
| + ASSERT(subgraph()->HasExit());
|
| + subgraph()->PreProcessOsrEntry(stmt);
|
| +
|
| + HSubgraph* body_graph = CreateLoopHeaderSubgraph(environment());
|
| + ADD_TO_SUBGRAPH(body_graph, stmt->body());
|
| + body_graph->ResolveContinue(stmt);
|
| +
|
| + if (!body_graph->HasExit() || stmt->cond()->ToBooleanIsTrue()) {
|
| + current_subgraph_->AppendEndless(body_graph, stmt);
|
| + } else {
|
| + HSubgraph* go_back = CreateEmptySubgraph();
|
| + HSubgraph* exit = CreateEmptySubgraph();
|
| + AddConditionToSubgraph(body_graph, stmt->cond(), go_back, exit);
|
| + if (HasStackOverflow()) return;
|
| + current_subgraph_->AppendDoWhile(body_graph, stmt, go_back, exit);
|
| + }
|
| +}
|
| +
|
| +
|
| +bool HGraphBuilder::ShouldPeel(HSubgraph* cond, HSubgraph* body) {
|
| + return FLAG_use_peeling;
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::VisitWhileStatement(WhileStatement* stmt) {
|
| + ASSERT(subgraph()->HasExit());
|
| + subgraph()->PreProcessOsrEntry(stmt);
|
| +
|
| + HSubgraph* cond_graph = NULL;
|
| + HSubgraph* body_graph = NULL;
|
| + HSubgraph* exit_graph = NULL;
|
| +
|
| + // If the condition is constant true, do not generate a condition subgraph.
|
| + if (stmt->cond()->ToBooleanIsTrue()) {
|
| + body_graph = CreateLoopHeaderSubgraph(environment());
|
| + ADD_TO_SUBGRAPH(body_graph, stmt->body());
|
| + } else {
|
| + cond_graph = CreateLoopHeaderSubgraph(environment());
|
| + body_graph = CreateEmptySubgraph();
|
| + exit_graph = CreateEmptySubgraph();
|
| + AddConditionToSubgraph(cond_graph, stmt->cond(), body_graph, exit_graph);
|
| + if (HasStackOverflow()) return;
|
| + ADD_TO_SUBGRAPH(body_graph, stmt->body());
|
| + }
|
| +
|
| + body_graph->ResolveContinue(stmt);
|
| +
|
| + if (cond_graph != NULL) {
|
| + AppendPeeledWhile(stmt, cond_graph, body_graph, exit_graph);
|
| + } else {
|
| + // TODO(fschneider): Implement peeling for endless loops as well.
|
| + current_subgraph_->AppendEndless(body_graph, stmt);
|
| + }
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::AppendPeeledWhile(IterationStatement* stmt,
|
| + HSubgraph* cond_graph,
|
| + HSubgraph* body_graph,
|
| + HSubgraph* exit_graph) {
|
| + HSubgraph* loop = NULL;
|
| + if (body_graph->HasExit() && stmt != peeled_statement_ &&
|
| + ShouldPeel(cond_graph, body_graph)) {
|
| + // Save the last peeled iteration statement to prevent infinite recursion.
|
| + IterationStatement* outer_peeled_statement = peeled_statement_;
|
| + peeled_statement_ = stmt;
|
| + loop = CreateGotoSubgraph(body_graph->environment());
|
| + ADD_TO_SUBGRAPH(loop, stmt);
|
| + peeled_statement_ = outer_peeled_statement;
|
| + }
|
| + current_subgraph_->AppendWhile(cond_graph, body_graph, stmt, loop,
|
| + exit_graph);
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::VisitForStatement(ForStatement* stmt) {
|
| + // Only visit the init statement in the peeled part of the loop.
|
| + if (stmt->init() != NULL && peeled_statement_ != stmt) {
|
| + Visit(stmt->init());
|
| + CHECK_BAILOUT;
|
| + }
|
| + ASSERT(subgraph()->HasExit());
|
| + subgraph()->PreProcessOsrEntry(stmt);
|
| +
|
| + HSubgraph* cond_graph = NULL;
|
| + HSubgraph* body_graph = NULL;
|
| + HSubgraph* exit_graph = NULL;
|
| + if (stmt->cond() != NULL) {
|
| + cond_graph = CreateLoopHeaderSubgraph(environment());
|
| + body_graph = CreateEmptySubgraph();
|
| + exit_graph = CreateEmptySubgraph();
|
| + AddConditionToSubgraph(cond_graph, stmt->cond(), body_graph, exit_graph);
|
| + if (HasStackOverflow()) return;
|
| + ADD_TO_SUBGRAPH(body_graph, stmt->body());
|
| + } else {
|
| + body_graph = CreateLoopHeaderSubgraph(environment());
|
| + ADD_TO_SUBGRAPH(body_graph, stmt->body());
|
| + }
|
| +
|
| + HSubgraph* next_graph = NULL;
|
| + body_graph->ResolveContinue(stmt);
|
| +
|
| + if (stmt->next() != NULL && body_graph->HasExit()) {
|
| + next_graph = CreateGotoSubgraph(body_graph->environment());
|
| + ADD_TO_SUBGRAPH(next_graph, stmt->next());
|
| + body_graph->Append(next_graph, NULL);
|
| + next_graph->entry_block()->SetJoinId(stmt->ContinueId());
|
| + }
|
| +
|
| + if (cond_graph != NULL) {
|
| + AppendPeeledWhile(stmt, cond_graph, body_graph, exit_graph);
|
| + } else {
|
| + current_subgraph_->AppendEndless(body_graph, stmt);
|
| + }
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::VisitForInStatement(ForInStatement* stmt) {
|
| + BAILOUT("ForInStatement");
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::VisitTryCatchStatement(TryCatchStatement* stmt) {
|
| + BAILOUT("TryCatchStatement");
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::VisitTryFinallyStatement(TryFinallyStatement* stmt) {
|
| + BAILOUT("TryFinallyStatement");
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::VisitDebuggerStatement(DebuggerStatement* stmt) {
|
| + BAILOUT("DebuggerStatement");
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::VisitFunctionLiteral(FunctionLiteral* expr) {
|
| + Handle<SharedFunctionInfo> shared_info =
|
| + Compiler::BuildFunctionInfo(expr, graph_->info()->script());
|
| + CHECK_BAILOUT;
|
| + PushAndAdd(new HFunctionLiteral(shared_info, expr->pretenure()));
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::VisitSharedFunctionInfoLiteral(
|
| + SharedFunctionInfoLiteral* expr) {
|
| + BAILOUT("SharedFunctionInfoLiteral");
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::VisitConditional(Conditional* expr) {
|
| + HSubgraph* then_graph = CreateEmptySubgraph();
|
| + HSubgraph* else_graph = CreateEmptySubgraph();
|
| + VisitCondition(expr->condition(),
|
| + then_graph->entry_block(),
|
| + else_graph->entry_block(),
|
| + false, false);
|
| + if (HasStackOverflow()) return;
|
| + ADD_TO_SUBGRAPH(then_graph, expr->then_expression());
|
| + ADD_TO_SUBGRAPH(else_graph, expr->else_expression());
|
| + current_subgraph_->AppendJoin(then_graph, else_graph, expr);
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::LookupGlobalPropertyCell(VariableProxy* expr,
|
| + LookupResult* lookup,
|
| + bool is_store) {
|
| + if (expr->is_this()) {
|
| + BAILOUT("global this reference");
|
| + }
|
| + if (!graph()->info()->has_global_object()) {
|
| + BAILOUT("no global object to optimize VariableProxy");
|
| + }
|
| + Handle<GlobalObject> global(graph()->info()->global_object());
|
| + global->Lookup(*expr->name(), lookup);
|
| + if (!lookup->IsProperty()) {
|
| + BAILOUT("global variable cell not yet introduced");
|
| + }
|
| + if (lookup->type() != NORMAL) {
|
| + BAILOUT("global variable has accessors");
|
| + }
|
| + if (is_store && lookup->IsReadOnly()) {
|
| + BAILOUT("read-only global variable");
|
| + }
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::HandleGlobalVariableLoad(VariableProxy* expr) {
|
| + LookupResult lookup;
|
| + LookupGlobalPropertyCell(expr, &lookup, false);
|
| + CHECK_BAILOUT;
|
| +
|
| + Handle<GlobalObject> global(graph()->info()->global_object());
|
| + // TODO(3039103): Handle global property load through an IC call when access
|
| + // checks are enabled.
|
| + if (global->IsAccessCheckNeeded()) {
|
| + BAILOUT("global object requires access check");
|
| + }
|
| + Handle<JSGlobalPropertyCell> cell(global->GetPropertyCell(&lookup));
|
| + bool check_hole = !lookup.IsDontDelete() || lookup.IsReadOnly();
|
| + PushAndAdd(new HLoadGlobal(cell, check_hole));
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::VisitVariableProxy(VariableProxy* expr) {
|
| + Variable* variable = expr->AsVariable();
|
| + if (variable == NULL) {
|
| + BAILOUT("reference to rewritten variable");
|
| + } else if (variable->IsStackAllocated()) {
|
| + if (environment()->Lookup(variable)->CheckFlag(HValue::kIsArguments)) {
|
| + BAILOUT("unsupported context for arguments object");
|
| + }
|
| + Push(environment()->Lookup(variable));
|
| + } else if (variable->is_global()) {
|
| + HandleGlobalVariableLoad(expr);
|
| + } else {
|
| + BAILOUT("reference to non-stack-allocated/non-global variable");
|
| + }
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::VisitLiteral(Literal* expr) {
|
| + PushAndAdd(new HConstant(expr->handle(), Representation::Tagged()));
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::VisitRegExpLiteral(RegExpLiteral* expr) {
|
| + PushAndAdd(new HRegExpLiteral(expr->pattern(),
|
| + expr->flags(),
|
| + expr->literal_index()));
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::VisitObjectLiteral(ObjectLiteral* expr) {
|
| + HObjectLiteral* literal = (new HObjectLiteral(expr->constant_properties(),
|
| + expr->fast_elements(),
|
| + expr->literal_index(),
|
| + expr->depth()));
|
| + PushAndAdd(literal);
|
| +
|
| + expr->CalculateEmitStore();
|
| +
|
| + for (int i = 0; i < expr->properties()->length(); i++) {
|
| + ObjectLiteral::Property* property = expr->properties()->at(i);
|
| + if (property->IsCompileTimeValue()) continue;
|
| +
|
| + Literal* key = property->key();
|
| + Expression* value = property->value();
|
| +
|
| + switch (property->kind()) {
|
| + case ObjectLiteral::Property::MATERIALIZED_LITERAL:
|
| + ASSERT(!CompileTimeValue::IsCompileTimeValue(value));
|
| + // Fall through.
|
| + case ObjectLiteral::Property::COMPUTED:
|
| + if (key->handle()->IsSymbol()) {
|
| + if (property->emit_store()) {
|
| + VISIT_FOR_VALUE(value);
|
| + HValue* value = Pop();
|
| + Handle<String> name = Handle<String>::cast(key->handle());
|
| + AddInstruction(new HStoreNamedGeneric(literal, name, value));
|
| + AddSimulate(key->id());
|
| + } else {
|
| + VISIT_FOR_EFFECT(value);
|
| + }
|
| + break;
|
| + }
|
| + // Fall through.
|
| + case ObjectLiteral::Property::PROTOTYPE:
|
| + case ObjectLiteral::Property::SETTER:
|
| + case ObjectLiteral::Property::GETTER:
|
| + BAILOUT("Object literal with complex property");
|
| + default: UNREACHABLE();
|
| + }
|
| + }
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::VisitArrayLiteral(ArrayLiteral* expr) {
|
| + ZoneList<Expression*>* subexprs = expr->values();
|
| + int length = subexprs->length();
|
| +
|
| + HArrayLiteral* literal = new HArrayLiteral(expr->constant_elements(),
|
| + length,
|
| + expr->literal_index(),
|
| + expr->depth());
|
| + PushAndAdd(literal);
|
| + HValue* elements = AddInstruction(new HLoadElements(literal));
|
| +
|
| + for (int i = 0; i < length; i++) {
|
| + Expression* subexpr = subexprs->at(i);
|
| + // If the subexpression is a literal or a simple materialized literal it
|
| + // is already set in the cloned array.
|
| + if (CompileTimeValue::IsCompileTimeValue(subexpr)) continue;
|
| +
|
| + VISIT_FOR_VALUE(subexpr);
|
| + HValue* value = Pop();
|
| + if (!Smi::IsValid(i)) BAILOUT("Non-smi key in array literal");
|
| + HValue* key = AddInstruction(new HConstant(Handle<Object>(Smi::FromInt(i)),
|
| + Representation::Integer32()));
|
| + AddInstruction(new HStoreKeyedFastElement(elements, key, value));
|
| + AddSimulate(expr->GetIdForElement(i));
|
| + }
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::VisitCatchExtensionObject(CatchExtensionObject* expr) {
|
| + BAILOUT("CatchExtensionObject");
|
| +}
|
| +
|
| +
|
| +HBasicBlock* HGraphBuilder::BuildTypeSwitch(ZoneMapList* maps,
|
| + ZoneList<HSubgraph*>* subgraphs,
|
| + HValue* receiver,
|
| + int join_id) {
|
| + ASSERT(subgraphs->length() == (maps->length() + 1));
|
| +
|
| + // Build map compare subgraphs for all but the first map.
|
| + ZoneList<HSubgraph*> map_compare_subgraphs(maps->length() - 1);
|
| + for (int i = maps->length() - 1; i > 0; --i) {
|
| + HSubgraph* subgraph = CreateBranchSubgraph(environment());
|
| + SubgraphScope scope(this, subgraph);
|
| + HSubgraph* else_subgraph =
|
| + (i == (maps->length() - 1))
|
| + ? subgraphs->last()
|
| + : map_compare_subgraphs.last();
|
| + current_subgraph_->exit_block()->Finish(
|
| + new HCompareMapAndBranch(receiver,
|
| + maps->at(i),
|
| + subgraphs->at(i)->entry_block(),
|
| + else_subgraph->entry_block()));
|
| + map_compare_subgraphs.Add(subgraph);
|
| + }
|
| +
|
| + // Generate first map check to end the current block.
|
| + AddInstruction(new HCheckNonSmi(receiver));
|
| + HSubgraph* else_subgraph =
|
| + (maps->length() == 1) ? subgraphs->at(1) : map_compare_subgraphs.last();
|
| + current_subgraph_->exit_block()->Finish(
|
| + new HCompareMapAndBranch(receiver,
|
| + Handle<Map>(maps->first()),
|
| + subgraphs->first()->entry_block(),
|
| + else_subgraph->entry_block()));
|
| +
|
| + // Join all the call subgraphs in a new basic block and make
|
| + // this basic block the current basic block.
|
| + HBasicBlock* join_block = graph_->CreateBasicBlock();
|
| + for (int i = 0; i < subgraphs->length(); ++i) {
|
| + if (subgraphs->at(i)->HasExit()) {
|
| + subgraphs->at(i)->exit_block()->Goto(join_block);
|
| + }
|
| + }
|
| +
|
| + if (join_block->predecessors()->is_empty()) return NULL;
|
| + join_block->SetJoinId(join_id);
|
| + return join_block;
|
| +}
|
| +
|
| +
|
| +// Sets the lookup result and returns true if the store can be inlined.
|
| +static bool ComputeStoredField(Handle<Map> type,
|
| + Handle<String> name,
|
| + LookupResult* lookup) {
|
| + type->LookupInDescriptors(NULL, *name, lookup);
|
| + if (!lookup->IsPropertyOrTransition()) return false;
|
| + if (lookup->type() == FIELD) return true;
|
| + return (lookup->type() == MAP_TRANSITION) &&
|
| + (type->unused_property_fields() > 0);
|
| +}
|
| +
|
| +
|
| +static int ComputeStoredFieldIndex(Handle<Map> type,
|
| + Handle<String> name,
|
| + LookupResult* lookup) {
|
| + ASSERT(lookup->type() == FIELD || lookup->type() == MAP_TRANSITION);
|
| + if (lookup->type() == FIELD) {
|
| + return lookup->GetLocalFieldIndexFromMap(*type);
|
| + } else {
|
| + Map* transition = lookup->GetTransitionMapFromMap(*type);
|
| + return transition->PropertyIndexFor(*name) - type->inobject_properties();
|
| + }
|
| +}
|
| +
|
| +
|
| +HInstruction* HGraphBuilder::BuildStoreNamedField(HValue* object,
|
| + Handle<String> name,
|
| + HValue* value,
|
| + Handle<Map> type,
|
| + LookupResult* lookup,
|
| + bool smi_and_map_check) {
|
| + if (smi_and_map_check) {
|
| + AddInstruction(new HCheckNonSmi(object));
|
| + AddInstruction(new HCheckMap(object, type));
|
| + }
|
| +
|
| + int index = ComputeStoredFieldIndex(type, name, lookup);
|
| + bool is_in_object = index < 0;
|
| + int offset = index * kPointerSize;
|
| + if (index < 0) {
|
| + // Negative property indices are in-object properties, indexed
|
| + // from the end of the fixed part of the object.
|
| + offset += type->instance_size();
|
| + } else {
|
| + offset += FixedArray::kHeaderSize;
|
| + }
|
| + HStoreNamedField* instr =
|
| + new HStoreNamedField(object, name, value, is_in_object, offset);
|
| + if (lookup->type() == MAP_TRANSITION) {
|
| + Handle<Map> transition(lookup->GetTransitionMapFromMap(*type));
|
| + instr->set_transition(transition);
|
| + }
|
| + return instr;
|
| +}
|
| +
|
| +
|
| +HInstruction* HGraphBuilder::BuildStoreNamedGeneric(HValue* object,
|
| + Handle<String> name,
|
| + HValue* value) {
|
| + return new HStoreNamedGeneric(object, name, value);
|
| +}
|
| +
|
| +
|
| +HInstruction* HGraphBuilder::BuildStoreNamed(HValue* object,
|
| + HValue* value,
|
| + Expression* expr) {
|
| + Property* prop = (expr->AsProperty() != NULL)
|
| + ? expr->AsProperty()
|
| + : expr->AsAssignment()->target()->AsProperty();
|
| + Literal* key = prop->key()->AsLiteral();
|
| + Handle<String> name = Handle<String>::cast(key->handle());
|
| + ASSERT(!name.is_null());
|
| +
|
| + LookupResult lookup;
|
| + ZoneMapList* types = expr->GetReceiverTypes();
|
| + bool is_monomorphic = expr->IsMonomorphic() &&
|
| + ComputeStoredField(types->first(), name, &lookup);
|
| +
|
| + return is_monomorphic
|
| + ? BuildStoreNamedField(object, name, value, types->first(), &lookup,
|
| + true) // Needs smi and map check.
|
| + : BuildStoreNamedGeneric(object, name, value);
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::HandlePolymorphicStoreNamedField(Assignment* expr,
|
| + HValue* object,
|
| + HValue* value,
|
| + ZoneMapList* types,
|
| + Handle<String> name) {
|
| + int number_of_types = Min(types->length(), kMaxStorePolymorphism);
|
| + ZoneMapList maps(number_of_types);
|
| + ZoneList<HSubgraph*> subgraphs(number_of_types + 1);
|
| + bool needs_generic = (types->length() > kMaxStorePolymorphism);
|
| +
|
| + // Build subgraphs for each of the specific maps.
|
| + //
|
| + // TODO(ager): We should recognize when the prototype chains for
|
| + // different maps are identical. In that case we can avoid
|
| + // repeatedly generating the same prototype map checks.
|
| + for (int i = 0; i < number_of_types; ++i) {
|
| + Handle<Map> map = types->at(i);
|
| + LookupResult lookup;
|
| + if (ComputeStoredField(map, name, &lookup)) {
|
| + maps.Add(map);
|
| + HSubgraph* subgraph = CreateBranchSubgraph(environment());
|
| + SubgraphScope scope(this, subgraph);
|
| + HInstruction* instr =
|
| + BuildStoreNamedField(object, name, value, map, &lookup, false);
|
| + Push(value);
|
| + instr->set_position(expr->position());
|
| + AddInstruction(instr);
|
| + subgraphs.Add(subgraph);
|
| + } else {
|
| + needs_generic = true;
|
| + }
|
| + }
|
| +
|
| + // If none of the properties were named fields we generate a
|
| + // generic store.
|
| + if (maps.length() == 0) {
|
| + HInstruction* instr = new HStoreNamedGeneric(object, name, value);
|
| + Push(value);
|
| + instr->set_position(expr->position());
|
| + AddInstruction(instr);
|
| + return;
|
| + }
|
| +
|
| + // Build subgraph for generic store through IC.
|
| + {
|
| + HSubgraph* subgraph = CreateBranchSubgraph(environment());
|
| + SubgraphScope scope(this, subgraph);
|
| + if (!needs_generic && FLAG_deoptimize_uncommon_cases) {
|
| + subgraph->FinishExit(new HDeoptimize());
|
| + } else {
|
| + HInstruction* instr = new HStoreNamedGeneric(object, name, value);
|
| + Push(value);
|
| + instr->set_position(expr->position());
|
| + AddInstruction(instr);
|
| + }
|
| + subgraphs.Add(subgraph);
|
| + }
|
| +
|
| + HBasicBlock* new_exit_block =
|
| + BuildTypeSwitch(&maps, &subgraphs, object, expr->id());
|
| + current_subgraph_->set_exit_block(new_exit_block);
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::HandlePropertyAssignment(Assignment* expr) {
|
| + Property* prop = expr->target()->AsProperty();
|
| + ASSERT(prop != NULL);
|
| + expr->RecordTypeFeedback(oracle());
|
| + VISIT_FOR_VALUE(prop->obj());
|
| +
|
| + HValue* value = NULL;
|
| + HInstruction* instr = NULL;
|
| +
|
| + if (prop->key()->IsPropertyName()) {
|
| + // Named store.
|
| + VISIT_FOR_VALUE(expr->value());
|
| + value = Pop();
|
| + HValue* object = Pop();
|
| +
|
| + Literal* key = prop->key()->AsLiteral();
|
| + Handle<String> name = Handle<String>::cast(key->handle());
|
| + ASSERT(!name.is_null());
|
| +
|
| + ZoneMapList* types = expr->GetReceiverTypes();
|
| + LookupResult lookup;
|
| +
|
| + if (expr->IsMonomorphic()) {
|
| + instr = BuildStoreNamed(object, value, expr);
|
| +
|
| + } else if (types != NULL && types->length() > 1) {
|
| + HandlePolymorphicStoreNamedField(expr, object, value, types, name);
|
| + return;
|
| +
|
| + } else {
|
| + instr = new HStoreNamedGeneric(object, name, value);
|
| + }
|
| +
|
| + } else {
|
| + // Keyed store.
|
| + VISIT_FOR_VALUE(prop->key());
|
| + VISIT_FOR_VALUE(expr->value());
|
| + value = Pop();
|
| + HValue* key = Pop();
|
| + HValue* object = Pop();
|
| +
|
| + bool is_fast_elements = expr->IsMonomorphic() &&
|
| + expr->GetMonomorphicReceiverType()->has_fast_elements();
|
| +
|
| + instr = is_fast_elements
|
| + ? BuildStoreKeyedFastElement(object, key, value, expr)
|
| + : BuildStoreKeyedGeneric(object, key, value);
|
| + }
|
| +
|
| + Push(value);
|
| + instr->set_position(expr->position());
|
| + AddInstruction(instr);
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::HandleGlobalVariableAssignment(VariableProxy* proxy,
|
| + HValue* value,
|
| + int position) {
|
| + LookupResult lookup;
|
| + LookupGlobalPropertyCell(proxy, &lookup, true);
|
| + CHECK_BAILOUT;
|
| +
|
| + Handle<GlobalObject> global(graph()->info()->global_object());
|
| + Handle<JSGlobalPropertyCell> cell(global->GetPropertyCell(&lookup));
|
| + HInstruction* instr = new HStoreGlobal(value, cell);
|
| + instr->set_position(position);
|
| + AddInstruction(instr);
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::HandleCompoundAssignment(Assignment* expr) {
|
| + Expression* target = expr->target();
|
| + VariableProxy* proxy = target->AsVariableProxy();
|
| + Variable* var = proxy->AsVariable();
|
| + Property* prop = target->AsProperty();
|
| + ASSERT(var == NULL || prop == NULL);
|
| +
|
| + // We have a second position recorded in the FullCodeGenerator to have
|
| + // type feedback for the binary operation.
|
| + BinaryOperation* operation = expr->binary_operation();
|
| + operation->RecordTypeFeedback(oracle());
|
| +
|
| + if (var != NULL) {
|
| + if (!var->is_global() && !var->IsStackAllocated()) {
|
| + BAILOUT("non-stack/non-global in compound assignment");
|
| + }
|
| +
|
| + VISIT_FOR_VALUE(operation);
|
| +
|
| + if (var->is_global()) {
|
| + HandleGlobalVariableAssignment(proxy, Top(), expr->position());
|
| + } else {
|
| + Bind(var, Top());
|
| + }
|
| + } else if (prop != NULL) {
|
| + prop->RecordTypeFeedback(oracle());
|
| +
|
| + if (prop->key()->IsPropertyName()) {
|
| + // Named property.
|
| + VISIT_FOR_VALUE(prop->obj());
|
| + HValue* obj = Top();
|
| +
|
| + HInstruction* load = NULL;
|
| + if (prop->IsMonomorphic()) {
|
| + Handle<String> name = prop->key()->AsLiteral()->AsPropertyName();
|
| + Handle<Map> map = prop->GetReceiverTypes()->first();
|
| + load = BuildLoadNamed(obj, prop, map, name);
|
| + } else {
|
| + load = BuildLoadNamedGeneric(obj, prop);
|
| + }
|
| + PushAndAdd(load);
|
| + if (load->HasSideEffects()) {
|
| + AddSimulate(expr->compound_bailout_id());
|
| + }
|
| +
|
| + VISIT_FOR_VALUE(expr->value());
|
| + HValue* right = Pop();
|
| + HValue* left = Pop();
|
| +
|
| + HInstruction* instr = BuildBinaryOperation(operation, left, right);
|
| + PushAndAdd(instr);
|
| + if (instr->HasSideEffects()) AddSimulate(operation->id());
|
| +
|
| + HInstruction* store = BuildStoreNamed(obj, instr, prop);
|
| + AddInstruction(store);
|
| +
|
| + // Drop the simulated receiver and value and put back the value.
|
| + Drop(2);
|
| + Push(instr);
|
| +
|
| + } else {
|
| + // Keyed property.
|
| + VISIT_FOR_VALUE(prop->obj());
|
| + VISIT_FOR_VALUE(prop->key());
|
| + HValue* obj = environment()->ExpressionStackAt(1);
|
| + HValue* key = environment()->ExpressionStackAt(0);
|
| +
|
| + bool is_fast_elements = prop->IsMonomorphic() &&
|
| + prop->GetMonomorphicReceiverType()->has_fast_elements();
|
| +
|
| + HInstruction* load = is_fast_elements
|
| + ? BuildLoadKeyedFastElement(obj, key, prop)
|
| + : BuildLoadKeyedGeneric(obj, key);
|
| + PushAndAdd(load);
|
| + if (load->HasSideEffects()) {
|
| + AddSimulate(expr->compound_bailout_id());
|
| + }
|
| +
|
| + VISIT_FOR_VALUE(expr->value());
|
| + HValue* right = Pop();
|
| + HValue* left = Pop();
|
| +
|
| + HInstruction* instr = BuildBinaryOperation(operation, left, right);
|
| + PushAndAdd(instr);
|
| + if (instr->HasSideEffects()) AddSimulate(operation->id());
|
| +
|
| + HInstruction* store = is_fast_elements
|
| + ? BuildStoreKeyedFastElement(obj, key, instr, prop)
|
| + : BuildStoreKeyedGeneric(obj, key, instr);
|
| + AddInstruction(store);
|
| +
|
| + // Drop the simulated receiver, key and value and put back the value.
|
| + Drop(3);
|
| + Push(instr);
|
| + }
|
| + } else {
|
| + BAILOUT("invalid lhs in compound assignment");
|
| + }
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::VisitAssignment(Assignment* expr) {
|
| + VariableProxy* proxy = expr->target()->AsVariableProxy();
|
| + Variable* var = proxy->AsVariable();
|
| + Property* prop = expr->target()->AsProperty();
|
| + ASSERT(var == NULL || prop == NULL);
|
| +
|
| + if (expr->is_compound()) {
|
| + HandleCompoundAssignment(expr);
|
| + return;
|
| + }
|
| +
|
| + if (var != NULL) {
|
| + if (proxy->IsArguments()) BAILOUT("assignment to arguments");
|
| + if (var->is_global()) {
|
| + VISIT_FOR_VALUE(expr->value());
|
| + HandleGlobalVariableAssignment(proxy, Top(), expr->position());
|
| + } else {
|
| + // We allow reference to the arguments object only in assignemtns
|
| + // to local variables to make sure that the arguments object does
|
| + // not escape and is not modified.
|
| + VariableProxy* rhs = expr->value()->AsVariableProxy();
|
| + if (rhs != NULL &&
|
| + rhs->var()->IsStackAllocated() &&
|
| + environment()->Lookup(rhs->var())->CheckFlag(HValue::kIsArguments)) {
|
| + Push(environment()->Lookup(rhs->var()));
|
| + } else {
|
| + VISIT_FOR_VALUE(expr->value());
|
| + }
|
| +
|
| + Bind(proxy->var(), Top());
|
| + }
|
| + } else if (prop != NULL) {
|
| + HandlePropertyAssignment(expr);
|
| + } else {
|
| + BAILOUT("unsupported invalid lhs");
|
| + }
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::VisitThrow(Throw* expr) {
|
| + VISIT_FOR_VALUE(expr->exception());
|
| +
|
| + HValue* value = environment()->Pop();
|
| + HControlInstruction* instr = new HThrow(value);
|
| + instr->set_position(expr->position());
|
| + current_subgraph_->FinishExit(instr);
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::HandlePolymorphicLoadNamedField(Property* expr,
|
| + HValue* object,
|
| + ZoneMapList* types,
|
| + Handle<String> name) {
|
| + int number_of_types = Min(types->length(), kMaxLoadPolymorphism);
|
| + ZoneMapList maps(number_of_types);
|
| + ZoneList<HSubgraph*> subgraphs(number_of_types + 1);
|
| + bool needs_generic = (types->length() > kMaxLoadPolymorphism);
|
| +
|
| + // Build subgraphs for each of the specific maps.
|
| + //
|
| + // TODO(ager): We should recognize when the prototype chains for
|
| + // different maps are identical. In that case we can avoid
|
| + // repeatedly generating the same prototype map checks.
|
| + for (int i = 0; i < number_of_types; ++i) {
|
| + Handle<Map> map = types->at(i);
|
| + LookupResult lookup;
|
| + map->LookupInDescriptors(NULL, *name, &lookup);
|
| + if (lookup.IsProperty() && lookup.type() == FIELD) {
|
| + maps.Add(map);
|
| + HSubgraph* subgraph = CreateBranchSubgraph(environment());
|
| + SubgraphScope scope(this, subgraph);
|
| + HInstruction* instr =
|
| + BuildLoadNamedField(object, expr, map, &lookup, false);
|
| + PushAndAdd(instr, expr->position());
|
| + subgraphs.Add(subgraph);
|
| + } else {
|
| + needs_generic = true;
|
| + }
|
| + }
|
| +
|
| + // If none of the properties were named fields we generate a
|
| + // generic load.
|
| + if (maps.length() == 0) {
|
| + HInstruction* instr = BuildLoadNamedGeneric(object, expr);
|
| + PushAndAdd(instr, expr->position());
|
| + return;
|
| + }
|
| +
|
| + // Build subgraph for generic load through IC.
|
| + {
|
| + HSubgraph* subgraph = CreateBranchSubgraph(environment());
|
| + SubgraphScope scope(this, subgraph);
|
| + if (!needs_generic && FLAG_deoptimize_uncommon_cases) {
|
| + subgraph->FinishExit(new HDeoptimize());
|
| + } else {
|
| + HInstruction* instr = BuildLoadNamedGeneric(object, expr);
|
| + PushAndAdd(instr, expr->position());
|
| + }
|
| + subgraphs.Add(subgraph);
|
| + }
|
| +
|
| + HBasicBlock* new_exit_block =
|
| + BuildTypeSwitch(&maps, &subgraphs, object, expr->id());
|
| + current_subgraph_->set_exit_block(new_exit_block);
|
| +}
|
| +
|
| +
|
| +HInstruction* HGraphBuilder::BuildLoadNamedField(HValue* object,
|
| + Property* expr,
|
| + Handle<Map> type,
|
| + LookupResult* lookup,
|
| + bool smi_and_map_check) {
|
| + if (smi_and_map_check) {
|
| + AddInstruction(new HCheckNonSmi(object));
|
| + AddInstruction(new HCheckMap(object, type));
|
| + }
|
| +
|
| + int index = lookup->GetLocalFieldIndexFromMap(*type);
|
| + if (index < 0) {
|
| + // Negative property indices are in-object properties, indexed
|
| + // from the end of the fixed part of the object.
|
| + int offset = (index * kPointerSize) + type->instance_size();
|
| + return new HLoadNamedField(object, true, offset);
|
| + } else {
|
| + // Non-negative property indices are in the properties array.
|
| + int offset = (index * kPointerSize) + FixedArray::kHeaderSize;
|
| + return new HLoadNamedField(object, false, offset);
|
| + }
|
| +}
|
| +
|
| +
|
| +HInstruction* HGraphBuilder::BuildLoadNamedGeneric(HValue* obj,
|
| + Property* expr) {
|
| + ASSERT(expr->key()->IsPropertyName());
|
| + Handle<Object> name = expr->key()->AsLiteral()->handle();
|
| + return new HLoadNamedGeneric(obj, name);
|
| +}
|
| +
|
| +
|
| +HInstruction* HGraphBuilder::BuildLoadNamed(HValue* obj,
|
| + Property* expr,
|
| + Handle<Map> map,
|
| + Handle<String> name) {
|
| + LookupResult lookup;
|
| + map->LookupInDescriptors(NULL, *name, &lookup);
|
| + if (lookup.IsProperty() && lookup.type() == FIELD) {
|
| + return BuildLoadNamedField(obj,
|
| + expr,
|
| + map,
|
| + &lookup,
|
| + true);
|
| + } else {
|
| + return BuildLoadNamedGeneric(obj, expr);
|
| + }
|
| +}
|
| +
|
| +
|
| +HInstruction* HGraphBuilder::BuildLoadKeyedGeneric(HValue* object,
|
| + HValue* key) {
|
| + return new HLoadKeyedGeneric(object, key);
|
| +}
|
| +
|
| +
|
| +HInstruction* HGraphBuilder::BuildLoadKeyedFastElement(HValue* object,
|
| + HValue* key,
|
| + Property* expr) {
|
| + ASSERT(!expr->key()->IsPropertyName() && expr->IsMonomorphic());
|
| + AddInstruction(new HCheckNonSmi(object));
|
| + Handle<Map> map = expr->GetMonomorphicReceiverType();
|
| + ASSERT(map->has_fast_elements());
|
| + AddInstruction(new HCheckMap(object, map));
|
| + HInstruction* elements = AddInstruction(new HLoadElements(object));
|
| + HInstruction* length = AddInstruction(new HArrayLength(elements));
|
| + AddInstruction(new HBoundsCheck(key, length));
|
| + return new HLoadKeyedFastElement(elements, key);
|
| +}
|
| +
|
| +
|
| +HInstruction* HGraphBuilder::BuildStoreKeyedGeneric(HValue* object,
|
| + HValue* key,
|
| + HValue* value) {
|
| + return new HStoreKeyedGeneric(object, key, value);
|
| +}
|
| +
|
| +
|
| +HInstruction* HGraphBuilder::BuildStoreKeyedFastElement(HValue* object,
|
| + HValue* key,
|
| + HValue* val,
|
| + Expression* expr) {
|
| + ASSERT(expr->IsMonomorphic());
|
| + AddInstruction(new HCheckNonSmi(object));
|
| + Handle<Map> map = expr->GetMonomorphicReceiverType();
|
| + ASSERT(map->has_fast_elements());
|
| + AddInstruction(new HCheckMap(object, map));
|
| + HInstruction* elements = AddInstruction(new HLoadElements(object));
|
| + AddInstruction(new HCheckMap(elements, FACTORY->fixed_array_map()));
|
| + bool is_array = (map->instance_type() == JS_ARRAY_TYPE);
|
| + HInstruction* length = NULL;
|
| + if (is_array) {
|
| + length = AddInstruction(new HArrayLength(object));
|
| + } else {
|
| + length = AddInstruction(new HArrayLength(elements));
|
| + }
|
| + AddInstruction(new HBoundsCheck(key, length));
|
| + return new HStoreKeyedFastElement(elements, key, val);
|
| +}
|
| +
|
| +
|
| +bool HGraphBuilder::TryArgumentsAccess(Property* expr) {
|
| + VariableProxy* proxy = expr->obj()->AsVariableProxy();
|
| + if (proxy == NULL) return false;
|
| + if (!proxy->var()->IsStackAllocated()) return false;
|
| + if (!environment()->Lookup(proxy->var())->CheckFlag(HValue::kIsArguments)) {
|
| + return false;
|
| + }
|
| +
|
| + if (expr->key()->IsPropertyName()) {
|
| + Handle<String> name = expr->key()->AsLiteral()->AsPropertyName();
|
| + if (!name->IsEqualTo(CStrVector("length"))) return false;
|
| + HInstruction* elements = AddInstruction(new HArgumentsElements);
|
| + PushAndAdd(new HArgumentsLength(elements));
|
| + } else {
|
| + VisitForValue(expr->key());
|
| + if (HasStackOverflow()) return false;
|
| + HValue* key = Pop();
|
| + HInstruction* elements = AddInstruction(new HArgumentsElements);
|
| + HInstruction* length = AddInstruction(new HArgumentsLength(elements));
|
| + AddInstruction(new HBoundsCheck(key, length));
|
| + PushAndAdd(new HAccessArgumentsAt(elements, length, key));
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::VisitProperty(Property* expr) {
|
| + expr->RecordTypeFeedback(oracle());
|
| +
|
| + if (TryArgumentsAccess(expr)) return;
|
| + CHECK_BAILOUT;
|
| +
|
| + VISIT_FOR_VALUE(expr->obj());
|
| +
|
| + HInstruction* instr = NULL;
|
| + if (expr->IsArrayLength()) {
|
| + HValue* array = Pop();
|
| + AddInstruction(new HCheckNonSmi(array));
|
| + instr = new HArrayLength(array);
|
| +
|
| + } else if (expr->key()->IsPropertyName()) {
|
| + Handle<String> name = expr->key()->AsLiteral()->AsPropertyName();
|
| + ZoneMapList* types = expr->GetReceiverTypes();
|
| +
|
| + HValue* obj = Pop();
|
| + if (expr->IsMonomorphic()) {
|
| + instr = BuildLoadNamed(obj, expr, types->first(), name);
|
| + } else if (types != NULL && types->length() > 1) {
|
| + HandlePolymorphicLoadNamedField(expr, obj, types, name);
|
| + return;
|
| +
|
| + } else {
|
| + instr = BuildLoadNamedGeneric(obj, expr);
|
| + }
|
| +
|
| + } else {
|
| + VISIT_FOR_VALUE(expr->key());
|
| +
|
| + HValue* key = Pop();
|
| + HValue* obj = Pop();
|
| +
|
| + bool is_fast_elements = expr->IsMonomorphic() &&
|
| + expr->GetMonomorphicReceiverType()->has_fast_elements();
|
| +
|
| + instr = is_fast_elements
|
| + ? BuildLoadKeyedFastElement(obj, key, expr)
|
| + : BuildLoadKeyedGeneric(obj, key);
|
| + }
|
| + PushAndAdd(instr, expr->position());
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::AddCheckConstantFunction(Call* expr,
|
| + HValue* receiver,
|
| + Handle<Map> receiver_map,
|
| + bool smi_and_map_check) {
|
| + // Constant functions have the nice property that the map will change if they
|
| + // are overwritten. Therefore it is enough to check the map of the holder and
|
| + // its prototypes.
|
| + if (smi_and_map_check) {
|
| + AddInstruction(new HCheckNonSmi(receiver));
|
| + AddInstruction(new HCheckMap(receiver, receiver_map));
|
| + }
|
| + if (!expr->holder().is_null()) {
|
| + AddInstruction(new HCheckPrototypeMaps(receiver,
|
| + expr->holder(),
|
| + receiver_map));
|
| + }
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::HandlePolymorphicCallNamed(Call* expr,
|
| + HValue* receiver,
|
| + ZoneMapList* types,
|
| + Handle<String> name) {
|
| + int argument_count = expr->arguments()->length() + 1; // Plus receiver.
|
| + int number_of_types = Min(types->length(), kMaxCallPolymorphism);
|
| + ZoneMapList maps(number_of_types);
|
| + ZoneList<HSubgraph*> subgraphs(number_of_types + 1);
|
| + bool needs_generic = (types->length() > kMaxCallPolymorphism);
|
| +
|
| + // Build subgraphs for each of the specific maps.
|
| + //
|
| + // TODO(ager): We should recognize when the prototype chains for
|
| + // different maps are identical. In that case we can avoid
|
| + // repeatedly generating the same prototype map checks.
|
| + for (int i = 0; i < number_of_types; ++i) {
|
| + Handle<Map> map = types->at(i);
|
| + if (expr->ComputeTarget(map, name)) {
|
| + maps.Add(map);
|
| + HSubgraph* subgraph = CreateBranchSubgraph(environment());
|
| + SubgraphScope scope(this, subgraph);
|
| + AddCheckConstantFunction(expr, receiver, map, false);
|
| + if (FLAG_trace_inlining && FLAG_polymorphic_inlining) {
|
| + PrintF("Trying to inline the polymorphic call to %s\n",
|
| + *name->ToCString());
|
| + }
|
| + if (!FLAG_polymorphic_inlining || !TryInline(expr)) {
|
| + // Check for bailout, as trying to inline might fail due to bailout
|
| + // during hydrogen processing.
|
| + CHECK_BAILOUT;
|
| + HCall* call = new HCallConstantFunction(expr->target(), argument_count);
|
| + ProcessCall(call, expr->position());
|
| + }
|
| + subgraphs.Add(subgraph);
|
| + } else {
|
| + needs_generic = true;
|
| + }
|
| + }
|
| +
|
| + // If we couldn't compute the target for any of the maps just
|
| + // perform an IC call.
|
| + if (maps.length() == 0) {
|
| + HCall* call = new HCallNamed(name, argument_count);
|
| + ProcessCall(call, expr->position());
|
| + return;
|
| + }
|
| +
|
| + // Build subgraph for generic call through IC.
|
| + {
|
| + HSubgraph* subgraph = CreateBranchSubgraph(environment());
|
| + SubgraphScope scope(this, subgraph);
|
| + if (!needs_generic && FLAG_deoptimize_uncommon_cases) {
|
| + subgraph->FinishExit(new HDeoptimize());
|
| + } else {
|
| + HCall* call = new HCallNamed(name, argument_count);
|
| + ProcessCall(call, expr->position());
|
| + }
|
| + subgraphs.Add(subgraph);
|
| + }
|
| +
|
| + HBasicBlock* new_exit_block =
|
| + BuildTypeSwitch(&maps, &subgraphs, receiver, expr->id());
|
| + current_subgraph_->set_exit_block(new_exit_block);
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::TraceInline(Handle<JSFunction> target, bool result) {
|
| + SmartPointer<char> callee = target->shared()->DebugName()->ToCString();
|
| + SmartPointer<char> caller =
|
| + graph()->info()->function()->debug_name()->ToCString();
|
| + if (result) {
|
| + PrintF("Inlined %s called from %s.\n", *callee, *caller);
|
| + } else {
|
| + PrintF("Do not inline %s called from %s.\n", *callee, *caller);
|
| + }
|
| +}
|
| +
|
| +
|
| +bool HGraphBuilder::TryInline(Call* expr) {
|
| + if (!FLAG_use_inlining) return false;
|
| +
|
| + // Precondition: call is monomorphic and we have found a target with the
|
| + // appropriate arity.
|
| + Handle<JSFunction> target = expr->target();
|
| +
|
| + // Do a quick check on source code length to avoid parsing large
|
| + // inlining candidates.
|
| + if (FLAG_limit_inlining && target->shared()->SourceSize() > kMaxSourceSize) {
|
| + if (FLAG_trace_inlining) TraceInline(target, false);
|
| + return false;
|
| + }
|
| +
|
| + // Target must be inlineable.
|
| + if (!target->IsInlineable()) return false;
|
| +
|
| + // No context change required.
|
| + CompilationInfo* outer_info = graph()->info();
|
| + if (target->context() != outer_info->closure()->context() ||
|
| + outer_info->scope()->contains_with() ||
|
| + outer_info->scope()->num_heap_slots() > 0) {
|
| + return false;
|
| + }
|
| +
|
| + // Don't inline deeper than two calls.
|
| + HEnvironment* env = environment();
|
| + if (env->outer() != NULL && env->outer()->outer() != NULL) return false;
|
| +
|
| + // Don't inline recursive functions.
|
| + if (target->shared() == outer_info->closure()->shared()) return false;
|
| +
|
| + // We don't want to add more than a certain number of nodes from inlining.
|
| + if (FLAG_limit_inlining && inlined_count_ > kMaxInlinedNodes) {
|
| + if (FLAG_trace_inlining) TraceInline(target, false);
|
| + return false;
|
| + }
|
| +
|
| + int count_before = AstNode::Count();
|
| +
|
| + // Parse and allocate variables.
|
| + Handle<SharedFunctionInfo> shared(target->shared());
|
| + CompilationInfo inner_info(shared);
|
| + if (!ParserApi::Parse(&inner_info) ||
|
| + !Scope::Analyze(&inner_info)) {
|
| + return false;
|
| + }
|
| + FunctionLiteral* function = inner_info.function();
|
| +
|
| + // Count the number of AST nodes added by inlining this call.
|
| + int nodes_added = AstNode::Count() - count_before;
|
| + if (FLAG_limit_inlining && nodes_added > kMaxInlinedSize) {
|
| + if (FLAG_trace_inlining) TraceInline(target, false);
|
| + return false;
|
| + }
|
| +
|
| + // Check if we can handle all declarations in the inlined functions.
|
| + VisitDeclarations(inner_info.scope()->declarations());
|
| + if (HasStackOverflow()) {
|
| + ClearStackOverflow();
|
| + return false;
|
| + }
|
| +
|
| + // Don't inline functions that uses the arguments object or that
|
| + // have a mismatching number of parameters.
|
| + int arity = expr->arguments()->length();
|
| + if (function->scope()->arguments() != NULL ||
|
| + arity != target->shared()->formal_parameter_count()) {
|
| + return false;
|
| + }
|
| +
|
| + // All statements in the body must be inlineable.
|
| + for (int i = 0, count = function->body()->length(); i < count; ++i) {
|
| + if (!function->body()->at(i)->IsInlineable()) return false;
|
| + }
|
| +
|
| + // Generate the deoptimization data for the unoptimized version of
|
| + // the target function if we don't already have it.
|
| + if (!shared->has_deoptimization_support()) {
|
| + // Note that we compile here using the same AST that we will use for
|
| + // generating the optimized inline code.
|
| + inner_info.EnableDeoptimizationSupport();
|
| + if (!FullCodeGenerator::MakeCode(&inner_info)) return false;
|
| + shared->EnableDeoptimizationSupport(*inner_info.code());
|
| + Compiler::RecordFunctionCompilation(
|
| + Logger::FUNCTION_TAG,
|
| + Handle<String>(shared->DebugName()),
|
| + shared->start_position(),
|
| + &inner_info);
|
| + }
|
| +
|
| + // Save the pending call context and type feedback oracle. Set up new ones
|
| + // for the inlined function.
|
| + ASSERT(shared->has_deoptimization_support());
|
| + AstContext* saved_call_context = call_context();
|
| + HBasicBlock* saved_function_return = function_return();
|
| + TypeFeedbackOracle* saved_oracle = oracle();
|
| + // On-stack replacement cannot target inlined functions. Since we don't
|
| + // use a separate CompilationInfo structure for the inlined function, we
|
| + // save and restore the AST ID in the original compilation info.
|
| + int saved_osr_ast_id = graph()->info()->osr_ast_id();
|
| +
|
| + TestContext* test_context = NULL;
|
| + if (ast_context()->IsTest()) {
|
| + // Inlined body is treated as if it occurs in an 'inlined' call context
|
| + // with true and false blocks that will forward to the real ones.
|
| + HBasicBlock* if_true = graph()->CreateBasicBlock();
|
| + HBasicBlock* if_false = graph()->CreateBasicBlock();
|
| + if_true->MarkAsInlineReturnTarget();
|
| + if_false->MarkAsInlineReturnTarget();
|
| + // AstContext constructor pushes on the context stack.
|
| + bool invert_true = TestContext::cast(ast_context())->invert_true();
|
| + bool invert_false = TestContext::cast(ast_context())->invert_false();
|
| + test_context = new TestContext(this, if_true, if_false,
|
| + invert_true, invert_false);
|
| + function_return_ = NULL;
|
| + } else {
|
| + // Inlined body is treated as if it occurs in the original call context.
|
| + function_return_ = graph()->CreateBasicBlock();
|
| + function_return_->MarkAsInlineReturnTarget();
|
| + }
|
| + call_context_ = ast_context();
|
| + TypeFeedbackOracle new_oracle(Handle<Code>(shared->code()));
|
| + oracle_ = &new_oracle;
|
| + graph()->info()->SetOsrAstId(AstNode::kNoNumber);
|
| +
|
| + HSubgraph* body = CreateInlinedSubgraph(env, target, function);
|
| + body->exit_block()->AddInstruction(new HEnterInlined(target, function));
|
| + AddToSubgraph(body, function->body());
|
| + if (HasStackOverflow()) {
|
| + // Bail out if the inline function did, as we cannot residualize a call
|
| + // instead.
|
| + delete test_context;
|
| + call_context_ = saved_call_context;
|
| + function_return_ = saved_function_return;
|
| + oracle_ = saved_oracle;
|
| + graph()->info()->SetOsrAstId(saved_osr_ast_id);
|
| + return false;
|
| + }
|
| +
|
| + // Update inlined nodes count.
|
| + inlined_count_ += nodes_added;
|
| +
|
| + if (FLAG_trace_inlining) TraceInline(target, true);
|
| +
|
| + if (body->HasExit()) {
|
| + // Add a return of undefined if control can fall off the body. In a
|
| + // test context, undefined is false.
|
| + HValue* return_value = NULL;
|
| + HBasicBlock* target = NULL;
|
| + if (test_context == NULL) {
|
| + ASSERT(function_return_ != NULL);
|
| + return_value = graph()->GetConstantUndefined();
|
| + target = function_return_;
|
| + } else {
|
| + return_value = graph()->GetConstantFalse();
|
| + target = test_context->if_false();
|
| + }
|
| + body->exit_block()->AddLeaveInlined(return_value, target);
|
| + body->set_exit_block(NULL);
|
| + }
|
| +
|
| + // Record the environment at the inlined function call.
|
| + AddSimulate(expr->ReturnId());
|
| +
|
| + // Jump to the function entry (without re-recording the environment).
|
| + subgraph()->exit_block()->Finish(new HGoto(body->entry_block()));
|
| +
|
| + // Fix up the function exits.
|
| + if (test_context != NULL) {
|
| + HBasicBlock* if_true = test_context->if_true();
|
| + HBasicBlock* if_false = test_context->if_false();
|
| + if_true->SetJoinId(expr->id());
|
| + if_false->SetJoinId(expr->id());
|
| + ASSERT(ast_context() == test_context);
|
| + delete test_context; // Destructor pops from expression context stack.
|
| + // Forward to the real test context.
|
| +
|
| + // Discard the lingering branch value (which may be true or false,
|
| + // depending on whether the final condition was negated) and jump to the
|
| + // true target with a true branch value.
|
| + HBasicBlock* true_target = TestContext::cast(ast_context())->if_true();
|
| + bool invert_true = TestContext::cast(ast_context())->invert_true();
|
| + HValue* true_value = invert_true
|
| + ? graph()->GetConstantFalse()
|
| + : graph()->GetConstantTrue();
|
| + if_true->last_environment()->Pop();
|
| + if (true_target->IsInlineReturnTarget()) {
|
| + if_true->AddLeaveInlined(true_value, true_target);
|
| + } else {
|
| + if_true->last_environment()->Push(true_value);
|
| + if_true->Goto(true_target);
|
| + }
|
| +
|
| + // Do the same for the false target.
|
| + HBasicBlock* false_target = TestContext::cast(ast_context())->if_false();
|
| + bool invert_false = TestContext::cast(ast_context())->invert_false();
|
| + HValue* false_value = invert_false
|
| + ? graph()->GetConstantTrue()
|
| + : graph()->GetConstantFalse();
|
| + if_false->last_environment()->Pop();
|
| + if (false_target->IsInlineReturnTarget()) {
|
| + if_false->AddLeaveInlined(false_value, false_target);
|
| + } else {
|
| + if_false->last_environment()->Push(false_value);
|
| + if_false->Goto(false_target);
|
| + }
|
| +
|
| + // TODO(kmillikin): Come up with a better way to handle this. It is too
|
| + // subtle. NULL here indicates that the enclosing context has no control
|
| + // flow to handle.
|
| + subgraph()->set_exit_block(NULL);
|
| +
|
| + } else {
|
| + function_return_->SetJoinId(expr->id());
|
| + subgraph()->set_exit_block(function_return_);
|
| + }
|
| +
|
| + call_context_ = saved_call_context;
|
| + function_return_ = saved_function_return;
|
| + oracle_ = saved_oracle;
|
| + graph()->info()->SetOsrAstId(saved_osr_ast_id);
|
| + return true;
|
| +}
|
| +
|
| +
|
| +void HBasicBlock::AddLeaveInlined(HValue* return_value, HBasicBlock* target) {
|
| + ASSERT(target->IsInlineReturnTarget());
|
| + AddInstruction(new HLeaveInlined);
|
| + HEnvironment* outer = last_environment()->outer();
|
| + outer->Push(return_value);
|
| + UpdateEnvironment(outer);
|
| + Goto(target);
|
| +}
|
| +
|
| +
|
| +bool HGraphBuilder::TryMathFunctionInline(Call* expr) {
|
| + // Try to inline calls like Math.* as operations in the calling function.
|
| + MathFunctionId id = expr->target()->shared()->math_function_id();
|
| + int argument_count = expr->arguments()->length() + 1; // Plus receiver.
|
| + switch (id) {
|
| + case kMathRound:
|
| + case kMathFloor:
|
| + case kMathAbs:
|
| + case kMathSqrt:
|
| + if (argument_count == 2) {
|
| + HValue* argument = Pop();
|
| + // Pop receiver.
|
| + Pop();
|
| + HUnaryMathOperation* op = new HUnaryMathOperation(argument, id);
|
| + PushAndAdd(op, expr->position());
|
| + return true;
|
| + }
|
| + break;
|
| + default:
|
| + // Either not a special math function or not yet supported for inlining.
|
| + break;
|
| + }
|
| + return false;
|
| +}
|
| +
|
| +
|
| +bool HGraphBuilder::TryCallApply(Call* expr) {
|
| + Expression* callee = expr->expression();
|
| + Property* prop = callee->AsProperty();
|
| + ASSERT(prop != NULL);
|
| +
|
| + if (graph()->info()->scope()->arguments() == NULL) return false;
|
| +
|
| + Handle<String> name = prop->key()->AsLiteral()->AsPropertyName();
|
| + if (!name->IsEqualTo(CStrVector("apply"))) return false;
|
| +
|
| + ZoneList<Expression*>* args = expr->arguments();
|
| + if (args->length() != 2) return false;
|
| +
|
| + VariableProxy* arg_two = args->at(1)->AsVariableProxy();
|
| + if (arg_two == NULL) return false;
|
| + HValue* arg_two_value = environment()->Lookup(arg_two->var());
|
| + if (!arg_two_value->CheckFlag(HValue::kIsArguments)) return false;
|
| +
|
| + if (!expr->IsMonomorphic()) return false;
|
| +
|
| + // Found pattern f.apply(receiver, arguments).
|
| + VisitForValue(prop->obj());
|
| + if (HasStackOverflow()) return false;
|
| + HValue* function = Pop();
|
| + VisitForValue(args->at(0));
|
| + if (HasStackOverflow()) return false;
|
| + HValue* receiver = Pop();
|
| + HInstruction* elements = AddInstruction(new HArgumentsElements);
|
| + HInstruction* length = AddInstruction(new HArgumentsLength(elements));
|
| + AddCheckConstantFunction(expr,
|
| + function,
|
| + expr->GetReceiverTypes()->first(),
|
| + true);
|
| + PushAndAdd(new HApplyArguments(function, receiver, length, elements),
|
| + expr->position());
|
| + return true;
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::VisitCall(Call* expr) {
|
| + Expression* callee = expr->expression();
|
| + int argument_count = expr->arguments()->length() + 1; // Plus receiver.
|
| + HCall* call = NULL;
|
| +
|
| + Property* prop = callee->AsProperty();
|
| + if (prop != NULL) {
|
| + if (!prop->key()->IsPropertyName()) {
|
| + // Keyed function call.
|
| + VisitArgument(prop->obj());
|
| + CHECK_BAILOUT;
|
| +
|
| + VISIT_FOR_VALUE(prop->key());
|
| + // Push receiver and key like the non-optimized code generator expects it.
|
| + HValue* key = Pop();
|
| + HValue* receiver = Pop();
|
| + Push(key);
|
| + Push(receiver);
|
| +
|
| + VisitArgumentList(expr->arguments());
|
| + CHECK_BAILOUT;
|
| +
|
| + call = new HCallKeyed(key, argument_count);
|
| + ProcessCall(call, expr->position());
|
| + HValue* result = Pop();
|
| + // Drop the receiver from the environment and put back the result of
|
| + // the call.
|
| + Drop(1);
|
| + Push(result);
|
| + return;
|
| + }
|
| +
|
| + // Named function call.
|
| + expr->RecordTypeFeedback(oracle());
|
| +
|
| + if (TryCallApply(expr)) return;
|
| + CHECK_BAILOUT;
|
| +
|
| + HValue* receiver = VisitArgument(prop->obj());
|
| + CHECK_BAILOUT;
|
| + VisitArgumentList(expr->arguments());
|
| + CHECK_BAILOUT;
|
| +
|
| + Handle<String> name = prop->key()->AsLiteral()->AsPropertyName();
|
| +
|
| + expr->RecordTypeFeedback(oracle());
|
| + ZoneMapList* types = expr->GetReceiverTypes();
|
| +
|
| + if (expr->IsMonomorphic()) {
|
| + AddCheckConstantFunction(expr, receiver, types->first(), true);
|
| +
|
| + if (TryMathFunctionInline(expr) || TryInline(expr)) {
|
| + return;
|
| + } else {
|
| + // Check for bailout, as the TryInline call in the if condition above
|
| + // might return false due to bailout during hydrogen processing.
|
| + CHECK_BAILOUT;
|
| + call = new HCallConstantFunction(expr->target(), argument_count);
|
| + }
|
| + } else if (types != NULL && types->length() > 1) {
|
| + HandlePolymorphicCallNamed(expr, receiver, types, name);
|
| + return;
|
| +
|
| + } else {
|
| + call = new HCallNamed(name, argument_count);
|
| + }
|
| +
|
| + } else {
|
| + Variable* var = expr->expression()->AsVariableProxy()->AsVariable();
|
| + bool global_call = (var != NULL) && var->is_global() && !var->is_this();
|
| +
|
| + if (!global_call) {
|
| + ++argument_count;
|
| + VisitArgument(expr->expression());
|
| + CHECK_BAILOUT;
|
| + }
|
| +
|
| + if (global_call) {
|
| + // If there is a global property cell for the name at compile time and
|
| + // access check is not enabled we assume that the function will not change
|
| + // and generate optimized code for calling the function.
|
| + CompilationInfo* info = graph()->info();
|
| + bool known_global_function = info->has_global_object() &&
|
| + !info->global_object()->IsAccessCheckNeeded() &&
|
| + expr->ComputeGlobalTarget(Handle<GlobalObject>(info->global_object()),
|
| + var->name());
|
| + if (known_global_function) {
|
| + // Push the global object instead of the global receiver because
|
| + // code generated by the full code generator expects it.
|
| + PushAndAdd(new HGlobalObject);
|
| + VisitArgumentList(expr->arguments());
|
| + CHECK_BAILOUT;
|
| +
|
| + VISIT_FOR_VALUE(expr->expression());
|
| + HValue* function = Pop();
|
| + AddInstruction(new HCheckFunction(function, expr->target()));
|
| +
|
| + // Replace the global object with the global receiver.
|
| + HGlobalReceiver* global_receiver = new HGlobalReceiver;
|
| + // Index of the receiver from the top of the expression stack.
|
| + const int receiver_index = argument_count - 1;
|
| + AddInstruction(global_receiver);
|
| + ASSERT(environment()->ExpressionStackAt(receiver_index)->
|
| + IsGlobalObject());
|
| + environment()->SetExpressionStackAt(receiver_index, global_receiver);
|
| +
|
| + if (TryInline(expr)) return;
|
| + // Check for bailout, as trying to inline might fail due to bailout
|
| + // during hydrogen processing.
|
| + CHECK_BAILOUT;
|
| +
|
| + call = new HCallKnownGlobal(expr->target(), argument_count);
|
| + } else {
|
| + PushAndAdd(new HGlobalObject);
|
| + VisitArgumentList(expr->arguments());
|
| + CHECK_BAILOUT;
|
| +
|
| + call = new HCallGlobal(var->name(), argument_count);
|
| + }
|
| +
|
| + } else {
|
| + PushAndAdd(new HGlobalReceiver);
|
| + VisitArgumentList(expr->arguments());
|
| + CHECK_BAILOUT;
|
| +
|
| + call = new HCallFunction(argument_count);
|
| + }
|
| + }
|
| +
|
| + ProcessCall(call, expr->position());
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::VisitCallNew(CallNew* expr) {
|
| + // The constructor function is also used as the receiver argument to the
|
| + // JS construct call builtin.
|
| + VisitArgument(expr->expression());
|
| + CHECK_BAILOUT;
|
| + VisitArgumentList(expr->arguments());
|
| + CHECK_BAILOUT;
|
| +
|
| + int argument_count = expr->arguments()->length() + 1; // Plus constructor.
|
| + HCall* call = new HCallNew(argument_count);
|
| +
|
| + ProcessCall(call, expr->position());
|
| +}
|
| +
|
| +
|
| +// Support for generating inlined runtime functions.
|
| +
|
| +// Lookup table for generators for runtime calls that are generated inline.
|
| +// Elements of the table are member pointers to functions of HGraphBuilder.
|
| +#define INLINE_FUNCTION_GENERATOR_ADDRESS(Name, argc, ressize) \
|
| + &HGraphBuilder::Generate##Name,
|
| +
|
| +const HGraphBuilder::InlineFunctionGenerator
|
| + HGraphBuilder::kInlineFunctionGenerators[] = {
|
| + INLINE_FUNCTION_LIST(INLINE_FUNCTION_GENERATOR_ADDRESS)
|
| + INLINE_RUNTIME_FUNCTION_LIST(INLINE_FUNCTION_GENERATOR_ADDRESS)
|
| +};
|
| +#undef INLINE_FUNCTION_GENERATOR_ADDRESS
|
| +
|
| +
|
| +void HGraphBuilder::VisitCallRuntime(CallRuntime* expr) {
|
| + Handle<String> name = expr->name();
|
| + if (name->IsEqualTo(CStrVector("_Log"))) {
|
| + Push(graph()->GetConstantUndefined());
|
| + return;
|
| + }
|
| +
|
| + const Runtime::Function* function = expr->function();
|
| + if (expr->is_jsruntime()) {
|
| + BAILOUT("call to a JavaScript runtime function");
|
| + }
|
| + ASSERT(function != NULL);
|
| +
|
| + VisitArgumentList(expr->arguments());
|
| + CHECK_BAILOUT;
|
| +
|
| + int argument_count = expr->arguments()->length();
|
| + if (function->intrinsic_type == Runtime::INLINE) {
|
| + ASSERT(name->length() > 0);
|
| + ASSERT(name->Get(0) == '_');
|
| + // Call to an inline function.
|
| + int lookup_index = static_cast<int>(function->function_id) -
|
| + static_cast<int>(Runtime::kFirstInlineFunction);
|
| + ASSERT(lookup_index >= 0);
|
| + ASSERT(static_cast<size_t>(lookup_index) <
|
| + ARRAY_SIZE(kInlineFunctionGenerators));
|
| + InlineFunctionGenerator generator = kInlineFunctionGenerators[lookup_index];
|
| +
|
| + // Call the inline code generator using the pointer-to-member.
|
| + (this->*generator)(argument_count);
|
| + } else {
|
| + ASSERT(function->intrinsic_type == Runtime::RUNTIME);
|
| + HCall* call = new HCallRuntime(name, expr->function(), argument_count);
|
| + ProcessCall(call, RelocInfo::kNoPosition);
|
| + }
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::VisitUnaryOperation(UnaryOperation* expr) {
|
| + Token::Value op = expr->op();
|
| + if (op == Token::VOID) {
|
| + VISIT_FOR_EFFECT(expr->expression());
|
| + Push(graph()->GetConstantUndefined());
|
| + } else if (op == Token::DELETE) {
|
| + Property* prop = expr->expression()->AsProperty();
|
| + Variable* var = expr->expression()->AsVariableProxy()->AsVariable();
|
| + if (prop == NULL && var == NULL) {
|
| + // Result of deleting non-property, non-variable reference is true.
|
| + // Evaluate the subexpression for side effects.
|
| + VISIT_FOR_EFFECT(expr->expression());
|
| + Push(graph_->GetConstantTrue());
|
| + } else if (var != NULL &&
|
| + !var->is_global() &&
|
| + var->AsSlot() != NULL &&
|
| + var->AsSlot()->type() != Slot::LOOKUP) {
|
| + // Result of deleting non-global, non-dynamic variables is false.
|
| + // The subexpression does not have side effects.
|
| + Push(graph_->GetConstantFalse());
|
| + } else if (prop != NULL) {
|
| + VISIT_FOR_VALUE(prop->obj());
|
| + VISIT_FOR_VALUE(prop->key());
|
| + HValue* key = Pop();
|
| + HValue* obj = Pop();
|
| + PushAndAdd(new HDeleteProperty(obj, key));
|
| + } else if (var->is_global()) {
|
| + BAILOUT("delete with global variable");
|
| + } else {
|
| + BAILOUT("delete with non-global variable");
|
| + }
|
| + } else if (op == Token::NOT) {
|
| + HSubgraph* true_graph = CreateEmptySubgraph();
|
| + HSubgraph* false_graph = CreateEmptySubgraph();
|
| + VisitCondition(expr->expression(),
|
| + false_graph->entry_block(),
|
| + true_graph->entry_block(),
|
| + true, true);
|
| + if (HasStackOverflow()) return;
|
| + true_graph->environment()->Push(graph_->GetConstantTrue());
|
| + false_graph->environment()->Push(graph_->GetConstantFalse());
|
| + current_subgraph_->AppendJoin(true_graph, false_graph, expr);
|
| + } else if (op == Token::BIT_NOT || op == Token::SUB) {
|
| + VISIT_FOR_VALUE(expr->expression());
|
| + HValue* value = Pop();
|
| + HInstruction* instr = NULL;
|
| + switch (op) {
|
| + case Token::BIT_NOT:
|
| + instr = new HBitNot(value);
|
| + break;
|
| + case Token::SUB:
|
| + instr = new HMul(graph_->GetConstantMinus1(), value);
|
| + break;
|
| + default:
|
| + UNREACHABLE();
|
| + break;
|
| + }
|
| + PushAndAdd(instr);
|
| + } else if (op == Token::TYPEOF) {
|
| + VISIT_FOR_VALUE(expr->expression());
|
| + HValue* value = Pop();
|
| + PushAndAdd(new HTypeof(value));
|
| + } else {
|
| + BAILOUT("Value: unsupported unary operation");
|
| + }
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::VisitIncrementOperation(IncrementOperation* expr) {
|
| + // IncrementOperation is never visited by the visitor. It only
|
| + // occurs as a subexpression of CountOperation.
|
| + UNREACHABLE();
|
| +}
|
| +
|
| +
|
| +HInstruction* HGraphBuilder::BuildIncrement(HValue* value, bool increment) {
|
| + HConstant* delta = increment
|
| + ? graph_->GetConstant1()
|
| + : graph_->GetConstantMinus1();
|
| + HInstruction* instr = new HAdd(value, delta);
|
| + AssumeRepresentation(instr, Representation::Integer32());
|
| + return instr;
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::VisitCountOperation(CountOperation* expr) {
|
| + IncrementOperation* increment = expr->increment();
|
| + Expression* target = increment->expression();
|
| + VariableProxy* proxy = target->AsVariableProxy();
|
| + Variable* var = proxy->AsVariable();
|
| + Property* prop = target->AsProperty();
|
| + ASSERT(var == NULL || prop == NULL);
|
| + bool inc = expr->op() == Token::INC;
|
| +
|
| + if (var != NULL) {
|
| + if (!var->is_global() && !var->IsStackAllocated()) {
|
| + BAILOUT("non-stack/non-global variable in count operation");
|
| + }
|
| +
|
| + VISIT_FOR_VALUE(target);
|
| +
|
| + HValue* value = Pop();
|
| + HInstruction* instr = BuildIncrement(value, inc);
|
| + AddInstruction(instr);
|
| +
|
| + if (expr->is_prefix()) {
|
| + Push(instr);
|
| + } else {
|
| + Push(value);
|
| + }
|
| +
|
| + if (var->is_global()) {
|
| + HandleGlobalVariableAssignment(proxy, instr, expr->position());
|
| + } else {
|
| + ASSERT(var->IsStackAllocated());
|
| + Bind(var, instr);
|
| + }
|
| +
|
| + } else if (prop != NULL) {
|
| + prop->RecordTypeFeedback(oracle());
|
| +
|
| + if (prop->key()->IsPropertyName()) {
|
| + // Named property.
|
| +
|
| + // Match the full code generator stack by simulate an extra stack element
|
| + // for postfix operations in a value context.
|
| + if (expr->is_postfix() && !ast_context()->IsEffect()) {
|
| + Push(graph_->GetConstantUndefined());
|
| + }
|
| +
|
| + VISIT_FOR_VALUE(prop->obj());
|
| + HValue* obj = Top();
|
| +
|
| + HInstruction* load = NULL;
|
| + if (prop->IsMonomorphic()) {
|
| + Handle<String> name = prop->key()->AsLiteral()->AsPropertyName();
|
| + Handle<Map> map = prop->GetReceiverTypes()->first();
|
| + load = BuildLoadNamed(obj, prop, map, name);
|
| + } else {
|
| + load = BuildLoadNamedGeneric(obj, prop);
|
| + }
|
| + PushAndAdd(load);
|
| + if (load->HasSideEffects()) AddSimulate(increment->id());
|
| +
|
| + HValue* value = Pop();
|
| +
|
| + HInstruction* instr = BuildIncrement(value, inc);
|
| + AddInstruction(instr);
|
| +
|
| + HInstruction* store = BuildStoreNamed(obj, instr, prop);
|
| + AddInstruction(store);
|
| +
|
| + // Drop simulated receiver and push the result.
|
| + // There is no deoptimization to after the increment, so we can simulate
|
| + // the expression stack here.
|
| + Drop(1);
|
| + if (expr->is_prefix()) {
|
| + Push(instr);
|
| + } else {
|
| + if (!ast_context()->IsEffect()) Drop(1); // Drop simulated zero.
|
| + Push(value);
|
| + }
|
| +
|
| + } else {
|
| + // Keyed property.
|
| +
|
| + // Match the full code generator stack by simulate an extra stack element
|
| + // for postfix operations in a value context.
|
| + if (expr->is_postfix() && !ast_context()->IsEffect()) {
|
| + Push(graph_->GetConstantUndefined());
|
| + }
|
| +
|
| + VISIT_FOR_VALUE(prop->obj());
|
| + VISIT_FOR_VALUE(prop->key());
|
| +
|
| + HValue* obj = environment()->ExpressionStackAt(1);
|
| + HValue* key = environment()->ExpressionStackAt(0);
|
| +
|
| + bool is_fast_elements = prop->IsMonomorphic() &&
|
| + prop->GetMonomorphicReceiverType()->has_fast_elements();
|
| +
|
| + HInstruction* load = is_fast_elements
|
| + ? BuildLoadKeyedFastElement(obj, key, prop)
|
| + : BuildLoadKeyedGeneric(obj, key);
|
| + PushAndAdd(load);
|
| + if (load->HasSideEffects()) AddSimulate(increment->id());
|
| +
|
| + HValue* value = Pop();
|
| +
|
| + HInstruction* instr = BuildIncrement(value, inc);
|
| + AddInstruction(instr);
|
| +
|
| + HInstruction* store = is_fast_elements
|
| + ? BuildStoreKeyedFastElement(obj, key, instr, prop)
|
| + : new HStoreKeyedGeneric(obj, key, instr);
|
| + AddInstruction(store);
|
| +
|
| + // Drop simulated receiver and key and push the result.
|
| + // There is no deoptimization to after the increment, so we can simulate
|
| + // the expression stack here.
|
| + Drop(2);
|
| + if (expr->is_prefix()) {
|
| + Push(instr);
|
| + } else {
|
| + if (!ast_context()->IsEffect()) Drop(1); // Drop simulated zero.
|
| + Push(value);
|
| + }
|
| + }
|
| + } else {
|
| + BAILOUT("invalid lhs in count operation");
|
| + }
|
| +}
|
| +
|
| +
|
| +HInstruction* HGraphBuilder::BuildBinaryOperation(BinaryOperation* expr,
|
| + HValue* left,
|
| + HValue* right) {
|
| + HInstruction* instr = NULL;
|
| + switch (expr->op()) {
|
| + case Token::ADD:
|
| + instr = new HAdd(left, right);
|
| + break;
|
| + case Token::SUB:
|
| + instr = new HSub(left, right);
|
| + break;
|
| + case Token::MUL:
|
| + instr = new HMul(left, right);
|
| + break;
|
| + case Token::MOD:
|
| + instr = new HMod(left, right);
|
| + break;
|
| + case Token::DIV:
|
| + instr = new HDiv(left, right);
|
| + break;
|
| + case Token::BIT_XOR:
|
| + instr = new HBitXor(left, right);
|
| + break;
|
| + case Token::BIT_AND:
|
| + instr = new HBitAnd(left, right);
|
| + break;
|
| + case Token::BIT_OR:
|
| + instr = new HBitOr(left, right);
|
| + break;
|
| + case Token::SAR:
|
| + instr = new HSar(left, right);
|
| + break;
|
| + case Token::SHR:
|
| + instr = new HShr(left, right);
|
| + break;
|
| + case Token::SHL:
|
| + instr = new HShl(left, right);
|
| + break;
|
| + default:
|
| + UNREACHABLE();
|
| + }
|
| + TypeInfo info = oracle()->BinaryType(expr, TypeFeedbackOracle::RESULT);
|
| + // If we hit an uninitialized binary op stub we will get type info
|
| + // for a smi operation. If one of the operands is a constant string
|
| + // do not generate code assuming it is a smi operation.
|
| + if (info.IsSmi() &&
|
| + ((left->IsConstant() && HConstant::cast(left)->HasStringValue()) ||
|
| + (right->IsConstant() && HConstant::cast(right)->HasStringValue()))) {
|
| + return instr;
|
| + }
|
| + if (FLAG_trace_representation) {
|
| + PrintF("Info: %s/%s\n", info.ToString(), ToRepresentation(info).Mnemonic());
|
| + }
|
| + AssumeRepresentation(instr, ToRepresentation(info));
|
| + return instr;
|
| +}
|
| +
|
| +
|
| +// Check for the form (%_ClassOf(foo) === 'BarClass').
|
| +static bool IsClassOfTest(CompareOperation* expr) {
|
| + if (expr->op() != Token::EQ_STRICT) return false;
|
| + CallRuntime* call = expr->left()->AsCallRuntime();
|
| + if (call == NULL) return false;
|
| + Literal* literal = expr->right()->AsLiteral();
|
| + if (literal == NULL) return false;
|
| + if (!literal->handle()->IsString()) return false;
|
| + if (!call->name()->IsEqualTo(CStrVector("_ClassOf"))) return false;
|
| + ASSERT(call->arguments()->length() == 1);
|
| + return true;
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::VisitBinaryOperation(BinaryOperation* expr) {
|
| + if (expr->op() == Token::COMMA) {
|
| + VISIT_FOR_EFFECT(expr->left());
|
| + VISIT_FOR_VALUE(expr->right());
|
| + } else if (expr->op() == Token::AND || expr->op() == Token::OR) {
|
| + VISIT_FOR_VALUE(expr->left());
|
| + ASSERT(current_subgraph_->HasExit());
|
| +
|
| + HValue* left = Top();
|
| + bool is_logical_and = (expr->op() == Token::AND);
|
| +
|
| + HEnvironment* environment_copy = environment()->Copy();
|
| + environment_copy->Pop();
|
| + HSubgraph* right_subgraph;
|
| + right_subgraph = CreateBranchSubgraph(environment_copy);
|
| + ADD_TO_SUBGRAPH(right_subgraph, expr->right());
|
| + current_subgraph_->AppendOptional(right_subgraph, is_logical_and, left);
|
| + current_subgraph_->exit_block()->SetJoinId(expr->id());
|
| + } else {
|
| + VISIT_FOR_VALUE(expr->left());
|
| + VISIT_FOR_VALUE(expr->right());
|
| +
|
| + HValue* right = Pop();
|
| + HValue* left = Pop();
|
| + HInstruction* instr = BuildBinaryOperation(expr, left, right);
|
| + PushAndAdd(instr, expr->position());
|
| + }
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::AssumeRepresentation(HValue* value, Representation r) {
|
| + if (value->CheckFlag(HValue::kFlexibleRepresentation)) {
|
| + if (FLAG_trace_representation) {
|
| + PrintF("Assume representation for %s to be %s (%d)\n",
|
| + value->Mnemonic(),
|
| + r.Mnemonic(),
|
| + graph_->GetMaximumValueID());
|
| + }
|
| + value->ChangeRepresentation(r);
|
| + // The representation of the value is dictated by type feedback.
|
| + value->ClearFlag(HValue::kFlexibleRepresentation);
|
| + } else if (FLAG_trace_representation) {
|
| + PrintF("No representation assumed\n");
|
| + }
|
| +}
|
| +
|
| +
|
| +Representation HGraphBuilder::ToRepresentation(TypeInfo info) {
|
| + if (info.IsSmi()) return Representation::Integer32();
|
| + if (info.IsInteger32()) return Representation::Integer32();
|
| + if (info.IsDouble()) return Representation::Double();
|
| + if (info.IsNumber()) return Representation::Double();
|
| + return Representation::Tagged();
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::VisitCompareOperation(CompareOperation* expr) {
|
| + if (IsClassOfTest(expr)) {
|
| + CallRuntime* call = expr->left()->AsCallRuntime();
|
| + VISIT_FOR_VALUE(call->arguments()->at(0));
|
| + HValue* value = Pop();
|
| + Literal* literal = expr->right()->AsLiteral();
|
| + Handle<String> rhs = Handle<String>::cast(literal->handle());
|
| + HInstruction* instr = new HClassOfTest(value, rhs);
|
| + PushAndAdd(instr, expr->position());
|
| + return;
|
| + }
|
| +
|
| + // Check for the pattern: typeof <expression> == <string literal>.
|
| + UnaryOperation* left_unary = expr->left()->AsUnaryOperation();
|
| + Literal* right_literal = expr->right()->AsLiteral();
|
| + if ((expr->op() == Token::EQ || expr->op() == Token::EQ_STRICT) &&
|
| + left_unary != NULL && left_unary->op() == Token::TYPEOF &&
|
| + right_literal != NULL && right_literal->handle()->IsString()) {
|
| + VISIT_FOR_VALUE(left_unary->expression());
|
| + HValue* left = Pop();
|
| + HInstruction* instr = new HTypeofIs(left,
|
| + Handle<String>::cast(right_literal->handle()));
|
| + PushAndAdd(instr, expr->position());
|
| + return;
|
| + }
|
| +
|
| + VISIT_FOR_VALUE(expr->left());
|
| + VISIT_FOR_VALUE(expr->right());
|
| +
|
| + HValue* right = Pop();
|
| + HValue* left = Pop();
|
| + Token::Value op = expr->op();
|
| +
|
| + TypeInfo info = oracle()->CompareType(expr, TypeFeedbackOracle::RESULT);
|
| + HInstruction* instr = NULL;
|
| + if (op == Token::INSTANCEOF) {
|
| + instr = new HInstanceOf(left, right);
|
| + } else if (op == Token::IN) {
|
| + BAILOUT("Unsupported comparison: in");
|
| + } else if (info.IsNonPrimitive()) {
|
| + switch (op) {
|
| + case Token::EQ:
|
| + case Token::EQ_STRICT: {
|
| + AddInstruction(HCheckInstanceType::NewIsJSObjectOrJSFunction(left));
|
| + AddInstruction(HCheckInstanceType::NewIsJSObjectOrJSFunction(right));
|
| + instr = new HCompareJSObjectEq(left, right);
|
| + break;
|
| + }
|
| + default:
|
| + BAILOUT("Unsupported non-primitive compare");
|
| + break;
|
| + }
|
| + } else {
|
| + HCompare* compare = new HCompare(left, right, op);
|
| + Representation r = ToRepresentation(info);
|
| + compare->SetInputRepresentation(r);
|
| + instr = compare;
|
| + }
|
| + PushAndAdd(instr, expr->position());
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::VisitCompareToNull(CompareToNull* expr) {
|
| + VISIT_FOR_VALUE(expr->expression());
|
| +
|
| + HValue* value = Pop();
|
| + HIsNull* compare = new HIsNull(value, expr->is_strict());
|
| +
|
| + PushAndAdd(compare);
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::VisitThisFunction(ThisFunction* expr) {
|
| + BAILOUT("ThisFunction");
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::VisitDeclaration(Declaration* decl) {
|
| + // We allow only declarations that do not require code generation.
|
| + // The following all require code generation: global variables and
|
| + // functions, variables with slot type LOOKUP, declarations with
|
| + // mode CONST, and functions.
|
| + Variable* var = decl->proxy()->var();
|
| + Slot* slot = var->AsSlot();
|
| + if (var->is_global() ||
|
| + (slot != NULL && slot->type() == Slot::LOOKUP) ||
|
| + decl->mode() == Variable::CONST ||
|
| + decl->fun() != NULL) {
|
| + BAILOUT("unsupported declaration");
|
| + }
|
| +}
|
| +
|
| +
|
| +// Generators for inline runtime functions.
|
| +// Support for types.
|
| +void HGraphBuilder::GenerateIsSmi(int argument_count) {
|
| + ASSERT(argument_count == 1);
|
| +
|
| + HValue* value = Pop();
|
| + PushAndAdd(new HIsSmi(value));
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::GenerateIsSpecObject(int argument_count) {
|
| + ASSERT(argument_count == 1);
|
| +
|
| + HValue* value = Pop();
|
| + HHasInstanceType* test =
|
| + new HHasInstanceType(value, FIRST_JS_OBJECT_TYPE, LAST_TYPE);
|
| + PushAndAdd(test);
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::GenerateIsFunction(int argument_count) {
|
| + ASSERT(argument_count == 1);
|
| +
|
| + HValue* value = Pop();
|
| + HHasInstanceType* test =
|
| + new HHasInstanceType(value, JS_FUNCTION_TYPE);
|
| + PushAndAdd(test);
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::GenerateHasCachedArrayIndex(int argument_count) {
|
| + ASSERT(argument_count == 1);
|
| +
|
| + HValue* value = Pop();
|
| + HHasCachedArrayIndex* spec_test = new HHasCachedArrayIndex(value);
|
| + PushAndAdd(spec_test);
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::GenerateIsArray(int argument_count) {
|
| + ASSERT(argument_count == 1);
|
| +
|
| + HValue* value = Pop();
|
| + HHasInstanceType* test =
|
| + new HHasInstanceType(value, JS_ARRAY_TYPE);
|
| + PushAndAdd(test);
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::GenerateIsRegExp(int argument_count) {
|
| + ASSERT(argument_count == 1);
|
| +
|
| + HValue* value = Pop();
|
| + HHasInstanceType* test =
|
| + new HHasInstanceType(value, JS_REGEXP_TYPE);
|
| + PushAndAdd(test);
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::GenerateIsNonNegativeSmi(int argument_count) {
|
| + BAILOUT("inlined runtime function: IsNonNegativeSmi");
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::GenerateIsObject(int argument_count) {
|
| + BAILOUT("inlined runtime function: IsObject");
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::GenerateIsUndetectableObject(int argument_count) {
|
| + BAILOUT("inlined runtime function: IsUndetectableObject");
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::GenerateIsStringWrapperSafeForDefaultValueOf(
|
| + int argument_count) {
|
| + BAILOUT("inlined runtime function: IsStringWrapperSafeForDefaultValueOf");
|
| +}
|
| +
|
| +
|
| + // Support for construct call checks.
|
| +void HGraphBuilder::GenerateIsConstructCall(int argument_count) {
|
| + BAILOUT("inlined runtime function: IsConstructCall");
|
| +}
|
| +
|
| +
|
| +// Support for arguments.length and arguments[?].
|
| +void HGraphBuilder::GenerateArgumentsLength(int argument_count) {
|
| + ASSERT(argument_count == 0);
|
| + HInstruction* elements = AddInstruction(new HArgumentsElements);
|
| + PushAndAdd(new HArgumentsLength(elements));
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::GenerateArguments(int argument_count) {
|
| + ASSERT(argument_count == 1);
|
| + HValue* index = Pop();
|
| + HInstruction* elements = AddInstruction(new HArgumentsElements);
|
| + HInstruction* length = AddInstruction(new HArgumentsLength(elements));
|
| + PushAndAdd(new HAccessArgumentsAt(elements, length, index));
|
| +}
|
| +
|
| +
|
| +// Support for accessing the class and value fields of an object.
|
| +void HGraphBuilder::GenerateClassOf(int argument_count) {
|
| + // The special form detected by IsClassOfTest is detected before we get here
|
| + // and does not cause a bailout.
|
| + BAILOUT("inlined runtime function: ClassOf");
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::GenerateValueOf(int argument_count) {
|
| + ASSERT(argument_count == 1);
|
| +
|
| + HValue* value = Pop();
|
| + HValueOf* op = new HValueOf(value);
|
| + PushAndAdd(op);
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::GenerateSetValueOf(int argument_count) {
|
| + BAILOUT("inlined runtime function: SetValueOf");
|
| +}
|
| +
|
| +
|
| +// Fast support for charCodeAt(n).
|
| +void HGraphBuilder::GenerateStringCharCodeAt(int argument_count) {
|
| + BAILOUT("inlined runtime function: StringCharCodeAt");
|
| +}
|
| +
|
| +
|
| +// Fast support for string.charAt(n) and string[n].
|
| +void HGraphBuilder::GenerateStringCharFromCode(int argument_count) {
|
| + BAILOUT("inlined runtime function: StringCharFromCode");
|
| +}
|
| +
|
| +
|
| +// Fast support for string.charAt(n) and string[n].
|
| +void HGraphBuilder::GenerateStringCharAt(int argument_count) {
|
| + ASSERT_EQ(2, argument_count);
|
| + PushArgumentsForStubCall(argument_count);
|
| + PushAndAdd(new HCallStub(CodeStub::StringCharAt, argument_count),
|
| + RelocInfo::kNoPosition);
|
| +}
|
| +
|
| +
|
| +// Fast support for object equality testing.
|
| +void HGraphBuilder::GenerateObjectEquals(int argument_count) {
|
| + ASSERT(argument_count == 2);
|
| +
|
| + HValue* right = Pop();
|
| + HValue* left = Pop();
|
| + PushAndAdd(new HCompareJSObjectEq(left, right));
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::GenerateLog(int argument_count) {
|
| + UNREACHABLE(); // We caught this in VisitCallRuntime.
|
| +}
|
| +
|
| +
|
| +// Fast support for Math.random().
|
| +void HGraphBuilder::GenerateRandomHeapNumber(int argument_count) {
|
| + BAILOUT("inlined runtime function: RandomHeapNumber");
|
| +}
|
| +
|
| +
|
| +// Fast support for StringAdd.
|
| +void HGraphBuilder::GenerateStringAdd(int argument_count) {
|
| + ASSERT_EQ(2, argument_count);
|
| + PushArgumentsForStubCall(argument_count);
|
| + PushAndAdd(new HCallStub(CodeStub::StringAdd, argument_count),
|
| + RelocInfo::kNoPosition);
|
| +}
|
| +
|
| +
|
| +// Fast support for SubString.
|
| +void HGraphBuilder::GenerateSubString(int argument_count) {
|
| + ASSERT_EQ(3, argument_count);
|
| + PushArgumentsForStubCall(argument_count);
|
| + PushAndAdd(new HCallStub(CodeStub::SubString, argument_count),
|
| + RelocInfo::kNoPosition);
|
| +}
|
| +
|
| +
|
| +// Fast support for StringCompare.
|
| +void HGraphBuilder::GenerateStringCompare(int argument_count) {
|
| + ASSERT_EQ(2, argument_count);
|
| + PushArgumentsForStubCall(argument_count);
|
| + PushAndAdd(new HCallStub(CodeStub::StringCompare, argument_count),
|
| + RelocInfo::kNoPosition);
|
| +}
|
| +
|
| +
|
| +// Support for direct calls from JavaScript to native RegExp code.
|
| +void HGraphBuilder::GenerateRegExpExec(int argument_count) {
|
| + ASSERT_EQ(4, argument_count);
|
| + PushArgumentsForStubCall(argument_count);
|
| + PushAndAdd(new HCallStub(CodeStub::RegExpExec, argument_count),
|
| + RelocInfo::kNoPosition);
|
| +}
|
| +
|
| +
|
| +// Construct a RegExp exec result with two in-object properties.
|
| +void HGraphBuilder::GenerateRegExpConstructResult(int argument_count) {
|
| + ASSERT_EQ(3, argument_count);
|
| + PushArgumentsForStubCall(argument_count);
|
| + PushAndAdd(new HCallStub(CodeStub::RegExpConstructResult, argument_count),
|
| + RelocInfo::kNoPosition);
|
| +}
|
| +
|
| +
|
| +// Support for fast native caches.
|
| +void HGraphBuilder::GenerateGetFromCache(int argument_count) {
|
| + BAILOUT("inlined runtime function: GetFromCache");
|
| +}
|
| +
|
| +
|
| +// Fast support for number to string.
|
| +void HGraphBuilder::GenerateNumberToString(int argument_count) {
|
| + ASSERT_EQ(1, argument_count);
|
| + PushArgumentsForStubCall(argument_count);
|
| + PushAndAdd(new HCallStub(CodeStub::NumberToString, argument_count),
|
| + RelocInfo::kNoPosition);
|
| +}
|
| +
|
| +
|
| +// Fast swapping of elements. Takes three expressions, the object and two
|
| +// indices. This should only be used if the indices are known to be
|
| +// non-negative and within bounds of the elements array at the call site.
|
| +void HGraphBuilder::GenerateSwapElements(int argument_count) {
|
| + BAILOUT("inlined runtime function: SwapElements");
|
| +}
|
| +
|
| +
|
| +// Fast call for custom callbacks.
|
| +void HGraphBuilder::GenerateCallFunction(int argument_count) {
|
| + BAILOUT("inlined runtime function: CallFunction");
|
| +}
|
| +
|
| +
|
| +// Fast call to math functions.
|
| +void HGraphBuilder::GenerateMathPow(int argument_count) {
|
| + ASSERT_EQ(2, argument_count);
|
| + PushArgumentsForStubCall(argument_count);
|
| + PushAndAdd(new HCallStub(CodeStub::MathPow, argument_count),
|
| + RelocInfo::kNoPosition);
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::GenerateMathSin(int argument_count) {
|
| + ASSERT_EQ(1, argument_count);
|
| + PushArgumentsForStubCall(argument_count);
|
| + HCallStub* instr =
|
| + new HCallStub(CodeStub::TranscendentalCache, argument_count);
|
| + instr->set_transcendental_type(TranscendentalCache::SIN);
|
| + PushAndAdd(instr, RelocInfo::kNoPosition);
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::GenerateMathCos(int argument_count) {
|
| + ASSERT_EQ(1, argument_count);
|
| + PushArgumentsForStubCall(argument_count);
|
| + HCallStub* instr =
|
| + new HCallStub(CodeStub::TranscendentalCache, argument_count);
|
| + instr->set_transcendental_type(TranscendentalCache::COS);
|
| + PushAndAdd(instr, RelocInfo::kNoPosition);
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::GenerateMathLog(int argument_count) {
|
| + ASSERT_EQ(1, argument_count);
|
| + PushArgumentsForStubCall(argument_count);
|
| + HCallStub* instr =
|
| + new HCallStub(CodeStub::TranscendentalCache, argument_count);
|
| + instr->set_transcendental_type(TranscendentalCache::LOG);
|
| + PushAndAdd(instr, RelocInfo::kNoPosition);
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::GenerateMathSqrt(int argument_count) {
|
| + BAILOUT("inlined runtime function: MathSqrt");
|
| +}
|
| +
|
| +
|
| +// Check whether two RegExps are equivalent
|
| +void HGraphBuilder::GenerateIsRegExpEquivalent(int argument_count) {
|
| + BAILOUT("inlined runtime function: IsRegExpEquivalent");
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::GenerateGetCachedArrayIndex(int argument_count) {
|
| + BAILOUT("inlined runtime function: GetCachedArrayIndex");
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::GenerateFastAsciiArrayJoin(int argument_count) {
|
| + BAILOUT("inlined runtime function: FastAsciiArrayJoin");
|
| +}
|
| +
|
| +
|
| +#undef BAILOUT
|
| +#undef CHECK_BAILOUT
|
| +#undef VISIT_FOR_EFFECT
|
| +#undef VISIT_FOR_VALUE
|
| +#undef ADD_TO_SUBGRAPH
|
| +
|
| +
|
| +HEnvironment::HEnvironment(HEnvironment* outer,
|
| + Scope* scope,
|
| + Handle<JSFunction> closure)
|
| + : closure_(closure),
|
| + values_(0),
|
| + assigned_variables_(4),
|
| + parameter_count_(0),
|
| + local_count_(0),
|
| + outer_(outer),
|
| + pop_count_(0),
|
| + push_count_(0),
|
| + ast_id_(AstNode::kNoNumber) {
|
| + Initialize(scope->num_parameters() + 1, scope->num_stack_slots(), 0);
|
| +}
|
| +
|
| +
|
| +HEnvironment::HEnvironment(const HEnvironment* other)
|
| + : values_(0),
|
| + assigned_variables_(0),
|
| + parameter_count_(0),
|
| + local_count_(0),
|
| + outer_(NULL),
|
| + pop_count_(0),
|
| + push_count_(0),
|
| + ast_id_(other->ast_id()) {
|
| + Initialize(other);
|
| +}
|
| +
|
| +
|
| +void HEnvironment::Initialize(int parameter_count,
|
| + int local_count,
|
| + int stack_height) {
|
| + parameter_count_ = parameter_count;
|
| + local_count_ = local_count;
|
| +
|
| + // Avoid reallocating the temporaries' backing store on the first Push.
|
| + int total = parameter_count + local_count + stack_height;
|
| + values_.Initialize(total + 4);
|
| + for (int i = 0; i < total; ++i) values_.Add(NULL);
|
| +}
|
| +
|
| +
|
| +void HEnvironment::AddIncomingEdge(HBasicBlock* block, HEnvironment* other) {
|
| + ASSERT(!block->IsLoopHeader());
|
| + ASSERT(values_.length() == other->values_.length());
|
| +
|
| + int length = values_.length();
|
| + for (int i = 0; i < length; ++i) {
|
| + HValue* value = values_[i];
|
| + if (value != NULL && value->IsPhi() && value->block() == block) {
|
| + // There is already a phi for the i'th value.
|
| + HPhi* phi = HPhi::cast(value);
|
| + // Assert index is correct and that we haven't missed an incoming edge.
|
| + ASSERT(phi->merged_index() == i);
|
| + ASSERT(phi->OperandCount() == block->predecessors()->length());
|
| + phi->AddInput(other->values_[i]);
|
| + } else if (values_[i] != other->values_[i]) {
|
| + // There is a fresh value on the incoming edge, a phi is needed.
|
| + ASSERT(values_[i] != NULL && other->values_[i] != NULL);
|
| + HPhi* phi = new HPhi(i);
|
| + HValue* old_value = values_[i];
|
| + for (int j = 0; j < block->predecessors()->length(); j++) {
|
| + phi->AddInput(old_value);
|
| + }
|
| + phi->AddInput(other->values_[i]);
|
| + this->values_[i] = phi;
|
| + block->AddPhi(phi);
|
| + }
|
| + }
|
| +}
|
| +
|
| +
|
| +void HEnvironment::Initialize(const HEnvironment* other) {
|
| + closure_ = other->closure();
|
| + values_.AddAll(other->values_);
|
| + assigned_variables_.AddAll(other->assigned_variables_);
|
| + parameter_count_ = other->parameter_count_;
|
| + local_count_ = other->local_count_;
|
| + if (other->outer_ != NULL) outer_ = other->outer_->Copy(); // Deep copy.
|
| + pop_count_ = other->pop_count_;
|
| + push_count_ = other->push_count_;
|
| + ast_id_ = other->ast_id_;
|
| +}
|
| +
|
| +
|
| +int HEnvironment::IndexFor(Variable* variable) const {
|
| + Slot* slot = variable->AsSlot();
|
| + ASSERT(slot != NULL && slot->IsStackAllocated());
|
| + if (slot->type() == Slot::PARAMETER) {
|
| + return slot->index() + 1;
|
| + } else {
|
| + return parameter_count_ + slot->index();
|
| + }
|
| +}
|
| +
|
| +
|
| +HEnvironment* HEnvironment::Copy() const {
|
| + return new HEnvironment(this);
|
| +}
|
| +
|
| +
|
| +HEnvironment* HEnvironment::CopyWithoutHistory() const {
|
| + HEnvironment* result = Copy();
|
| + result->ClearHistory();
|
| + return result;
|
| +}
|
| +
|
| +
|
| +HEnvironment* HEnvironment::CopyAsLoopHeader(HBasicBlock* loop_header) const {
|
| + HEnvironment* new_env = Copy();
|
| + for (int i = 0; i < values_.length(); ++i) {
|
| + HPhi* phi = new HPhi(i);
|
| + phi->AddInput(values_[i]);
|
| + new_env->values_[i] = phi;
|
| + loop_header->AddPhi(phi);
|
| + }
|
| + new_env->ClearHistory();
|
| + return new_env;
|
| +}
|
| +
|
| +
|
| +HEnvironment* HEnvironment::CopyForInlining(Handle<JSFunction> target,
|
| + FunctionLiteral* function,
|
| + bool is_speculative,
|
| + HConstant* undefined) const {
|
| + // Outer environment is a copy of this one without the arguments.
|
| + int arity = function->scope()->num_parameters();
|
| + HEnvironment* outer = Copy();
|
| + outer->Drop(arity + 1); // Including receiver.
|
| + outer->ClearHistory();
|
| + HEnvironment* inner = new HEnvironment(outer, function->scope(), target);
|
| + // Get the argument values from the original environment.
|
| + if (is_speculative) {
|
| + for (int i = 0; i <= arity; ++i) { // Include receiver.
|
| + HValue* push = ExpressionStackAt(arity - i);
|
| + inner->SetValueAt(i, push);
|
| + }
|
| + } else {
|
| + for (int i = 0; i <= arity; ++i) { // Include receiver.
|
| + inner->SetValueAt(i, ExpressionStackAt(arity - i));
|
| + }
|
| + }
|
| +
|
| + // Initialize the stack-allocated locals to undefined.
|
| + int local_base = arity + 1;
|
| + int local_count = function->scope()->num_stack_slots();
|
| + for (int i = 0; i < local_count; ++i) {
|
| + inner->SetValueAt(local_base + i, undefined);
|
| + }
|
| +
|
| + inner->set_ast_id(function->id());
|
| + return inner;
|
| +}
|
| +
|
| +
|
| +void HEnvironment::PrintTo(StringStream* stream) {
|
| + for (int i = 0; i < total_count(); i++) {
|
| + if (i == 0) stream->Add("parameters\n");
|
| + if (i == parameter_count()) stream->Add("locals\n");
|
| + if (i == parameter_count() + local_count()) stream->Add("expressions");
|
| + HValue* val = values_.at(i);
|
| + stream->Add("%d: ", i);
|
| + if (val != NULL) {
|
| + val->PrintNameTo(stream);
|
| + } else {
|
| + stream->Add("NULL");
|
| + }
|
| + stream->Add("\n");
|
| + }
|
| +}
|
| +
|
| +
|
| +void HEnvironment::PrintToStd() {
|
| + HeapStringAllocator string_allocator;
|
| + StringStream trace(&string_allocator);
|
| + PrintTo(&trace);
|
| + PrintF("%s", *trace.ToCString());
|
| +}
|
| +
|
| +
|
| +void HTracer::TraceCompilation(FunctionLiteral* function) {
|
| + Tag tag(this, "compilation");
|
| + Handle<String> name = function->debug_name();
|
| + PrintStringProperty("name", *name->ToCString());
|
| + PrintStringProperty("method", *name->ToCString());
|
| + PrintLongProperty("date", static_cast<int64_t>(OS::TimeCurrentMillis()));
|
| +}
|
| +
|
| +
|
| +void HTracer::TraceLithium(const char* name, LChunk* chunk) {
|
| + Trace(name, chunk->graph(), chunk);
|
| +}
|
| +
|
| +
|
| +void HTracer::TraceHydrogen(const char* name, HGraph* graph) {
|
| + Trace(name, graph, NULL);
|
| +}
|
| +
|
| +
|
| +void HTracer::Trace(const char* name, HGraph* graph, LChunk* chunk) {
|
| + Tag tag(this, "cfg");
|
| + PrintStringProperty("name", name);
|
| + const ZoneList<HBasicBlock*>* blocks = graph->blocks();
|
| + for (int i = 0; i < blocks->length(); i++) {
|
| + HBasicBlock* current = blocks->at(i);
|
| + Tag block_tag(this, "block");
|
| + PrintBlockProperty("name", current->block_id());
|
| + PrintIntProperty("from_bci", -1);
|
| + PrintIntProperty("to_bci", -1);
|
| +
|
| + if (!current->predecessors()->is_empty()) {
|
| + PrintIndent();
|
| + trace_.Add("predecessors");
|
| + for (int j = 0; j < current->predecessors()->length(); ++j) {
|
| + trace_.Add(" \"B%d\"", current->predecessors()->at(j)->block_id());
|
| + }
|
| + trace_.Add("\n");
|
| + } else {
|
| + PrintEmptyProperty("predecessors");
|
| + }
|
| +
|
| + if (current->end() == NULL || current->end()->FirstSuccessor() == NULL) {
|
| + PrintEmptyProperty("successors");
|
| + } else if (current->end()->SecondSuccessor() == NULL) {
|
| + PrintBlockProperty("successors",
|
| + current->end()->FirstSuccessor()->block_id());
|
| + } else {
|
| + PrintBlockProperty("successors",
|
| + current->end()->FirstSuccessor()->block_id(),
|
| + current->end()->SecondSuccessor()->block_id());
|
| + }
|
| +
|
| + PrintEmptyProperty("xhandlers");
|
| + PrintEmptyProperty("flags");
|
| +
|
| + if (current->dominator() != NULL) {
|
| + PrintBlockProperty("dominator", current->dominator()->block_id());
|
| + }
|
| +
|
| + if (chunk != NULL) {
|
| + int first_index = current->first_instruction_index();
|
| + int last_index = current->last_instruction_index();
|
| + PrintIntProperty(
|
| + "first_lir_id",
|
| + LifetimePosition::FromInstructionIndex(first_index).Value());
|
| + PrintIntProperty(
|
| + "last_lir_id",
|
| + LifetimePosition::FromInstructionIndex(last_index).Value());
|
| + }
|
| +
|
| + {
|
| + Tag states_tag(this, "states");
|
| + Tag locals_tag(this, "locals");
|
| + int total = current->phis()->length();
|
| + trace_.Add("size %d\n", total);
|
| + trace_.Add("method \"None\"");
|
| + for (int j = 0; j < total; ++j) {
|
| + HPhi* phi = current->phis()->at(j);
|
| + trace_.Add("%d ", phi->merged_index());
|
| + phi->PrintNameTo(&trace_);
|
| + trace_.Add(" ");
|
| + phi->PrintTo(&trace_);
|
| + trace_.Add("\n");
|
| + }
|
| + }
|
| +
|
| + {
|
| + Tag HIR_tag(this, "HIR");
|
| + HInstruction* instruction = current->first();
|
| + while (instruction != NULL) {
|
| + int bci = 0;
|
| + int uses = instruction->uses()->length();
|
| + trace_.Add("%d %d ", bci, uses);
|
| + instruction->PrintNameTo(&trace_);
|
| + trace_.Add(" ");
|
| + instruction->PrintTo(&trace_);
|
| + trace_.Add(" <|@\n");
|
| + instruction = instruction->next();
|
| + }
|
| + }
|
| +
|
| +
|
| + if (chunk != NULL) {
|
| + Tag LIR_tag(this, "LIR");
|
| + int first_index = current->first_instruction_index();
|
| + int last_index = current->last_instruction_index();
|
| + if (first_index != -1 && last_index != -1) {
|
| + const ZoneList<LInstruction*>* instructions = chunk->instructions();
|
| + for (int i = first_index; i <= last_index; ++i) {
|
| + LInstruction* linstr = instructions->at(i);
|
| + if (linstr != NULL) {
|
| + trace_.Add("%d ",
|
| + LifetimePosition::FromInstructionIndex(i).Value());
|
| + linstr->PrintTo(&trace_);
|
| + trace_.Add(" <|@\n");
|
| + }
|
| + }
|
| + }
|
| + }
|
| + }
|
| +}
|
| +
|
| +
|
| +void HTracer::TraceLiveRanges(const char* name, LAllocator* allocator) {
|
| + Tag tag(this, "intervals");
|
| + PrintStringProperty("name", name);
|
| +
|
| + const ZoneList<LiveRange*>* fixed_d = allocator->fixed_double_live_ranges();
|
| + for (int i = 0; i < fixed_d->length(); ++i) {
|
| + TraceLiveRange(fixed_d->at(i), "fixed");
|
| + }
|
| +
|
| + const ZoneList<LiveRange*>* fixed = allocator->fixed_live_ranges();
|
| + for (int i = 0; i < fixed->length(); ++i) {
|
| + TraceLiveRange(fixed->at(i), "fixed");
|
| + }
|
| +
|
| + const ZoneList<LiveRange*>* live_ranges = allocator->live_ranges();
|
| + for (int i = 0; i < live_ranges->length(); ++i) {
|
| + TraceLiveRange(live_ranges->at(i), "object");
|
| + }
|
| +}
|
| +
|
| +
|
| +void HTracer::TraceLiveRange(LiveRange* range, const char* type) {
|
| + if (range != NULL && !range->IsEmpty()) {
|
| + trace_.Add("%d %s", range->id(), type);
|
| + if (range->HasRegisterAssigned()) {
|
| + LOperand* op = range->CreateAssignedOperand();
|
| + int assigned_reg = op->index();
|
| + if (op->IsDoubleRegister()) {
|
| + trace_.Add(" \"%s\"",
|
| + DoubleRegister::AllocationIndexToString(assigned_reg));
|
| + } else {
|
| + ASSERT(op->IsRegister());
|
| + trace_.Add(" \"%s\"", Register::AllocationIndexToString(assigned_reg));
|
| + }
|
| + } else if (range->IsSpilled()) {
|
| + LOperand* op = range->TopLevel()->GetSpillOperand();
|
| + if (op->IsDoubleStackSlot()) {
|
| + trace_.Add(" \"double_stack:%d\"", op->index());
|
| + } else {
|
| + ASSERT(op->IsStackSlot());
|
| + trace_.Add(" \"stack:%d\"", op->index());
|
| + }
|
| + }
|
| + int parent_index = -1;
|
| + if (range->IsChild()) {
|
| + parent_index = range->parent()->id();
|
| + } else {
|
| + parent_index = range->id();
|
| + }
|
| + LOperand* op = range->FirstHint();
|
| + int hint_index = -1;
|
| + if (op != NULL && op->IsUnallocated()) hint_index = op->VirtualRegister();
|
| + trace_.Add(" %d %d", parent_index, hint_index);
|
| + UseInterval* cur_interval = range->first_interval();
|
| + while (cur_interval != NULL) {
|
| + trace_.Add(" [%d, %d[",
|
| + cur_interval->start().Value(),
|
| + cur_interval->end().Value());
|
| + cur_interval = cur_interval->next();
|
| + }
|
| +
|
| + UsePosition* current_pos = range->first_pos();
|
| + while (current_pos != NULL) {
|
| + if (current_pos->RegisterIsBeneficial()) {
|
| + trace_.Add(" %d M", current_pos->pos().Value());
|
| + }
|
| + current_pos = current_pos->next();
|
| + }
|
| +
|
| + trace_.Add(" \"\"\n");
|
| + }
|
| +}
|
| +
|
| +
|
| +void HTracer::FlushToFile() {
|
| + AppendChars(filename_, *trace_.ToCString(), trace_.length(), false);
|
| + trace_.Reset();
|
| +}
|
| +
|
| +
|
| +void HStatistics::Print() {
|
| + PrintF("Timing results:\n");
|
| + int64_t sum = 0;
|
| + for (int i = 0; i < timing_.length(); ++i) {
|
| + sum += timing_[i];
|
| + }
|
| +
|
| + for (int i = 0; i < names_.length(); ++i) {
|
| + PrintF("%30s", names_[i]);
|
| + double ms = static_cast<double>(timing_[i]) / 1000;
|
| + double percent = static_cast<double>(timing_[i]) * 100 / sum;
|
| + PrintF(" - %0.3f ms / %0.3f %% \n", ms, percent);
|
| + }
|
| + PrintF("%30s - %0.3f ms \n", "Sum", static_cast<double>(sum) / 1000);
|
| + PrintF("---------------------------------------------------------------\n");
|
| + PrintF("%30s - %0.3f ms (%0.1f times slower than full code gen)\n",
|
| + "Total",
|
| + static_cast<double>(total_) / 1000,
|
| + static_cast<double>(total_) / full_code_gen_);
|
| +}
|
| +
|
| +
|
| +void HStatistics::SaveTiming(const char* name, int64_t ticks) {
|
| + if (name == HPhase::kFullCodeGen) {
|
| + full_code_gen_ += ticks;
|
| + } else if (name == HPhase::kTotal) {
|
| + total_ += ticks;
|
| + } else {
|
| + for (int i = 0; i < names_.length(); ++i) {
|
| + if (names_[i] == name) {
|
| + timing_[i] += ticks;
|
| + return;
|
| + }
|
| + }
|
| + names_.Add(name);
|
| + timing_.Add(ticks);
|
| + }
|
| +}
|
| +
|
| +
|
| +const char* const HPhase::kFullCodeGen = "Full code generator";
|
| +const char* const HPhase::kTotal = "Total";
|
| +
|
| +
|
| +void HPhase::Begin(const char* name,
|
| + HGraph* graph,
|
| + LChunk* chunk,
|
| + LAllocator* allocator) {
|
| + name_ = name;
|
| + graph_ = graph;
|
| + chunk_ = chunk;
|
| + allocator_ = allocator;
|
| + if (allocator != NULL && chunk_ == NULL) {
|
| + chunk_ = allocator->chunk();
|
| + }
|
| + if (FLAG_time_hydrogen) start_ = OS::Ticks();
|
| +}
|
| +
|
| +
|
| +void HPhase::End() const {
|
| + if (FLAG_time_hydrogen) {
|
| + int64_t end = OS::Ticks();
|
| + HStatistics::Instance()->SaveTiming(name_, end - start_);
|
| + }
|
| +
|
| + if (FLAG_trace_hydrogen) {
|
| + if (graph_ != NULL) HTracer::Instance()->TraceHydrogen(name_, graph_);
|
| + if (chunk_ != NULL) HTracer::Instance()->TraceLithium(name_, chunk_);
|
| + if (allocator_ != NULL) {
|
| + HTracer::Instance()->TraceLiveRanges(name_, allocator_);
|
| + }
|
| + }
|
| +
|
| +#ifdef DEBUG
|
| + if (graph_ != NULL) graph_->Verify();
|
| + if (chunk_ != NULL) chunk_->Verify();
|
| + if (allocator_ != NULL) allocator_->Verify();
|
| +#endif
|
| +}
|
| +
|
| +} } // namespace v8::internal
|
|
|