| Index: src/hydrogen.cc
|
| ===================================================================
|
| --- src/hydrogen.cc (revision 8618)
|
| +++ src/hydrogen.cc (working copy)
|
| @@ -33,6 +33,7 @@
|
| #include "hashmap.h"
|
| #include "lithium-allocator.h"
|
| #include "parser.h"
|
| +#include "scopeinfo.h"
|
| #include "scopes.h"
|
| #include "stub-cache.h"
|
|
|
| @@ -68,8 +69,8 @@
|
| last_instruction_index_(-1),
|
| deleted_phis_(4),
|
| parent_loop_header_(NULL),
|
| - is_inline_return_target_(false) {
|
| -}
|
| + is_inline_return_target_(false),
|
| + is_deoptimizing_(false) { }
|
|
|
|
|
| void HBasicBlock::AttachLoopInformation() {
|
| @@ -131,16 +132,16 @@
|
| }
|
|
|
|
|
| -HSimulate* HBasicBlock::CreateSimulate(int id) {
|
| +HSimulate* HBasicBlock::CreateSimulate(int ast_id) {
|
| ASSERT(HasEnvironment());
|
| HEnvironment* environment = last_environment();
|
| - ASSERT(id == AstNode::kNoNumber ||
|
| - environment->closure()->shared()->VerifyBailoutId(id));
|
| + ASSERT(ast_id == AstNode::kNoNumber ||
|
| + environment->closure()->shared()->VerifyBailoutId(ast_id));
|
|
|
| int push_count = environment->push_count();
|
| int pop_count = environment->pop_count();
|
|
|
| - HSimulate* instr = new(zone()) HSimulate(id, pop_count);
|
| + HSimulate* instr = new(zone()) HSimulate(ast_id, pop_count);
|
| for (int i = push_count - 1; i >= 0; --i) {
|
| instr->AddPushedValue(environment->ExpressionStackAt(i));
|
| }
|
| @@ -157,23 +158,19 @@
|
| ASSERT(!IsFinished());
|
| AddInstruction(end);
|
| end_ = end;
|
| - if (end->FirstSuccessor() != NULL) {
|
| - end->FirstSuccessor()->RegisterPredecessor(this);
|
| - if (end->SecondSuccessor() != NULL) {
|
| - end->SecondSuccessor()->RegisterPredecessor(this);
|
| - }
|
| + for (HSuccessorIterator it(end); !it.Done(); it.Advance()) {
|
| + it.Current()->RegisterPredecessor(this);
|
| }
|
| }
|
|
|
|
|
| -void HBasicBlock::Goto(HBasicBlock* block, bool include_stack_check) {
|
| +void HBasicBlock::Goto(HBasicBlock* block) {
|
| if (block->IsInlineReturnTarget()) {
|
| AddInstruction(new(zone()) HLeaveInlined);
|
| last_environment_ = last_environment()->outer();
|
| }
|
| AddSimulate(AstNode::kNoNumber);
|
| HGoto* instr = new(zone()) HGoto(block);
|
| - instr->set_include_stack_check(include_stack_check);
|
| Finish(instr);
|
| }
|
|
|
| @@ -197,7 +194,7 @@
|
| }
|
|
|
|
|
| -void HBasicBlock::SetJoinId(int id) {
|
| +void HBasicBlock::SetJoinId(int ast_id) {
|
| int length = predecessors_.length();
|
| ASSERT(length > 0);
|
| for (int i = 0; i < length; i++) {
|
| @@ -207,8 +204,8 @@
|
| // We only need to verify the ID once.
|
| ASSERT(i != 0 ||
|
| predecessor->last_environment()->closure()->shared()
|
| - ->VerifyBailoutId(id));
|
| - simulate->set_ast_id(id);
|
| + ->VerifyBailoutId(ast_id));
|
| + simulate->set_ast_id(ast_id);
|
| }
|
| }
|
|
|
| @@ -401,8 +398,9 @@
|
| void Analyze() {
|
| while (!stack_.is_empty()) {
|
| HControlInstruction* end = stack_.RemoveLast()->end();
|
| - PushBlock(end->FirstSuccessor());
|
| - PushBlock(end->SecondSuccessor());
|
| + for (HSuccessorIterator it(end); !it.Done(); it.Advance()) {
|
| + PushBlock(it.Current());
|
| + }
|
| }
|
| }
|
|
|
| @@ -521,6 +519,12 @@
|
| return GetConstant(&constant_false_, isolate()->heap()->false_value());
|
| }
|
|
|
| +
|
| +HConstant* HGraph::GetConstantHole() {
|
| + return GetConstant(&constant_hole_, isolate()->heap()->the_hole_value());
|
| +}
|
| +
|
| +
|
| HGraphBuilder::HGraphBuilder(CompilationInfo* info,
|
| TypeFeedbackOracle* oracle)
|
| : function_state_(NULL),
|
| @@ -572,7 +576,7 @@
|
| HBasicBlock* body_exit,
|
| HBasicBlock* loop_successor,
|
| HBasicBlock* break_block) {
|
| - if (body_exit != NULL) body_exit->Goto(loop_entry, true);
|
| + if (body_exit != NULL) body_exit->Goto(loop_entry);
|
| loop_entry->PostProcessLoopHeader(statement);
|
| if (break_block != NULL) {
|
| if (loop_successor != NULL) loop_successor->Goto(break_block);
|
| @@ -691,8 +695,9 @@
|
| 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);
|
| + for (HSuccessorIterator it(b->end()); !it.Done(); it.Advance()) {
|
| + Postorder(it.Current(), visited, order, loop_header);
|
| + }
|
| if (b->IsLoopHeader() && b != loop->loop_header()) {
|
| PostorderLoopBlocks(b->loop_information(), visited, order, loop_header);
|
| }
|
| @@ -709,11 +714,13 @@
|
| 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);
|
| + for (HSuccessorIterator it(block->end()); !it.Done(); it.Advance()) {
|
| + Postorder(it.Current(), visited, order, block);
|
| + }
|
| } else {
|
| - Postorder(block->end()->SecondSuccessor(), visited, order, loop_header);
|
| - Postorder(block->end()->FirstSuccessor(), visited, order, loop_header);
|
| + for (HSuccessorIterator it(block->end()); !it.Done(); it.Advance()) {
|
| + Postorder(it.Current(), visited, order, loop_header);
|
| + }
|
| }
|
| ASSERT(block->end()->FirstSuccessor() == NULL ||
|
| order->Contains(block->end()->FirstSuccessor()) ||
|
| @@ -736,9 +743,21 @@
|
| }
|
| }
|
| }
|
| +
|
| + // Propagate flag marking blocks containing unconditional deoptimize.
|
| + MarkAsDeoptimizingRecursively(entry_block());
|
| }
|
|
|
|
|
| +// Mark all blocks that are dominated by an unconditional deoptimize.
|
| +void HGraph::MarkAsDeoptimizingRecursively(HBasicBlock* block) {
|
| + for (int i = 0; i < block->dominated_blocks()->length(); ++i) {
|
| + HBasicBlock* dominated = block->dominated_blocks()->at(i);
|
| + if (block->IsDeoptimizing()) dominated->MarkAsDeoptimizing();
|
| + MarkAsDeoptimizingRecursively(dominated);
|
| + }
|
| +}
|
| +
|
| void HGraph::EliminateRedundantPhis() {
|
| HPhase phase("Redundant phi elimination", this);
|
|
|
| @@ -826,6 +845,10 @@
|
| phi_list_->Add(phi);
|
| // We don't support phi uses of arguments for now.
|
| if (phi->CheckFlag(HValue::kIsArguments)) return false;
|
| + // Check for the hole value (from an uninitialized const).
|
| + for (int k = 0; k < phi->OperandCount(); k++) {
|
| + if (phi->OperandAt(k) == GetConstantHole()) return false;
|
| + }
|
| }
|
| }
|
| return true;
|
| @@ -864,9 +887,8 @@
|
| private:
|
| void TraceRange(const char* msg, ...);
|
| void Analyze(HBasicBlock* block);
|
| - void InferControlFlowRange(HTest* test, HBasicBlock* dest);
|
| - void InferControlFlowRange(Token::Value op, HValue* value, HValue* other);
|
| - void InferPhiRange(HPhi* phi);
|
| + void InferControlFlowRange(HCompareIDAndBranch* test, HBasicBlock* dest);
|
| + void UpdateControlFlowRange(Token::Value op, HValue* value, HValue* other);
|
| void InferRange(HValue* value);
|
| void RollBackTo(int index);
|
| void AddRange(HValue* value, Range* range);
|
| @@ -888,7 +910,7 @@
|
|
|
| void HRangeAnalysis::Analyze() {
|
| HPhase phase("Range analysis", graph_);
|
| - Analyze(graph_->blocks()->at(0));
|
| + Analyze(graph_->entry_block());
|
| }
|
|
|
|
|
| @@ -900,15 +922,15 @@
|
| // Infer range based on control flow.
|
| if (block->predecessors()->length() == 1) {
|
| HBasicBlock* pred = block->predecessors()->first();
|
| - if (pred->end()->IsTest()) {
|
| - InferControlFlowRange(HTest::cast(pred->end()), block);
|
| + if (pred->end()->IsCompareIDAndBranch()) {
|
| + InferControlFlowRange(HCompareIDAndBranch::cast(pred->end()), block);
|
| }
|
| }
|
|
|
| // Process phi instructions.
|
| for (int i = 0; i < block->phis()->length(); ++i) {
|
| HPhi* phi = block->phis()->at(i);
|
| - InferPhiRange(phi);
|
| + InferRange(phi);
|
| }
|
|
|
| // Go through all instructions of the current block.
|
| @@ -927,28 +949,26 @@
|
| }
|
|
|
|
|
| -void HRangeAnalysis::InferControlFlowRange(HTest* test, HBasicBlock* dest) {
|
| +void HRangeAnalysis::InferControlFlowRange(HCompareIDAndBranch* test,
|
| + HBasicBlock* dest) {
|
| ASSERT((test->FirstSuccessor() == dest) == (test->SecondSuccessor() != dest));
|
| - if (test->value()->IsCompare()) {
|
| - HCompare* compare = HCompare::cast(test->value());
|
| - if (compare->GetInputRepresentation().IsInteger32()) {
|
| - Token::Value op = compare->token();
|
| - if (test->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());
|
| + if (test->GetInputRepresentation().IsInteger32()) {
|
| + Token::Value op = test->token();
|
| + if (test->SecondSuccessor() == dest) {
|
| + op = Token::NegateCompareOp(op);
|
| }
|
| + Token::Value inverted_op = Token::InvertCompareOp(op);
|
| + UpdateControlFlowRange(op, test->left(), test->right());
|
| + UpdateControlFlowRange(inverted_op, test->right(), test->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) {
|
| +void HRangeAnalysis::UpdateControlFlowRange(Token::Value op,
|
| + HValue* value,
|
| + HValue* other) {
|
| Range temp_range;
|
| Range* range = other->range() != NULL ? other->range() : &temp_range;
|
| Range* new_range = NULL;
|
| @@ -979,12 +999,6 @@
|
| }
|
|
|
|
|
| -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()) {
|
| @@ -1211,8 +1225,6 @@
|
| void Process();
|
|
|
| private:
|
| - void RemoveStackCheck(HBasicBlock* block);
|
| -
|
| HGraph* graph_;
|
| };
|
|
|
| @@ -1227,16 +1239,20 @@
|
| if (block->IsLoopHeader()) {
|
| HBasicBlock* back_edge = block->loop_information()->GetLastBackEdge();
|
| HBasicBlock* dominator = back_edge;
|
| - bool back_edge_dominated_by_call = false;
|
| - while (dominator != block && !back_edge_dominated_by_call) {
|
| + while (true) {
|
| HInstruction* instr = dominator->first();
|
| - while (instr != NULL && !back_edge_dominated_by_call) {
|
| + while (instr != NULL) {
|
| if (instr->IsCall()) {
|
| - RemoveStackCheck(back_edge);
|
| - back_edge_dominated_by_call = true;
|
| + block->loop_information()->stack_check()->Eliminate();
|
| + break;
|
| }
|
| instr = instr->next();
|
| }
|
| +
|
| + // Done when the loop header is processed.
|
| + if (dominator == block) break;
|
| +
|
| + // Move up the dominator tree.
|
| dominator = dominator->dominator();
|
| }
|
| }
|
| @@ -1244,18 +1260,6 @@
|
| }
|
|
|
|
|
| -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();
|
| - }
|
| -}
|
| -
|
| -
|
| // Simple sparse set with O(1) add, contains, and clear.
|
| class SparseSet {
|
| public:
|
| @@ -1263,7 +1267,12 @@
|
| : capacity_(capacity),
|
| length_(0),
|
| dense_(zone->NewArray<int>(capacity)),
|
| - sparse_(zone->NewArray<int>(capacity)) {}
|
| + sparse_(zone->NewArray<int>(capacity)) {
|
| +#ifndef NVALGRIND
|
| + // Initialize the sparse array to make valgrind happy.
|
| + memset(sparse_, 0, sizeof(sparse_[0]) * capacity);
|
| +#endif
|
| + }
|
|
|
| bool Contains(int n) const {
|
| ASSERT(0 <= n && n < capacity_);
|
| @@ -1346,7 +1355,7 @@
|
| LoopInvariantCodeMotion();
|
| }
|
| HValueMap* map = new(zone()) HValueMap();
|
| - AnalyzeBlock(graph_->blocks()->at(0), map);
|
| + AnalyzeBlock(graph_->entry_block(), map);
|
| }
|
|
|
|
|
| @@ -1438,37 +1447,9 @@
|
|
|
| bool HGlobalValueNumberer::ShouldMove(HInstruction* instr,
|
| HBasicBlock* loop_header) {
|
| - // If we've disabled code motion, don't move any instructions.
|
| - if (!AllowCodeMotion()) return false;
|
| -
|
| - // If --aggressive-loop-invariant-motion, move everything except change
|
| - // instructions.
|
| - if (FLAG_aggressive_loop_invariant_motion && !instr->IsChange()) {
|
| - return true;
|
| - }
|
| -
|
| - // Otherwise 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.
|
| - 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;
|
| + // If we've disabled code motion or we're in a block that unconditionally
|
| + // deoptimizes, don't move any instructions.
|
| + return AllowCodeMotion() && !instr->block()->IsDeoptimizing();
|
| }
|
|
|
|
|
| @@ -1841,6 +1822,8 @@
|
| // change instructions for them.
|
| HInstruction* new_value = NULL;
|
| bool is_truncating = use_value->CheckFlag(HValue::kTruncatingToInt32);
|
| + bool deoptimize_on_undefined =
|
| + use_value->CheckFlag(HValue::kDeoptimizeOnUndefined);
|
| if (value->IsConstant()) {
|
| HConstant* constant = HConstant::cast(value);
|
| // Try to create a new copy of the constant with the new representation.
|
| @@ -1850,8 +1833,8 @@
|
| }
|
|
|
| if (new_value == NULL) {
|
| - new_value =
|
| - new(zone()) HChange(value, value->representation(), to, is_truncating);
|
| + new_value = new(zone()) HChange(value, value->representation(), to,
|
| + is_truncating, deoptimize_on_undefined);
|
| }
|
|
|
| new_value->InsertBefore(next);
|
| @@ -1933,6 +1916,40 @@
|
| }
|
|
|
|
|
| +void HGraph::RecursivelyMarkPhiDeoptimizeOnUndefined(HPhi* phi) {
|
| + if (phi->CheckFlag(HValue::kDeoptimizeOnUndefined)) return;
|
| + phi->SetFlag(HValue::kDeoptimizeOnUndefined);
|
| + for (int i = 0; i < phi->OperandCount(); ++i) {
|
| + HValue* input = phi->OperandAt(i);
|
| + if (input->IsPhi()) {
|
| + RecursivelyMarkPhiDeoptimizeOnUndefined(HPhi::cast(input));
|
| + }
|
| + }
|
| +}
|
| +
|
| +
|
| +void HGraph::MarkDeoptimizeOnUndefined() {
|
| + HPhase phase("MarkDeoptimizeOnUndefined", this);
|
| + // Compute DeoptimizeOnUndefined flag for phis.
|
| + // Any phi that can reach a use with DeoptimizeOnUndefined set must
|
| + // have DeoptimizeOnUndefined set. Currently only HCompareIDAndBranch, with
|
| + // double input representation, has this flag set.
|
| + // The flag is used by HChange tagged->double, which must deoptimize
|
| + // if one of its uses has this flag set.
|
| + for (int i = 0; i < phi_list()->length(); i++) {
|
| + HPhi* phi = phi_list()->at(i);
|
| + if (phi->representation().IsDouble()) {
|
| + for (HUseIterator it(phi->uses()); !it.Done(); it.Advance()) {
|
| + if (it.value()->CheckFlag(HValue::kDeoptimizeOnUndefined)) {
|
| + RecursivelyMarkPhiDeoptimizeOnUndefined(phi);
|
| + break;
|
| + }
|
| + }
|
| + }
|
| + }
|
| +}
|
| +
|
| +
|
| void HGraph::ComputeMinusZeroChecks() {
|
| BitVector visited(GetMaximumValueID());
|
| for (int i = 0; i < blocks_.length(); ++i) {
|
| @@ -1976,9 +1993,10 @@
|
| HBasicBlock* if_false = owner->graph()->CreateBasicBlock();
|
| if_true->MarkAsInlineReturnTarget();
|
| if_false->MarkAsInlineReturnTarget();
|
| + Expression* cond = TestContext::cast(owner->ast_context())->condition();
|
| // The AstContext constructor pushed on the context stack. This newed
|
| // instance is the reason that AstContext can't be BASE_EMBEDDED.
|
| - test_context_ = new TestContext(owner, if_true, if_false);
|
| + test_context_ = new TestContext(owner, cond, if_true, if_false);
|
| } else {
|
| function_return_ = owner->graph()->CreateBasicBlock();
|
| function_return()->MarkAsInlineReturnTarget();
|
| @@ -2052,14 +2070,28 @@
|
|
|
|
|
| void EffectContext::ReturnInstruction(HInstruction* instr, int ast_id) {
|
| + ASSERT(!instr->IsControlInstruction());
|
| owner()->AddInstruction(instr);
|
| if (instr->HasSideEffects()) owner()->AddSimulate(ast_id);
|
| }
|
|
|
|
|
| +void EffectContext::ReturnControl(HControlInstruction* instr, int ast_id) {
|
| + ASSERT(!instr->HasSideEffects());
|
| + HBasicBlock* empty_true = owner()->graph()->CreateBasicBlock();
|
| + HBasicBlock* empty_false = owner()->graph()->CreateBasicBlock();
|
| + instr->SetSuccessorAt(0, empty_true);
|
| + instr->SetSuccessorAt(1, empty_false);
|
| + owner()->current_block()->Finish(instr);
|
| + HBasicBlock* join = owner()->CreateJoin(empty_true, empty_false, ast_id);
|
| + owner()->set_current_block(join);
|
| +}
|
| +
|
| +
|
| void ValueContext::ReturnInstruction(HInstruction* instr, int ast_id) {
|
| + ASSERT(!instr->IsControlInstruction());
|
| if (!arguments_allowed() && instr->CheckFlag(HValue::kIsArguments)) {
|
| - owner()->Bailout("bad value context for arguments object value");
|
| + return owner()->Bailout("bad value context for arguments object value");
|
| }
|
| owner()->AddInstruction(instr);
|
| owner()->Push(instr);
|
| @@ -2067,7 +2099,28 @@
|
| }
|
|
|
|
|
| +void ValueContext::ReturnControl(HControlInstruction* instr, int ast_id) {
|
| + ASSERT(!instr->HasSideEffects());
|
| + if (!arguments_allowed() && instr->CheckFlag(HValue::kIsArguments)) {
|
| + return owner()->Bailout("bad value context for arguments object value");
|
| + }
|
| + HBasicBlock* materialize_false = owner()->graph()->CreateBasicBlock();
|
| + HBasicBlock* materialize_true = owner()->graph()->CreateBasicBlock();
|
| + instr->SetSuccessorAt(0, materialize_true);
|
| + instr->SetSuccessorAt(1, materialize_false);
|
| + owner()->current_block()->Finish(instr);
|
| + owner()->set_current_block(materialize_true);
|
| + owner()->Push(owner()->graph()->GetConstantTrue());
|
| + owner()->set_current_block(materialize_false);
|
| + owner()->Push(owner()->graph()->GetConstantFalse());
|
| + HBasicBlock* join =
|
| + owner()->CreateJoin(materialize_true, materialize_false, ast_id);
|
| + owner()->set_current_block(join);
|
| +}
|
| +
|
| +
|
| void TestContext::ReturnInstruction(HInstruction* instr, int ast_id) {
|
| + ASSERT(!instr->IsControlInstruction());
|
| HGraphBuilder* builder = owner();
|
| builder->AddInstruction(instr);
|
| // We expect a simulate after every expression with side effects, though
|
| @@ -2081,22 +2134,35 @@
|
| }
|
|
|
|
|
| +void TestContext::ReturnControl(HControlInstruction* instr, int ast_id) {
|
| + ASSERT(!instr->HasSideEffects());
|
| + HBasicBlock* empty_true = owner()->graph()->CreateBasicBlock();
|
| + HBasicBlock* empty_false = owner()->graph()->CreateBasicBlock();
|
| + instr->SetSuccessorAt(0, empty_true);
|
| + instr->SetSuccessorAt(1, empty_false);
|
| + owner()->current_block()->Finish(instr);
|
| + empty_true->Goto(if_true());
|
| + empty_false->Goto(if_false());
|
| + owner()->set_current_block(NULL);
|
| +}
|
| +
|
| +
|
| void TestContext::BuildBranch(HValue* value) {
|
| // We expect the graph to be in edge-split form: there is no edge that
|
| // connects a branch node to a join node. We conservatively ensure that
|
| // property by always adding an empty block on the outgoing edges of this
|
| // branch.
|
| HGraphBuilder* builder = owner();
|
| - if (value->CheckFlag(HValue::kIsArguments)) {
|
| + if (value != NULL && value->CheckFlag(HValue::kIsArguments)) {
|
| builder->Bailout("arguments object value in a test context");
|
| }
|
| HBasicBlock* empty_true = builder->graph()->CreateBasicBlock();
|
| HBasicBlock* empty_false = builder->graph()->CreateBasicBlock();
|
| - HTest* test = new(zone()) HTest(value, empty_true, empty_false);
|
| + HBranch* test = new(zone()) HBranch(value, empty_true, empty_false);
|
| builder->current_block()->Finish(test);
|
|
|
| - empty_true->Goto(if_true(), false);
|
| - empty_false->Goto(if_false(), false);
|
| + empty_true->Goto(if_true());
|
| + empty_false->Goto(if_false());
|
| builder->set_current_block(NULL);
|
| }
|
|
|
| @@ -2148,14 +2214,17 @@
|
| void HGraphBuilder::VisitForControl(Expression* expr,
|
| HBasicBlock* true_block,
|
| HBasicBlock* false_block) {
|
| - TestContext for_test(this, true_block, false_block);
|
| + TestContext for_test(this, expr, true_block, false_block);
|
| Visit(expr);
|
| }
|
|
|
|
|
| -void HGraphBuilder::VisitArgument(Expression* expr) {
|
| - CHECK_ALIVE(VisitForValue(expr));
|
| - Push(AddInstruction(new(zone()) HPushArgument(Pop())));
|
| +HValue* HGraphBuilder::VisitArgument(Expression* expr) {
|
| + VisitForValue(expr);
|
| + if (HasStackOverflow() || current_block() == NULL) return NULL;
|
| + HValue* value = Pop();
|
| + Push(AddInstruction(new(zone()) HPushArgument(value)));
|
| + return value;
|
| }
|
|
|
|
|
| @@ -2188,7 +2257,9 @@
|
| }
|
| SetupScope(scope);
|
| VisitDeclarations(scope->declarations());
|
| - AddInstruction(new(zone()) HStackCheck());
|
| + HValue* context = environment()->LookupContext();
|
| + AddInstruction(
|
| + new(zone()) HStackCheck(context, HStackCheck::kFunctionEntry));
|
|
|
| // Add an edge to the body entry. This is warty: the graph's start
|
| // environment will be used by the Lithium translation as the initial
|
| @@ -2225,7 +2296,7 @@
|
| graph()->EliminateRedundantPhis();
|
| if (FLAG_eliminate_dead_phis) graph()->EliminateUnreachablePhis();
|
| if (!graph()->CollectPhis()) {
|
| - Bailout("Phi-use of arguments object");
|
| + Bailout("Unsupported phi-use");
|
| return NULL;
|
| }
|
|
|
| @@ -2239,6 +2310,7 @@
|
|
|
| graph()->InitializeInferredTypes();
|
| graph()->Canonicalize();
|
| + graph()->MarkDeoptimizeOnUndefined();
|
| graph()->InsertRepresentationChanges();
|
| graph()->ComputeMinusZeroChecks();
|
|
|
| @@ -2253,10 +2325,32 @@
|
| gvn.Analyze();
|
| }
|
|
|
| + // Replace the results of check instructions with the original value, if the
|
| + // result is used. This is safe now, since we don't do code motion after this
|
| + // point. It enables better register allocation since the value produced by
|
| + // check instructions is really a copy of the original value.
|
| + graph()->ReplaceCheckedValues();
|
| +
|
| return graph();
|
| }
|
|
|
|
|
| +void HGraph::ReplaceCheckedValues() {
|
| + HPhase phase("Replace checked values", this);
|
| + for (int i = 0; i < blocks()->length(); ++i) {
|
| + HInstruction* instr = blocks()->at(i)->first();
|
| + while (instr != NULL) {
|
| + if (instr->IsBoundsCheck()) {
|
| + // Replace all uses of the checked value with the original input.
|
| + ASSERT(instr->UseCount() > 0);
|
| + instr->ReplaceAllUsesWith(HBoundsCheck::cast(instr)->index());
|
| + }
|
| + instr = instr->next();
|
| + }
|
| + }
|
| +}
|
| +
|
| +
|
| HInstruction* HGraphBuilder::AddInstruction(HInstruction* instr) {
|
| ASSERT(current_block() != NULL);
|
| current_block()->AddInstruction(instr);
|
| @@ -2264,9 +2358,9 @@
|
| }
|
|
|
|
|
| -void HGraphBuilder::AddSimulate(int id) {
|
| +void HGraphBuilder::AddSimulate(int ast_id) {
|
| ASSERT(current_block() != NULL);
|
| - current_block()->AddSimulate(id);
|
| + current_block()->AddSimulate(ast_id);
|
| }
|
|
|
|
|
| @@ -2298,9 +2392,6 @@
|
|
|
|
|
| void HGraphBuilder::SetupScope(Scope* scope) {
|
| - // We don't yet handle the function name for named function expressions.
|
| - if (scope->function() != NULL) return Bailout("named function expression");
|
| -
|
| HConstant* undefined_constant = new(zone()) HConstant(
|
| isolate()->factory()->undefined_value(), Representation::Tagged());
|
| AddInstruction(undefined_constant);
|
| @@ -2329,18 +2420,13 @@
|
| // Handle the arguments and arguments shadow variables specially (they do
|
| // not have declarations).
|
| if (scope->arguments() != NULL) {
|
| - if (!scope->arguments()->IsStackAllocated() ||
|
| - (scope->arguments_shadow() != NULL &&
|
| - !scope->arguments_shadow()->IsStackAllocated())) {
|
| + if (!scope->arguments()->IsStackAllocated()) {
|
| return Bailout("context-allocated arguments");
|
| }
|
| HArgumentsObject* object = new(zone()) HArgumentsObject;
|
| AddInstruction(object);
|
| graph()->SetArgumentsObject(object);
|
| environment()->Bind(scope->arguments(), object);
|
| - if (scope->arguments_shadow() != NULL) {
|
| - environment()->Bind(scope->arguments_shadow(), object);
|
| - }
|
| }
|
| }
|
|
|
| @@ -2433,7 +2519,7 @@
|
| cond_false = NULL;
|
| }
|
|
|
| - HBasicBlock* join = CreateJoin(cond_true, cond_false, stmt->id());
|
| + HBasicBlock* join = CreateJoin(cond_true, cond_false, stmt->IfId());
|
| set_current_block(join);
|
| }
|
| }
|
| @@ -2511,7 +2597,7 @@
|
| test->if_false());
|
| } else if (context->IsEffect()) {
|
| CHECK_ALIVE(VisitForEffect(stmt->expression()));
|
| - current_block()->Goto(function_return(), false);
|
| + current_block()->Goto(function_return());
|
| } else {
|
| ASSERT(context->IsValue());
|
| CHECK_ALIVE(VisitForValue(stmt->expression()));
|
| @@ -2523,19 +2609,20 @@
|
| }
|
|
|
|
|
| -void HGraphBuilder::VisitWithEnterStatement(WithEnterStatement* stmt) {
|
| +void HGraphBuilder::VisitEnterWithContextStatement(
|
| + EnterWithContextStatement* stmt) {
|
| ASSERT(!HasStackOverflow());
|
| ASSERT(current_block() != NULL);
|
| ASSERT(current_block()->HasPredecessor());
|
| - return Bailout("WithEnterStatement");
|
| + return Bailout("EnterWithContextStatement");
|
| }
|
|
|
|
|
| -void HGraphBuilder::VisitWithExitStatement(WithExitStatement* stmt) {
|
| +void HGraphBuilder::VisitExitContextStatement(ExitContextStatement* stmt) {
|
| ASSERT(!HasStackOverflow());
|
| ASSERT(current_block() != NULL);
|
| ASSERT(current_block()->HasPredecessor());
|
| - return Bailout("WithExitStatement");
|
| + return Bailout("ExitContextStatement");
|
| }
|
|
|
|
|
| @@ -2579,15 +2666,16 @@
|
| // Otherwise generate a compare and branch.
|
| CHECK_ALIVE(VisitForValue(clause->label()));
|
| HValue* label_value = Pop();
|
| - HCompare* compare =
|
| - new(zone()) HCompare(tag_value, label_value, Token::EQ_STRICT);
|
| + HCompareIDAndBranch* compare =
|
| + new(zone()) HCompareIDAndBranch(tag_value,
|
| + label_value,
|
| + Token::EQ_STRICT);
|
| compare->SetInputRepresentation(Representation::Integer32());
|
| - ASSERT(!compare->HasSideEffects());
|
| - AddInstruction(compare);
|
| HBasicBlock* body_block = graph()->CreateBasicBlock();
|
| HBasicBlock* next_test_block = graph()->CreateBasicBlock();
|
| - HTest* branch = new(zone()) HTest(compare, body_block, next_test_block);
|
| - current_block()->Finish(branch);
|
| + compare->SetSuccessorAt(0, body_block);
|
| + compare->SetSuccessorAt(1, next_test_block);
|
| + current_block()->Finish(compare);
|
| set_current_block(next_test_block);
|
| }
|
|
|
| @@ -2673,7 +2761,7 @@
|
| HBasicBlock* non_osr_entry = graph()->CreateBasicBlock();
|
| HBasicBlock* osr_entry = graph()->CreateBasicBlock();
|
| HValue* true_value = graph()->GetConstantTrue();
|
| - HTest* test = new(zone()) HTest(true_value, non_osr_entry, osr_entry);
|
| + HBranch* test = new(zone()) HBranch(true_value, non_osr_entry, osr_entry);
|
| current_block()->Finish(test);
|
|
|
| HBasicBlock* loop_predecessor = graph()->CreateBasicBlock();
|
| @@ -2701,6 +2789,21 @@
|
| }
|
|
|
|
|
| +void HGraphBuilder::VisitLoopBody(IterationStatement* stmt,
|
| + HBasicBlock* loop_entry,
|
| + BreakAndContinueInfo* break_info) {
|
| + BreakAndContinueScope push(break_info, this);
|
| + AddSimulate(stmt->StackCheckId());
|
| + HValue* context = environment()->LookupContext();
|
| + HStackCheck* stack_check =
|
| + new(zone()) HStackCheck(context, HStackCheck::kBackwardsBranch);
|
| + AddInstruction(stack_check);
|
| + ASSERT(loop_entry->IsLoopHeader());
|
| + loop_entry->loop_information()->set_stack_check(stack_check);
|
| + CHECK_BAILOUT(Visit(stmt->body()));
|
| +}
|
| +
|
| +
|
| void HGraphBuilder::VisitDoWhileStatement(DoWhileStatement* stmt) {
|
| ASSERT(!HasStackOverflow());
|
| ASSERT(current_block() != NULL);
|
| @@ -2708,13 +2811,11 @@
|
| ASSERT(current_block() != NULL);
|
| PreProcessOsrEntry(stmt);
|
| HBasicBlock* loop_entry = CreateLoopHeaderBlock();
|
| - current_block()->Goto(loop_entry, false);
|
| + current_block()->Goto(loop_entry);
|
| set_current_block(loop_entry);
|
|
|
| BreakAndContinueInfo break_info(stmt);
|
| - { BreakAndContinueScope push(&break_info, this);
|
| - CHECK_BAILOUT(Visit(stmt->body()));
|
| - }
|
| + CHECK_BAILOUT(VisitLoopBody(stmt, loop_entry, &break_info));
|
| HBasicBlock* body_exit =
|
| JoinContinue(stmt, current_block(), break_info.continue_block());
|
| HBasicBlock* loop_successor = NULL;
|
| @@ -2752,7 +2853,7 @@
|
| ASSERT(current_block() != NULL);
|
| PreProcessOsrEntry(stmt);
|
| HBasicBlock* loop_entry = CreateLoopHeaderBlock();
|
| - current_block()->Goto(loop_entry, false);
|
| + current_block()->Goto(loop_entry);
|
| set_current_block(loop_entry);
|
|
|
| // If the condition is constant true, do not generate a branch.
|
| @@ -2775,7 +2876,7 @@
|
| BreakAndContinueInfo break_info(stmt);
|
| if (current_block() != NULL) {
|
| BreakAndContinueScope push(&break_info, this);
|
| - CHECK_BAILOUT(Visit(stmt->body()));
|
| + CHECK_BAILOUT(VisitLoopBody(stmt, loop_entry, &break_info));
|
| }
|
| HBasicBlock* body_exit =
|
| JoinContinue(stmt, current_block(), break_info.continue_block());
|
| @@ -2798,7 +2899,7 @@
|
| ASSERT(current_block() != NULL);
|
| PreProcessOsrEntry(stmt);
|
| HBasicBlock* loop_entry = CreateLoopHeaderBlock();
|
| - current_block()->Goto(loop_entry, false);
|
| + current_block()->Goto(loop_entry);
|
| set_current_block(loop_entry);
|
|
|
| HBasicBlock* loop_successor = NULL;
|
| @@ -2820,7 +2921,7 @@
|
| BreakAndContinueInfo break_info(stmt);
|
| if (current_block() != NULL) {
|
| BreakAndContinueScope push(&break_info, this);
|
| - CHECK_BAILOUT(Visit(stmt->body()));
|
| + CHECK_BAILOUT(VisitLoopBody(stmt, loop_entry, &break_info));
|
| }
|
| HBasicBlock* body_exit =
|
| JoinContinue(stmt, current_block(), break_info.continue_block());
|
| @@ -2904,9 +3005,10 @@
|
| }
|
| // We also have a stack overflow if the recursive compilation did.
|
| if (HasStackOverflow()) return;
|
| + HValue* context = environment()->LookupContext();
|
| HFunctionLiteral* instr =
|
| - new(zone()) HFunctionLiteral(shared_info, expr->pretenure());
|
| - ast_context()->ReturnInstruction(instr, expr->id());
|
| + new(zone()) HFunctionLiteral(context, shared_info, expr->pretenure());
|
| + return ast_context()->ReturnInstruction(instr, expr->id());
|
| }
|
|
|
|
|
| @@ -2951,7 +3053,7 @@
|
| HBasicBlock* join = CreateJoin(cond_true, cond_false, expr->id());
|
| set_current_block(join);
|
| if (join != NULL && !ast_context()->IsEffect()) {
|
| - ast_context()->ReturnValue(Pop());
|
| + return ast_context()->ReturnValue(Pop());
|
| }
|
| }
|
| }
|
| @@ -2996,7 +3098,12 @@
|
| if (variable == NULL) {
|
| return Bailout("reference to rewritten variable");
|
| } else if (variable->IsStackAllocated()) {
|
| - ast_context()->ReturnValue(environment()->Lookup(variable));
|
| + HValue* value = environment()->Lookup(variable);
|
| + if (variable->mode() == Variable::CONST &&
|
| + value == graph()->GetConstantHole()) {
|
| + return Bailout("reference to uninitialized const variable");
|
| + }
|
| + return ast_context()->ReturnValue(value);
|
| } else if (variable->IsContextSlot()) {
|
| if (variable->mode() == Variable::CONST) {
|
| return Bailout("reference to const context slot");
|
| @@ -3004,7 +3111,7 @@
|
| HValue* context = BuildContextChainWalk(variable);
|
| int index = variable->AsSlot()->index();
|
| HLoadContextSlot* instr = new(zone()) HLoadContextSlot(context, index);
|
| - ast_context()->ReturnInstruction(instr, expr->id());
|
| + return ast_context()->ReturnInstruction(instr, expr->id());
|
| } else if (variable->is_global()) {
|
| LookupResult lookup;
|
| GlobalPropertyAccess type = LookupGlobalProperty(variable, &lookup, false);
|
| @@ -3019,7 +3126,7 @@
|
| Handle<JSGlobalPropertyCell> cell(global->GetPropertyCell(&lookup));
|
| bool check_hole = !lookup.IsDontDelete() || lookup.IsReadOnly();
|
| HLoadGlobalCell* instr = new(zone()) HLoadGlobalCell(cell, check_hole);
|
| - ast_context()->ReturnInstruction(instr, expr->id());
|
| + return ast_context()->ReturnInstruction(instr, expr->id());
|
| } else {
|
| HValue* context = environment()->LookupContext();
|
| HGlobalObject* global_object = new(zone()) HGlobalObject(context);
|
| @@ -3031,7 +3138,7 @@
|
| ast_context()->is_for_typeof());
|
| instr->set_position(expr->position());
|
| ASSERT(instr->HasSideEffects());
|
| - ast_context()->ReturnInstruction(instr, expr->id());
|
| + return ast_context()->ReturnInstruction(instr, expr->id());
|
| }
|
| } else {
|
| return Bailout("reference to a variable which requires dynamic lookup");
|
| @@ -3045,7 +3152,7 @@
|
| ASSERT(current_block()->HasPredecessor());
|
| HConstant* instr =
|
| new(zone()) HConstant(expr->handle(), Representation::Tagged());
|
| - ast_context()->ReturnInstruction(instr, expr->id());
|
| + return ast_context()->ReturnInstruction(instr, expr->id());
|
| }
|
|
|
|
|
| @@ -3053,10 +3160,13 @@
|
| ASSERT(!HasStackOverflow());
|
| ASSERT(current_block() != NULL);
|
| ASSERT(current_block()->HasPredecessor());
|
| - HRegExpLiteral* instr = new(zone()) HRegExpLiteral(expr->pattern(),
|
| + HValue* context = environment()->LookupContext();
|
| +
|
| + HRegExpLiteral* instr = new(zone()) HRegExpLiteral(context,
|
| + expr->pattern(),
|
| expr->flags(),
|
| expr->literal_index());
|
| - ast_context()->ReturnInstruction(instr, expr->id());
|
| + return ast_context()->ReturnInstruction(instr, expr->id());
|
| }
|
|
|
|
|
| @@ -3126,9 +3236,9 @@
|
| // (e.g. because of code motion).
|
| HToFastProperties* result = new(zone()) HToFastProperties(Pop());
|
| AddInstruction(result);
|
| - ast_context()->ReturnValue(result);
|
| + return ast_context()->ReturnValue(result);
|
| } else {
|
| - ast_context()->ReturnValue(Pop());
|
| + return ast_context()->ReturnValue(Pop());
|
| }
|
| }
|
|
|
| @@ -3139,8 +3249,10 @@
|
| ASSERT(current_block()->HasPredecessor());
|
| ZoneList<Expression*>* subexprs = expr->values();
|
| int length = subexprs->length();
|
| + HValue* context = environment()->LookupContext();
|
|
|
| - HArrayLiteral* literal = new(zone()) HArrayLiteral(expr->constant_elements(),
|
| + HArrayLiteral* literal = new(zone()) HArrayLiteral(context,
|
| + expr->constant_elements(),
|
| length,
|
| expr->literal_index(),
|
| expr->depth());
|
| @@ -3172,18 +3284,10 @@
|
| AddInstruction(new(zone()) HStoreKeyedFastElement(elements, key, value));
|
| AddSimulate(expr->GetIdForElement(i));
|
| }
|
| - ast_context()->ReturnValue(Pop());
|
| + return ast_context()->ReturnValue(Pop());
|
| }
|
|
|
|
|
| -void HGraphBuilder::VisitCatchExtensionObject(CatchExtensionObject* expr) {
|
| - ASSERT(!HasStackOverflow());
|
| - ASSERT(current_block() != NULL);
|
| - ASSERT(current_block()->HasPredecessor());
|
| - return Bailout("CatchExtensionObject");
|
| -}
|
| -
|
| -
|
| // Sets the lookup result and returns true if the store can be inlined.
|
| static bool ComputeStoredField(Handle<Map> type,
|
| Handle<String> name,
|
| @@ -3342,15 +3446,14 @@
|
| Drop(1);
|
| }
|
| }
|
| - ast_context()->ReturnValue(value);
|
| - return;
|
| + return ast_context()->ReturnValue(value);
|
| }
|
| }
|
|
|
| ASSERT(join != NULL);
|
| join->SetJoinId(expr->id());
|
| set_current_block(join);
|
| - if (!ast_context()->IsEffect()) ast_context()->ReturnValue(Pop());
|
| + if (!ast_context()->IsEffect()) return ast_context()->ReturnValue(Pop());
|
| }
|
|
|
|
|
| @@ -3394,13 +3497,21 @@
|
| value = Pop();
|
| HValue* key = Pop();
|
| HValue* object = Pop();
|
| - instr = BuildStoreKeyed(object, key, value, expr);
|
| + bool has_side_effects = false;
|
| + HandleKeyedElementAccess(object, key, value, expr, expr->AssignmentId(),
|
| + expr->position(),
|
| + true, // is_store
|
| + &has_side_effects);
|
| + Push(value);
|
| + ASSERT(has_side_effects); // Stores always have side effects.
|
| + AddSimulate(expr->AssignmentId());
|
| + return ast_context()->ReturnValue(Pop());
|
| }
|
| Push(value);
|
| instr->set_position(expr->position());
|
| AddInstruction(instr);
|
| if (instr->HasSideEffects()) AddSimulate(expr->AssignmentId());
|
| - ast_context()->ReturnValue(Pop());
|
| + return ast_context()->ReturnValue(Pop());
|
| }
|
|
|
|
|
| @@ -3451,6 +3562,10 @@
|
| BinaryOperation* operation = expr->binary_operation();
|
|
|
| if (var != NULL) {
|
| + if (var->mode() == Variable::CONST) {
|
| + return Bailout("unsupported const compound assignment");
|
| + }
|
| +
|
| CHECK_ALIVE(VisitForValue(operation));
|
|
|
| if (var->is_global()) {
|
| @@ -3461,6 +3576,20 @@
|
| } else if (var->IsStackAllocated()) {
|
| Bind(var, Top());
|
| } else if (var->IsContextSlot()) {
|
| + // Bail out if we try to mutate a parameter value in a function using
|
| + // the arguments object. We do not (yet) correctly handle the
|
| + // arguments property of the function.
|
| + if (info()->scope()->arguments() != NULL) {
|
| + // Parameters will rewrite to context slots. We have no direct way
|
| + // to detect that the variable is a parameter.
|
| + int count = info()->scope()->num_parameters();
|
| + for (int i = 0; i < count; ++i) {
|
| + if (var == info()->scope()->parameter(i)) {
|
| + Bailout("assignment to parameter, function uses arguments object");
|
| + }
|
| + }
|
| + }
|
| +
|
| HValue* context = BuildContextChainWalk(var);
|
| int index = var->AsSlot()->index();
|
| HStoreContextSlot* instr =
|
| @@ -3470,7 +3599,7 @@
|
| } else {
|
| return Bailout("compound assignment to lookup slot");
|
| }
|
| - ast_context()->ReturnValue(Pop());
|
| + return ast_context()->ReturnValue(Pop());
|
|
|
| } else if (prop != NULL) {
|
| prop->RecordTypeFeedback(oracle());
|
| @@ -3505,7 +3634,7 @@
|
| Drop(2);
|
| Push(instr);
|
| if (store->HasSideEffects()) AddSimulate(expr->AssignmentId());
|
| - ast_context()->ReturnValue(Pop());
|
| + return ast_context()->ReturnValue(Pop());
|
|
|
| } else {
|
| // Keyed property.
|
| @@ -3514,10 +3643,15 @@
|
| HValue* obj = environment()->ExpressionStackAt(1);
|
| HValue* key = environment()->ExpressionStackAt(0);
|
|
|
| - HInstruction* load = BuildLoadKeyed(obj, key, prop);
|
| - PushAndAdd(load);
|
| - if (load->HasSideEffects()) AddSimulate(expr->CompoundLoadId());
|
| + bool has_side_effects = false;
|
| + HValue* load = HandleKeyedElementAccess(
|
| + obj, key, NULL, prop, expr->CompoundLoadId(), RelocInfo::kNoPosition,
|
| + false, // is_store
|
| + &has_side_effects);
|
| + Push(load);
|
| + if (has_side_effects) AddSimulate(expr->CompoundLoadId());
|
|
|
| +
|
| CHECK_ALIVE(VisitForValue(expr->value()));
|
| HValue* right = Pop();
|
| HValue* left = Pop();
|
| @@ -3527,13 +3661,17 @@
|
| if (instr->HasSideEffects()) AddSimulate(operation->id());
|
|
|
| expr->RecordTypeFeedback(oracle());
|
| - HInstruction* store = BuildStoreKeyed(obj, key, instr, expr);
|
| - AddInstruction(store);
|
| + HandleKeyedElementAccess(obj, key, instr, expr, expr->AssignmentId(),
|
| + RelocInfo::kNoPosition,
|
| + true, // is_store
|
| + &has_side_effects);
|
| +
|
| // Drop the simulated receiver, key, and value. Return the value.
|
| Drop(3);
|
| Push(instr);
|
| - if (store->HasSideEffects()) AddSimulate(expr->AssignmentId());
|
| - ast_context()->ReturnValue(Pop());
|
| + ASSERT(has_side_effects); // Stores always have side effects.
|
| + AddSimulate(expr->AssignmentId());
|
| + return ast_context()->ReturnValue(Pop());
|
| }
|
|
|
| } else {
|
| @@ -3557,6 +3695,19 @@
|
| }
|
|
|
| if (var != NULL) {
|
| + if (var->mode() == Variable::CONST) {
|
| + if (expr->op() != Token::INIT_CONST) {
|
| + return Bailout("non-initializer assignment to const");
|
| + }
|
| + if (!var->IsStackAllocated()) {
|
| + return Bailout("assignment to const context slot");
|
| + }
|
| + // We insert a use of the old value to detect unsupported uses of const
|
| + // variables (e.g. initialization inside a loop).
|
| + HValue* old_value = environment()->Lookup(var);
|
| + AddInstruction(new HUseConst(old_value));
|
| + }
|
| +
|
| if (proxy->IsArguments()) return Bailout("assignment to arguments");
|
|
|
| // Handle the assignment.
|
| @@ -3567,9 +3718,24 @@
|
| CHECK_ALIVE(VisitForValue(expr->value(), ARGUMENTS_ALLOWED));
|
| HValue* value = Pop();
|
| Bind(var, value);
|
| - ast_context()->ReturnValue(value);
|
| + return ast_context()->ReturnValue(value);
|
|
|
| - } else if (var->IsContextSlot() && var->mode() != Variable::CONST) {
|
| + } else if (var->IsContextSlot()) {
|
| + ASSERT(var->mode() != Variable::CONST);
|
| + // Bail out if we try to mutate a parameter value in a function using
|
| + // the arguments object. We do not (yet) correctly handle the
|
| + // arguments property of the function.
|
| + if (info()->scope()->arguments() != NULL) {
|
| + // Parameters will rewrite to context slots. We have no direct way
|
| + // to detect that the variable is a parameter.
|
| + int count = info()->scope()->num_parameters();
|
| + for (int i = 0; i < count; ++i) {
|
| + if (var == info()->scope()->parameter(i)) {
|
| + Bailout("assignment to parameter, function uses arguments object");
|
| + }
|
| + }
|
| + }
|
| +
|
| CHECK_ALIVE(VisitForValue(expr->value()));
|
| HValue* context = BuildContextChainWalk(var);
|
| int index = var->AsSlot()->index();
|
| @@ -3577,7 +3743,7 @@
|
| new(zone()) HStoreContextSlot(context, index, Top());
|
| AddInstruction(instr);
|
| if (instr->HasSideEffects()) AddSimulate(expr->AssignmentId());
|
| - ast_context()->ReturnValue(Pop());
|
| + return ast_context()->ReturnValue(Pop());
|
|
|
| } else if (var->is_global()) {
|
| CHECK_ALIVE(VisitForValue(expr->value()));
|
| @@ -3585,7 +3751,7 @@
|
| Top(),
|
| expr->position(),
|
| expr->AssignmentId());
|
| - ast_context()->ReturnValue(Pop());
|
| + return ast_context()->ReturnValue(Pop());
|
|
|
| } else {
|
| return Bailout("assignment to LOOKUP or const CONTEXT variable");
|
| @@ -3609,8 +3775,9 @@
|
| ASSERT(ast_context()->IsEffect());
|
| CHECK_ALIVE(VisitForValue(expr->exception()));
|
|
|
| + HValue* context = environment()->LookupContext();
|
| HValue* value = environment()->Pop();
|
| - HThrow* instr = new(zone()) HThrow(value);
|
| + HThrow* instr = new(zone()) HThrow(context, value);
|
| instr->set_position(expr->position());
|
| AddInstruction(instr);
|
| AddSimulate(expr->id());
|
| @@ -3682,70 +3849,270 @@
|
| }
|
|
|
|
|
| -HInstruction* HGraphBuilder::BuildLoadKeyedFastElement(HValue* object,
|
| - HValue* key,
|
| - Property* expr) {
|
| - ASSERT(!expr->key()->IsPropertyName() && expr->IsMonomorphic());
|
| +HInstruction* HGraphBuilder::BuildExternalArrayElementAccess(
|
| + HValue* external_elements,
|
| + HValue* checked_key,
|
| + HValue* val,
|
| + JSObject::ElementsKind elements_kind,
|
| + bool is_store) {
|
| + if (is_store) {
|
| + ASSERT(val != NULL);
|
| + switch (elements_kind) {
|
| + case JSObject::EXTERNAL_PIXEL_ELEMENTS: {
|
| + HClampToUint8* clamp = new(zone()) HClampToUint8(val);
|
| + AddInstruction(clamp);
|
| + val = clamp;
|
| + break;
|
| + }
|
| + case JSObject::EXTERNAL_BYTE_ELEMENTS:
|
| + case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
|
| + case JSObject::EXTERNAL_SHORT_ELEMENTS:
|
| + case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
|
| + case JSObject::EXTERNAL_INT_ELEMENTS:
|
| + case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: {
|
| + HToInt32* floor_val = new(zone()) HToInt32(val);
|
| + AddInstruction(floor_val);
|
| + val = floor_val;
|
| + break;
|
| + }
|
| + case JSObject::EXTERNAL_FLOAT_ELEMENTS:
|
| + case JSObject::EXTERNAL_DOUBLE_ELEMENTS:
|
| + break;
|
| + case JSObject::FAST_ELEMENTS:
|
| + case JSObject::FAST_DOUBLE_ELEMENTS:
|
| + case JSObject::DICTIONARY_ELEMENTS:
|
| + case JSObject::NON_STRICT_ARGUMENTS_ELEMENTS:
|
| + UNREACHABLE();
|
| + break;
|
| + }
|
| + return new(zone()) HStoreKeyedSpecializedArrayElement(
|
| + external_elements, checked_key, val, elements_kind);
|
| + } else {
|
| + return new(zone()) HLoadKeyedSpecializedArrayElement(
|
| + external_elements, checked_key, elements_kind);
|
| + }
|
| +}
|
| +
|
| +
|
| +HInstruction* HGraphBuilder::BuildMonomorphicElementAccess(HValue* object,
|
| + HValue* key,
|
| + HValue* val,
|
| + Expression* expr,
|
| + bool is_store) {
|
| + ASSERT(expr->IsMonomorphic());
|
| + Handle<Map> map = expr->GetMonomorphicReceiverType();
|
| + if (!map->has_fast_elements() && !map->has_external_array_elements()) {
|
| + return is_store ? BuildStoreKeyedGeneric(object, key, val)
|
| + : BuildLoadKeyedGeneric(object, key);
|
| + }
|
| AddInstruction(new(zone()) HCheckNonSmi(object));
|
| - Handle<Map> map = expr->GetMonomorphicReceiverType();
|
| - ASSERT(map->has_fast_elements());
|
| AddInstruction(new(zone()) HCheckMap(object, map));
|
| - bool is_array = (map->instance_type() == JS_ARRAY_TYPE);
|
| - HLoadElements* elements = new(zone()) HLoadElements(object);
|
| + HInstruction* elements = new(zone()) HLoadElements(object);
|
| HInstruction* length = NULL;
|
| - if (is_array) {
|
| + HInstruction* checked_key = NULL;
|
| + if (map->has_external_array_elements()) {
|
| + AddInstruction(elements);
|
| + length = AddInstruction(new(zone()) HExternalArrayLength(elements));
|
| + checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length));
|
| + HLoadExternalArrayPointer* external_elements =
|
| + new(zone()) HLoadExternalArrayPointer(elements);
|
| + AddInstruction(external_elements);
|
| + return BuildExternalArrayElementAccess(external_elements, checked_key,
|
| + val, map->elements_kind(), is_store);
|
| + }
|
| + ASSERT(map->has_fast_elements());
|
| + if (map->instance_type() == JS_ARRAY_TYPE) {
|
| length = AddInstruction(new(zone()) HJSArrayLength(object));
|
| - AddInstruction(new(zone()) HBoundsCheck(key, length));
|
| + checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length));
|
| AddInstruction(elements);
|
| } else {
|
| AddInstruction(elements);
|
| length = AddInstruction(new(zone()) HFixedArrayLength(elements));
|
| - AddInstruction(new(zone()) HBoundsCheck(key, length));
|
| + checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length));
|
| }
|
| - return new(zone()) HLoadKeyedFastElement(elements, key);
|
| + if (is_store) {
|
| + return new(zone()) HStoreKeyedFastElement(elements, checked_key, val);
|
| + } else {
|
| + return new(zone()) HLoadKeyedFastElement(elements, checked_key);
|
| + }
|
| }
|
|
|
|
|
| -HInstruction* HGraphBuilder::BuildLoadKeyedSpecializedArrayElement(
|
| - HValue* object,
|
| - HValue* key,
|
| - Property* expr) {
|
| - ASSERT(!expr->key()->IsPropertyName() && expr->IsMonomorphic());
|
| +HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object,
|
| + HValue* key,
|
| + HValue* val,
|
| + Expression* prop,
|
| + int ast_id,
|
| + int position,
|
| + bool is_store,
|
| + bool* has_side_effects) {
|
| + *has_side_effects = false;
|
| AddInstruction(new(zone()) HCheckNonSmi(object));
|
| - Handle<Map> map = expr->GetMonomorphicReceiverType();
|
| - ASSERT(!map->has_fast_elements());
|
| - ASSERT(map->has_external_array_elements());
|
| - AddInstruction(new(zone()) HCheckMap(object, map));
|
| - HLoadElements* elements = new(zone()) HLoadElements(object);
|
| - AddInstruction(elements);
|
| - HInstruction* length = new(zone()) HExternalArrayLength(elements);
|
| - AddInstruction(length);
|
| - AddInstruction(new(zone()) HBoundsCheck(key, length));
|
| - HLoadExternalArrayPointer* external_elements =
|
| - new(zone()) HLoadExternalArrayPointer(elements);
|
| - AddInstruction(external_elements);
|
| - HLoadKeyedSpecializedArrayElement* pixel_array_value =
|
| - new(zone()) HLoadKeyedSpecializedArrayElement(
|
| - external_elements, key, expr->external_array_type());
|
| - return pixel_array_value;
|
| + AddInstruction(HCheckInstanceType::NewIsSpecObject(object));
|
| + ZoneMapList* maps = prop->GetReceiverTypes();
|
| + bool todo_external_array = false;
|
| +
|
| + static const int kNumElementTypes = JSObject::kElementsKindCount;
|
| + bool type_todo[kNumElementTypes];
|
| + for (int i = 0; i < kNumElementTypes; ++i) {
|
| + type_todo[i] = false;
|
| + }
|
| +
|
| + for (int i = 0; i < maps->length(); ++i) {
|
| + ASSERT(maps->at(i)->IsMap());
|
| + type_todo[maps->at(i)->elements_kind()] = true;
|
| + if (maps->at(i)->elements_kind()
|
| + >= JSObject::FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND) {
|
| + todo_external_array = true;
|
| + }
|
| + }
|
| + // Support for FAST_DOUBLE_ELEMENTS isn't implemented yet, so we deopt.
|
| + type_todo[JSObject::FAST_DOUBLE_ELEMENTS] = false;
|
| +
|
| + HBasicBlock* join = graph()->CreateBasicBlock();
|
| +
|
| + HInstruction* elements_kind_instr =
|
| + AddInstruction(new(zone()) HElementsKind(object));
|
| + HInstruction* elements = NULL;
|
| + HLoadExternalArrayPointer* external_elements = NULL;
|
| + HInstruction* checked_key = NULL;
|
| +
|
| + // FAST_ELEMENTS is assumed to be the first case.
|
| + STATIC_ASSERT(JSObject::FAST_ELEMENTS == 0);
|
| +
|
| + for (JSObject::ElementsKind elements_kind = JSObject::FAST_ELEMENTS;
|
| + elements_kind <= JSObject::LAST_ELEMENTS_KIND;
|
| + elements_kind = JSObject::ElementsKind(elements_kind + 1)) {
|
| + // After having handled FAST_ELEMENTS and DICTIONARY_ELEMENTS, we
|
| + // need to add some code that's executed for all external array cases.
|
| + STATIC_ASSERT(JSObject::LAST_EXTERNAL_ARRAY_ELEMENTS_KIND ==
|
| + JSObject::LAST_ELEMENTS_KIND);
|
| + if (elements_kind == JSObject::FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND
|
| + && todo_external_array) {
|
| + elements = AddInstruction(new(zone()) HLoadElements(object));
|
| + // We need to forcibly prevent some ElementsKind-dependent instructions
|
| + // from being hoisted out of any loops they might occur in, because
|
| + // the current loop-invariant-code-motion algorithm isn't clever enough
|
| + // to deal with them properly.
|
| + // There's some performance to be gained by developing a smarter
|
| + // solution for this.
|
| + elements->ClearFlag(HValue::kUseGVN);
|
| + HInstruction* length =
|
| + AddInstruction(new(zone()) HExternalArrayLength(elements));
|
| + checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length));
|
| + external_elements = new(zone()) HLoadExternalArrayPointer(elements);
|
| + AddInstruction(external_elements);
|
| + }
|
| + if (type_todo[elements_kind]) {
|
| + HBasicBlock* if_true = graph()->CreateBasicBlock();
|
| + HBasicBlock* if_false = graph()->CreateBasicBlock();
|
| + HCompareConstantEqAndBranch* compare =
|
| + new(zone()) HCompareConstantEqAndBranch(elements_kind_instr,
|
| + elements_kind,
|
| + Token::EQ_STRICT);
|
| + compare->SetSuccessorAt(0, if_true);
|
| + compare->SetSuccessorAt(1, if_false);
|
| + current_block()->Finish(compare);
|
| +
|
| + set_current_block(if_true);
|
| + HInstruction* access;
|
| + if (elements_kind == JSObject::FAST_ELEMENTS) {
|
| + HBasicBlock* if_jsarray = graph()->CreateBasicBlock();
|
| + HBasicBlock* if_fastobject = graph()->CreateBasicBlock();
|
| + HHasInstanceTypeAndBranch* typecheck =
|
| + new(zone()) HHasInstanceTypeAndBranch(object, JS_ARRAY_TYPE);
|
| + typecheck->SetSuccessorAt(0, if_jsarray);
|
| + typecheck->SetSuccessorAt(1, if_fastobject);
|
| + current_block()->Finish(typecheck);
|
| +
|
| + set_current_block(if_jsarray);
|
| + HInstruction* length = new(zone()) HJSArrayLength(object);
|
| + AddInstruction(length);
|
| + length->ClearFlag(HValue::kUseGVN);
|
| + checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length));
|
| + elements = AddInstruction(new(zone()) HLoadElements(object));
|
| + elements->ClearFlag(HValue::kUseGVN);
|
| + if (is_store) {
|
| + access = AddInstruction(
|
| + new(zone()) HStoreKeyedFastElement(elements, checked_key, val));
|
| + } else {
|
| + access = AddInstruction(
|
| + new(zone()) HLoadKeyedFastElement(elements, checked_key));
|
| + Push(access);
|
| + }
|
| + *has_side_effects |= access->HasSideEffects();
|
| + if (position != -1) {
|
| + access->set_position(position);
|
| + }
|
| + if_jsarray->Goto(join);
|
| +
|
| + set_current_block(if_fastobject);
|
| + elements = AddInstruction(new(zone()) HLoadElements(object));
|
| + elements->ClearFlag(HValue::kUseGVN);
|
| + length = AddInstruction(new(zone()) HFixedArrayLength(elements));
|
| + checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length));
|
| + if (is_store) {
|
| + access = AddInstruction(
|
| + new(zone()) HStoreKeyedFastElement(elements, checked_key, val));
|
| + } else {
|
| + access = AddInstruction(
|
| + new(zone()) HLoadKeyedFastElement(elements, checked_key));
|
| + }
|
| + } else if (elements_kind == JSObject::DICTIONARY_ELEMENTS) {
|
| + if (is_store) {
|
| + access = AddInstruction(BuildStoreKeyedGeneric(object, key, val));
|
| + } else {
|
| + access = AddInstruction(BuildLoadKeyedGeneric(object, key));
|
| + }
|
| + } else { // External array elements.
|
| + access = AddInstruction(BuildExternalArrayElementAccess(
|
| + external_elements, checked_key, val, elements_kind, is_store));
|
| + }
|
| + *has_side_effects |= access->HasSideEffects();
|
| + access->set_position(position);
|
| + if (!is_store) {
|
| + Push(access);
|
| + }
|
| + current_block()->Goto(join);
|
| + set_current_block(if_false);
|
| + }
|
| + }
|
| +
|
| + // Deopt if none of the cases matched.
|
| + current_block()->FinishExitWithDeoptimization(HDeoptimize::kNoUses);
|
| + join->SetJoinId(ast_id);
|
| + set_current_block(join);
|
| + return is_store ? NULL : Pop();
|
| }
|
|
|
|
|
| -HInstruction* HGraphBuilder::BuildLoadKeyed(HValue* obj,
|
| - HValue* key,
|
| - Property* prop) {
|
| - if (prop->IsMonomorphic()) {
|
| - Handle<Map> receiver_type(prop->GetMonomorphicReceiverType());
|
| - // An object has either fast elements or pixel array elements, but never
|
| - // both. Pixel array maps that are assigned to pixel array elements are
|
| - // always created with the fast elements flag cleared.
|
| - if (receiver_type->has_external_array_elements()) {
|
| - return BuildLoadKeyedSpecializedArrayElement(obj, key, prop);
|
| - } else if (receiver_type->has_fast_elements()) {
|
| - return BuildLoadKeyedFastElement(obj, key, prop);
|
| +HValue* HGraphBuilder::HandleKeyedElementAccess(HValue* obj,
|
| + HValue* key,
|
| + HValue* val,
|
| + Expression* expr,
|
| + int ast_id,
|
| + int position,
|
| + bool is_store,
|
| + bool* has_side_effects) {
|
| + ASSERT(!expr->IsPropertyName());
|
| + HInstruction* instr = NULL;
|
| + if (expr->IsMonomorphic()) {
|
| + instr = BuildMonomorphicElementAccess(obj, key, val, expr, is_store);
|
| + } else if (expr->GetReceiverTypes() != NULL &&
|
| + !expr->GetReceiverTypes()->is_empty()) {
|
| + return HandlePolymorphicElementAccess(
|
| + obj, key, val, expr, ast_id, position, is_store, has_side_effects);
|
| + } else {
|
| + if (is_store) {
|
| + instr = BuildStoreKeyedGeneric(obj, key, val);
|
| + } else {
|
| + instr = BuildLoadKeyedGeneric(obj, key);
|
| }
|
| }
|
| - return BuildLoadKeyedGeneric(obj, key);
|
| + instr->set_position(position);
|
| + AddInstruction(instr);
|
| + *has_side_effects = instr->HasSideEffects();
|
| + return instr;
|
| }
|
|
|
|
|
| @@ -3761,85 +4128,6 @@
|
| function_strict_mode());
|
| }
|
|
|
| -
|
| -HInstruction* HGraphBuilder::BuildStoreKeyedFastElement(HValue* object,
|
| - HValue* key,
|
| - HValue* val,
|
| - Expression* expr) {
|
| - ASSERT(expr->IsMonomorphic());
|
| - AddInstruction(new(zone()) HCheckNonSmi(object));
|
| - Handle<Map> map = expr->GetMonomorphicReceiverType();
|
| - ASSERT(map->has_fast_elements());
|
| - AddInstruction(new(zone()) HCheckMap(object, map));
|
| - HInstruction* elements = AddInstruction(new(zone()) HLoadElements(object));
|
| - AddInstruction(new(zone()) HCheckMap(
|
| - elements, isolate()->factory()->fixed_array_map()));
|
| - bool is_array = (map->instance_type() == JS_ARRAY_TYPE);
|
| - HInstruction* length = NULL;
|
| - if (is_array) {
|
| - length = AddInstruction(new(zone()) HJSArrayLength(object));
|
| - } else {
|
| - length = AddInstruction(new(zone()) HFixedArrayLength(elements));
|
| - }
|
| - AddInstruction(new(zone()) HBoundsCheck(key, length));
|
| - return new(zone()) HStoreKeyedFastElement(elements, key, val);
|
| -}
|
| -
|
| -
|
| -HInstruction* HGraphBuilder::BuildStoreKeyedSpecializedArrayElement(
|
| - HValue* object,
|
| - HValue* key,
|
| - HValue* val,
|
| - Expression* expr) {
|
| - ASSERT(expr->IsMonomorphic());
|
| - AddInstruction(new(zone()) HCheckNonSmi(object));
|
| - Handle<Map> map = expr->GetMonomorphicReceiverType();
|
| - ASSERT(!map->has_fast_elements());
|
| - ASSERT(map->has_external_array_elements());
|
| - AddInstruction(new(zone()) HCheckMap(object, map));
|
| - HLoadElements* elements = new(zone()) HLoadElements(object);
|
| - AddInstruction(elements);
|
| - HInstruction* length = AddInstruction(
|
| - new(zone()) HExternalArrayLength(elements));
|
| - AddInstruction(new(zone()) HBoundsCheck(key, length));
|
| - HLoadExternalArrayPointer* external_elements =
|
| - new(zone()) HLoadExternalArrayPointer(elements);
|
| - AddInstruction(external_elements);
|
| - if (expr->external_array_type() == kExternalPixelArray) {
|
| - HClampToUint8* clamp = new(zone()) HClampToUint8(val);
|
| - AddInstruction(clamp);
|
| - val = clamp;
|
| - }
|
| - return new(zone()) HStoreKeyedSpecializedArrayElement(
|
| - external_elements,
|
| - key,
|
| - val,
|
| - expr->external_array_type());
|
| -}
|
| -
|
| -
|
| -HInstruction* HGraphBuilder::BuildStoreKeyed(HValue* object,
|
| - HValue* key,
|
| - HValue* value,
|
| - Expression* expr) {
|
| - if (expr->IsMonomorphic()) {
|
| - Handle<Map> receiver_type(expr->GetMonomorphicReceiverType());
|
| - // An object has either fast elements or external array elements, but
|
| - // never both. Pixel array maps that are assigned to pixel array elements
|
| - // are always created with the fast elements flag cleared.
|
| - if (receiver_type->has_external_array_elements()) {
|
| - return BuildStoreKeyedSpecializedArrayElement(object,
|
| - key,
|
| - value,
|
| - expr);
|
| - } else if (receiver_type->has_fast_elements()) {
|
| - return BuildStoreKeyedFastElement(object, key, value, expr);
|
| - }
|
| - }
|
| - return BuildStoreKeyedGeneric(object, key, value);
|
| -}
|
| -
|
| -
|
| bool HGraphBuilder::TryArgumentsAccess(Property* expr) {
|
| VariableProxy* proxy = expr->obj()->AsVariableProxy();
|
| if (proxy == NULL) return false;
|
| @@ -3848,6 +4136,13 @@
|
| return false;
|
| }
|
|
|
| + // Our implementation of arguments (based on this stack frame or an
|
| + // adapter below it) does not work for inlined functions.
|
| + if (function_state()->outer() != NULL) {
|
| + Bailout("arguments access in inlined function");
|
| + return true;
|
| + }
|
| +
|
| HInstruction* result = NULL;
|
| if (expr->key()->IsPropertyName()) {
|
| Handle<String> name = expr->key()->AsLiteral()->AsPropertyName();
|
| @@ -3863,8 +4158,9 @@
|
| HInstruction* elements = AddInstruction(new(zone()) HArgumentsElements);
|
| HInstruction* length = AddInstruction(
|
| new(zone()) HArgumentsLength(elements));
|
| - AddInstruction(new(zone()) HBoundsCheck(key, length));
|
| - result = new(zone()) HAccessArgumentsAt(elements, length, key);
|
| + HInstruction* checked_key =
|
| + AddInstruction(new(zone()) HBoundsCheck(key, length));
|
| + result = new(zone()) HAccessArgumentsAt(elements, length, checked_key);
|
| }
|
| ast_context()->ReturnInstruction(result, expr->id());
|
| return true;
|
| @@ -3897,9 +4193,11 @@
|
| CHECK_ALIVE(VisitForValue(expr->key()));
|
| HValue* index = Pop();
|
| HValue* string = Pop();
|
| - HStringCharCodeAt* char_code = BuildStringCharCodeAt(string, index);
|
| + HValue* context = environment()->LookupContext();
|
| + HStringCharCodeAt* char_code =
|
| + BuildStringCharCodeAt(context, string, index);
|
| AddInstruction(char_code);
|
| - instr = new(zone()) HStringCharFromCode(char_code);
|
| + instr = new(zone()) HStringCharFromCode(context, char_code);
|
|
|
| } else if (expr->IsFunctionPrototype()) {
|
| HValue* function = Pop();
|
| @@ -3915,7 +4213,8 @@
|
| instr = BuildLoadNamed(obj, expr, types->first(), name);
|
| } else if (types != NULL && types->length() > 1) {
|
| AddInstruction(new(zone()) HCheckNonSmi(obj));
|
| - instr = new(zone()) HLoadNamedFieldPolymorphic(obj, types, name);
|
| + HValue* context = environment()->LookupContext();
|
| + instr = new(zone()) HLoadNamedFieldPolymorphic(context, obj, types, name);
|
| } else {
|
| instr = BuildLoadNamedGeneric(obj, expr);
|
| }
|
| @@ -3925,10 +4224,25 @@
|
|
|
| HValue* key = Pop();
|
| HValue* obj = Pop();
|
| - instr = BuildLoadKeyed(obj, key, expr);
|
| +
|
| + bool has_side_effects = false;
|
| + HValue* load = HandleKeyedElementAccess(
|
| + obj, key, NULL, expr, expr->id(), expr->position(),
|
| + false, // is_store
|
| + &has_side_effects);
|
| + if (has_side_effects) {
|
| + if (ast_context()->IsEffect()) {
|
| + AddSimulate(expr->id());
|
| + } else {
|
| + Push(load);
|
| + AddSimulate(expr->id());
|
| + Drop(1);
|
| + }
|
| + }
|
| + return ast_context()->ReturnValue(load);
|
| }
|
| instr->set_position(expr->position());
|
| - ast_context()->ReturnInstruction(instr, expr->id());
|
| + return ast_context()->ReturnInstruction(instr, expr->id());
|
| }
|
|
|
|
|
| @@ -4016,8 +4330,7 @@
|
| if (!ast_context()->IsEffect()) Push(call);
|
| current_block()->Goto(join);
|
| } else {
|
| - ast_context()->ReturnInstruction(call, expr->id());
|
| - return;
|
| + return ast_context()->ReturnInstruction(call, expr->id());
|
| }
|
| }
|
|
|
| @@ -4028,7 +4341,7 @@
|
| if (join->HasPredecessor()) {
|
| set_current_block(join);
|
| join->SetJoinId(expr->id());
|
| - if (!ast_context()->IsEffect()) ast_context()->ReturnValue(Pop());
|
| + if (!ast_context()->IsEffect()) return ast_context()->ReturnValue(Pop());
|
| } else {
|
| set_current_block(NULL);
|
| }
|
| @@ -4140,14 +4453,6 @@
|
| return false;
|
| }
|
|
|
| - // Check if we can handle all declarations in the inlined functions.
|
| - VisitDeclarations(target_info.scope()->declarations());
|
| - if (HasStackOverflow()) {
|
| - TraceInline(target, caller, "target has non-trivial declaration");
|
| - 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();
|
| @@ -4157,6 +4462,15 @@
|
| return false;
|
| }
|
|
|
| + // All declarations must be inlineable.
|
| + ZoneList<Declaration*>* decls = target_info.scope()->declarations();
|
| + int decl_count = decls->length();
|
| + for (int i = 0; i < decl_count; ++i) {
|
| + if (!decls->at(i)->IsInlineable()) {
|
| + TraceInline(target, caller, "target has non-trivial declaration");
|
| + 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()) {
|
| @@ -4175,6 +4489,13 @@
|
| TraceInline(target, caller, "could not generate deoptimization info");
|
| return false;
|
| }
|
| + if (target_shared->scope_info() == SerializedScopeInfo::Empty()) {
|
| + // The scope info might not have been set if a lazily compiled
|
| + // function is inlined before being called for the first time.
|
| + Handle<SerializedScopeInfo> target_scope_info =
|
| + SerializedScopeInfo::Create(target_info.scope());
|
| + target_shared->set_scope_info(*target_scope_info);
|
| + }
|
| target_shared->EnableDeoptimizationSupport(*target_info.code());
|
| Compiler::RecordFunctionCompilation(Logger::FUNCTION_TAG,
|
| &target_info,
|
| @@ -4182,6 +4503,9 @@
|
| }
|
|
|
| // ----------------------------------------------------------------
|
| + // After this point, we've made a decision to inline this function (so
|
| + // TryInline should always return true).
|
| +
|
| // Save the pending call context and type feedback oracle. Set up new ones
|
| // for the inlined function.
|
| ASSERT(target_shared->has_deoptimization_support());
|
| @@ -4194,17 +4518,16 @@
|
| HEnvironment* inner_env =
|
| environment()->CopyForInlining(target,
|
| function,
|
| - HEnvironment::HYDROGEN,
|
| undefined,
|
| call_kind);
|
| HBasicBlock* body_entry = CreateBasicBlock(inner_env);
|
| current_block()->Goto(body_entry);
|
| -
|
| body_entry->SetJoinId(expr->ReturnId());
|
| set_current_block(body_entry);
|
| AddInstruction(new(zone()) HEnterInlined(target,
|
| function,
|
| call_kind));
|
| + VisitDeclarations(target_info.scope()->declarations());
|
| VisitStatements(function->body());
|
| if (HasStackOverflow()) {
|
| // Bail out if the inline function did, as we cannot residualize a call
|
| @@ -4227,7 +4550,7 @@
|
| ASSERT(function_return() != NULL);
|
| ASSERT(call_context()->IsEffect() || call_context()->IsValue());
|
| if (call_context()->IsEffect()) {
|
| - current_block()->Goto(function_return(), false);
|
| + current_block()->Goto(function_return());
|
| } else {
|
| current_block()->AddLeaveInlined(undefined, function_return());
|
| }
|
| @@ -4239,11 +4562,11 @@
|
| // TODO(3168478): refactor to avoid this.
|
| HBasicBlock* empty_true = graph()->CreateBasicBlock();
|
| HBasicBlock* empty_false = graph()->CreateBasicBlock();
|
| - HTest* test = new(zone()) HTest(undefined, empty_true, empty_false);
|
| + HBranch* test = new(zone()) HBranch(undefined, empty_true, empty_false);
|
| current_block()->Finish(test);
|
|
|
| - empty_true->Goto(inlined_test_context()->if_true(), false);
|
| - empty_false->Goto(inlined_test_context()->if_false(), false);
|
| + empty_true->Goto(inlined_test_context()->if_true());
|
| + empty_false->Goto(inlined_test_context()->if_false());
|
| }
|
| }
|
|
|
| @@ -4251,21 +4574,22 @@
|
| if (inlined_test_context() != NULL) {
|
| HBasicBlock* if_true = inlined_test_context()->if_true();
|
| HBasicBlock* if_false = inlined_test_context()->if_false();
|
| - if_true->SetJoinId(expr->id());
|
| - if_false->SetJoinId(expr->id());
|
| +
|
| + // Pop the return test context from the expression context stack.
|
| ASSERT(ast_context() == inlined_test_context());
|
| - // Pop the return test context from the expression context stack.
|
| ClearInlinedTestContext();
|
|
|
| // Forward to the real test context.
|
| - HBasicBlock* true_target = TestContext::cast(ast_context())->if_true();
|
| - HBasicBlock* false_target = TestContext::cast(ast_context())->if_false();
|
| - if_true->Goto(true_target, false);
|
| - if_false->Goto(false_target, false);
|
| -
|
| - // 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.
|
| + if (if_true->HasPredecessor()) {
|
| + if_true->SetJoinId(expr->id());
|
| + HBasicBlock* true_target = TestContext::cast(ast_context())->if_true();
|
| + if_true->Goto(true_target);
|
| + }
|
| + if (if_false->HasPredecessor()) {
|
| + if_false->SetJoinId(expr->id());
|
| + HBasicBlock* false_target = TestContext::cast(ast_context())->if_false();
|
| + if_false->Goto(false_target);
|
| + }
|
| set_current_block(NULL);
|
|
|
| } else if (function_return()->HasPredecessor()) {
|
| @@ -4294,18 +4618,20 @@
|
| if (argument_count == 2 && check_type == STRING_CHECK) {
|
| HValue* index = Pop();
|
| HValue* string = Pop();
|
| + HValue* context = environment()->LookupContext();
|
| ASSERT(!expr->holder().is_null());
|
| AddInstruction(new(zone()) HCheckPrototypeMaps(
|
| oracle()->GetPrototypeForPrimitiveCheck(STRING_CHECK),
|
| expr->holder()));
|
| - HStringCharCodeAt* char_code = BuildStringCharCodeAt(string, index);
|
| + HStringCharCodeAt* char_code =
|
| + BuildStringCharCodeAt(context, string, index);
|
| if (id == kStringCharCodeAt) {
|
| ast_context()->ReturnInstruction(char_code, expr->id());
|
| return true;
|
| }
|
| AddInstruction(char_code);
|
| HStringCharFromCode* result =
|
| - new(zone()) HStringCharFromCode(char_code);
|
| + new(zone()) HStringCharFromCode(context, char_code);
|
| ast_context()->ReturnInstruction(result, expr->id());
|
| return true;
|
| }
|
| @@ -4320,8 +4646,10 @@
|
| if (argument_count == 2 && check_type == RECEIVER_MAP_CHECK) {
|
| AddCheckConstantFunction(expr, receiver, receiver_map, true);
|
| HValue* argument = Pop();
|
| + HValue* context = environment()->LookupContext();
|
| Drop(1); // Receiver.
|
| - HUnaryMathOperation* op = new(zone()) HUnaryMathOperation(argument, id);
|
| + HUnaryMathOperation* op =
|
| + new(zone()) HUnaryMathOperation(context, argument, id);
|
| op->set_position(expr->position());
|
| ast_context()->ReturnInstruction(op, expr->id());
|
| return true;
|
| @@ -4333,31 +4661,33 @@
|
| HValue* right = Pop();
|
| HValue* left = Pop();
|
| Pop(); // Pop receiver.
|
| + HValue* context = environment()->LookupContext();
|
| HInstruction* result = NULL;
|
| // Use sqrt() if exponent is 0.5 or -0.5.
|
| if (right->IsConstant() && HConstant::cast(right)->HasDoubleValue()) {
|
| double exponent = HConstant::cast(right)->DoubleValue();
|
| if (exponent == 0.5) {
|
| - result = new(zone()) HUnaryMathOperation(left, kMathPowHalf);
|
| + result =
|
| + new(zone()) HUnaryMathOperation(context, left, kMathPowHalf);
|
| } else if (exponent == -0.5) {
|
| HConstant* double_one =
|
| new(zone()) HConstant(Handle<Object>(Smi::FromInt(1)),
|
| Representation::Double());
|
| AddInstruction(double_one);
|
| HUnaryMathOperation* square_root =
|
| - new(zone()) HUnaryMathOperation(left, kMathPowHalf);
|
| + new(zone()) HUnaryMathOperation(context, left, kMathPowHalf);
|
| AddInstruction(square_root);
|
| // MathPowHalf doesn't have side effects so there's no need for
|
| // an environment simulation here.
|
| ASSERT(!square_root->HasSideEffects());
|
| - result = new(zone()) HDiv(double_one, square_root);
|
| + result = new(zone()) HDiv(context, double_one, square_root);
|
| } else if (exponent == 2.0) {
|
| - result = new(zone()) HMul(left, left);
|
| + result = new(zone()) HMul(context, left, left);
|
| }
|
| } else if (right->IsConstant() &&
|
| HConstant::cast(right)->HasInteger32Value() &&
|
| HConstant::cast(right)->Integer32Value() == 2) {
|
| - result = new(zone()) HMul(left, left);
|
| + result = new(zone()) HMul(context, left, left);
|
| }
|
|
|
| if (result == NULL) {
|
| @@ -4396,6 +4726,13 @@
|
| if (!expr->IsMonomorphic() ||
|
| expr->check_type() != RECEIVER_MAP_CHECK) return false;
|
|
|
| + // Our implementation of arguments (based on this stack frame or an
|
| + // adapter below it) does not work for inlined functions.
|
| + if (function_state()->outer() != NULL) {
|
| + Bailout("Function.prototype.apply optimization in inlined function");
|
| + return true;
|
| + }
|
| +
|
| // Found pattern f.apply(receiver, arguments).
|
| VisitForValue(prop->obj());
|
| if (HasStackOverflow() || current_block() == NULL) return true;
|
| @@ -4429,7 +4766,7 @@
|
| if (prop != NULL) {
|
| if (!prop->key()->IsPropertyName()) {
|
| // Keyed function call.
|
| - CHECK_ALIVE(VisitForValue(prop->obj()));
|
| + CHECK_ALIVE(VisitArgument(prop->obj()));
|
|
|
| CHECK_ALIVE(VisitForValue(prop->key()));
|
| // Push receiver and key like the non-optimized code generator expects it.
|
| @@ -4438,15 +4775,13 @@
|
| Push(key);
|
| Push(receiver);
|
|
|
| - CHECK_ALIVE(VisitExpressions(expr->arguments()));
|
| + CHECK_ALIVE(VisitArgumentList(expr->arguments()));
|
|
|
| HValue* context = environment()->LookupContext();
|
| - call = PreProcessCall(
|
| - new(zone()) HCallKeyed(context, key, argument_count));
|
| + call = new(zone()) HCallKeyed(context, key, argument_count);
|
| call->set_position(expr->position());
|
| - Drop(1); // Key.
|
| - ast_context()->ReturnInstruction(call, expr->id());
|
| - return;
|
| + Drop(argument_count + 1); // 1 is the key.
|
| + return ast_context()->ReturnInstruction(call, expr->id());
|
| }
|
|
|
| // Named function call.
|
| @@ -4504,11 +4839,6 @@
|
| Variable* var = expr->expression()->AsVariableProxy()->AsVariable();
|
| bool global_call = (var != NULL) && var->is_global() && !var->is_this();
|
|
|
| - if (!global_call) {
|
| - ++argument_count;
|
| - CHECK_ALIVE(VisitForValue(expr->expression()));
|
| - }
|
| -
|
| if (global_call) {
|
| bool known_global_function = false;
|
| // If there is a global property cell for the name at compile time and
|
| @@ -4548,27 +4878,34 @@
|
| argument_count));
|
| } else {
|
| HValue* context = environment()->LookupContext();
|
| - PushAndAdd(new(zone()) HGlobalObject(context));
|
| - CHECK_ALIVE(VisitExpressions(expr->arguments()));
|
| + HGlobalObject* receiver = new(zone()) HGlobalObject(context);
|
| + AddInstruction(receiver);
|
| + PushAndAdd(new(zone()) HPushArgument(receiver));
|
| + CHECK_ALIVE(VisitArgumentList(expr->arguments()));
|
|
|
| - call = PreProcessCall(new(zone()) HCallGlobal(context,
|
| - var->name(),
|
| - argument_count));
|
| + call = new(zone()) HCallGlobal(context, var->name(), argument_count);
|
| + Drop(argument_count);
|
| }
|
|
|
| } else {
|
| + CHECK_ALIVE(VisitArgument(expr->expression()));
|
| HValue* context = environment()->LookupContext();
|
| HGlobalObject* global_object = new(zone()) HGlobalObject(context);
|
| + HGlobalReceiver* receiver = new(zone()) HGlobalReceiver(global_object);
|
| AddInstruction(global_object);
|
| - PushAndAdd(new(zone()) HGlobalReceiver(global_object));
|
| - CHECK_ALIVE(VisitExpressions(expr->arguments()));
|
| + AddInstruction(receiver);
|
| + PushAndAdd(new(zone()) HPushArgument(receiver));
|
| + CHECK_ALIVE(VisitArgumentList(expr->arguments()));
|
|
|
| - call = PreProcessCall(new(zone()) HCallFunction(context, argument_count));
|
| + // The function to call is treated as an argument to the call function
|
| + // stub.
|
| + call = new(zone()) HCallFunction(context, argument_count + 1);
|
| + Drop(argument_count + 1);
|
| }
|
| }
|
|
|
| call->set_position(expr->position());
|
| - ast_context()->ReturnInstruction(call, expr->id());
|
| + return ast_context()->ReturnInstruction(call, expr->id());
|
| }
|
|
|
|
|
| @@ -4578,19 +4915,19 @@
|
| ASSERT(current_block()->HasPredecessor());
|
| // The constructor function is also used as the receiver argument to the
|
| // JS construct call builtin.
|
| - CHECK_ALIVE(VisitForValue(expr->expression()));
|
| - CHECK_ALIVE(VisitExpressions(expr->arguments()));
|
| + HValue* constructor = NULL;
|
| + CHECK_ALIVE(constructor = VisitArgument(expr->expression()));
|
| + CHECK_ALIVE(VisitArgumentList(expr->arguments()));
|
|
|
| HValue* context = environment()->LookupContext();
|
|
|
| // The constructor is both an operand to the instruction and an argument
|
| // to the construct call.
|
| int arg_count = expr->arguments()->length() + 1; // Plus constructor.
|
| - HValue* constructor = environment()->ExpressionStackAt(arg_count - 1);
|
| HCallNew* call = new(zone()) HCallNew(context, constructor, arg_count);
|
| call->set_position(expr->position());
|
| - PreProcessCall(call);
|
| - ast_context()->ReturnInstruction(call, expr->id());
|
| + Drop(arg_count);
|
| + return ast_context()->ReturnInstruction(call, expr->id());
|
| }
|
|
|
|
|
| @@ -4636,13 +4973,14 @@
|
| ASSERT(function->intrinsic_type == Runtime::RUNTIME);
|
| CHECK_ALIVE(VisitArgumentList(expr->arguments()));
|
|
|
| + HValue* context = environment()->LookupContext();
|
| Handle<String> name = expr->name();
|
| int argument_count = expr->arguments()->length();
|
| HCallRuntime* call =
|
| - new(zone()) HCallRuntime(name, function, argument_count);
|
| + new(zone()) HCallRuntime(context, name, function, argument_count);
|
| call->set_position(RelocInfo::kNoPosition);
|
| Drop(argument_count);
|
| - ast_context()->ReturnInstruction(call, expr->id());
|
| + return ast_context()->ReturnInstruction(call, expr->id());
|
| }
|
| }
|
|
|
| @@ -4670,26 +5008,27 @@
|
| // Result of deleting non-property, non-variable reference is true.
|
| // Evaluate the subexpression for side effects.
|
| CHECK_ALIVE(VisitForEffect(expr->expression()));
|
| - ast_context()->ReturnValue(graph()->GetConstantTrue());
|
| + return ast_context()->ReturnValue(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.
|
| - ast_context()->ReturnValue(graph()->GetConstantFalse());
|
| + return ast_context()->ReturnValue(graph()->GetConstantFalse());
|
| } else if (prop != NULL) {
|
| if (prop->is_synthetic()) {
|
| // Result of deleting parameters is false, even when they rewrite
|
| // to accesses on the arguments object.
|
| - ast_context()->ReturnValue(graph()->GetConstantFalse());
|
| - } else {
|
| + return ast_context()->ReturnValue(graph()->GetConstantFalse());
|
| + } else {
|
| CHECK_ALIVE(VisitForValue(prop->obj()));
|
| CHECK_ALIVE(VisitForValue(prop->key()));
|
| HValue* key = Pop();
|
| HValue* obj = Pop();
|
| - HDeleteProperty* instr = new(zone()) HDeleteProperty(obj, key);
|
| - ast_context()->ReturnInstruction(instr, expr->id());
|
| + HValue* context = environment()->LookupContext();
|
| + HDeleteProperty* instr = new(zone()) HDeleteProperty(context, obj, key);
|
| + return ast_context()->ReturnInstruction(instr, expr->id());
|
| }
|
| } else if (var->is_global()) {
|
| Bailout("delete with global variable");
|
| @@ -4701,42 +5040,58 @@
|
|
|
| void HGraphBuilder::VisitVoid(UnaryOperation* expr) {
|
| CHECK_ALIVE(VisitForEffect(expr->expression()));
|
| - ast_context()->ReturnValue(graph()->GetConstantUndefined());
|
| + return ast_context()->ReturnValue(graph()->GetConstantUndefined());
|
| }
|
|
|
|
|
| void HGraphBuilder::VisitTypeof(UnaryOperation* expr) {
|
| CHECK_ALIVE(VisitForTypeOf(expr->expression()));
|
| HValue* value = Pop();
|
| - ast_context()->ReturnInstruction(new(zone()) HTypeof(value), expr->id());
|
| + HValue* context = environment()->LookupContext();
|
| + HInstruction* instr = new(zone()) HTypeof(context, value);
|
| + return ast_context()->ReturnInstruction(instr, expr->id());
|
| }
|
|
|
|
|
| void HGraphBuilder::VisitAdd(UnaryOperation* expr) {
|
| CHECK_ALIVE(VisitForValue(expr->expression()));
|
| HValue* value = Pop();
|
| - HInstruction* instr = new(zone()) HMul(value, graph_->GetConstant1());
|
| - ast_context()->ReturnInstruction(instr, expr->id());
|
| + HValue* context = environment()->LookupContext();
|
| + HInstruction* instr =
|
| + new(zone()) HMul(context, value, graph_->GetConstant1());
|
| + return ast_context()->ReturnInstruction(instr, expr->id());
|
| }
|
|
|
|
|
| void HGraphBuilder::VisitSub(UnaryOperation* expr) {
|
| CHECK_ALIVE(VisitForValue(expr->expression()));
|
| HValue* value = Pop();
|
| - HInstruction* instr = new(zone()) HMul(value, graph_->GetConstantMinus1());
|
| + HValue* context = environment()->LookupContext();
|
| + HInstruction* instr =
|
| + new(zone()) HMul(context, value, graph_->GetConstantMinus1());
|
| TypeInfo info = oracle()->UnaryType(expr);
|
| + if (info.IsUninitialized()) {
|
| + AddInstruction(new(zone()) HSoftDeoptimize);
|
| + current_block()->MarkAsDeoptimizing();
|
| + info = TypeInfo::Unknown();
|
| + }
|
| Representation rep = ToRepresentation(info);
|
| TraceRepresentation(expr->op(), info, instr, rep);
|
| instr->AssumeRepresentation(rep);
|
| - ast_context()->ReturnInstruction(instr, expr->id());
|
| + return ast_context()->ReturnInstruction(instr, expr->id());
|
| }
|
|
|
|
|
| void HGraphBuilder::VisitBitNot(UnaryOperation* expr) {
|
| CHECK_ALIVE(VisitForValue(expr->expression()));
|
| HValue* value = Pop();
|
| + TypeInfo info = oracle()->UnaryType(expr);
|
| + if (info.IsUninitialized()) {
|
| + AddInstruction(new(zone()) HSoftDeoptimize);
|
| + current_block()->MarkAsDeoptimizing();
|
| + }
|
| HInstruction* instr = new(zone()) HBitNot(value);
|
| - ast_context()->ReturnInstruction(instr, expr->id());
|
| + return ast_context()->ReturnInstruction(instr, expr->id());
|
| }
|
|
|
|
|
| @@ -4781,7 +5136,7 @@
|
| HBasicBlock* join =
|
| CreateJoin(materialize_false, materialize_true, expr->id());
|
| set_current_block(join);
|
| - if (join != NULL) ast_context()->ReturnValue(Pop());
|
| + if (join != NULL) return ast_context()->ReturnValue(Pop());
|
| }
|
|
|
|
|
| @@ -4810,7 +5165,8 @@
|
| HConstant* delta = (expr->op() == Token::INC)
|
| ? graph_->GetConstant1()
|
| : graph_->GetConstantMinus1();
|
| - HInstruction* instr = new(zone()) HAdd(Top(), delta);
|
| + HValue* context = environment()->LookupContext();
|
| + HInstruction* instr = new(zone()) HAdd(context, Top(), delta);
|
| TraceRepresentation(expr->op(), info, instr, rep);
|
| instr->AssumeRepresentation(rep);
|
| AddInstruction(instr);
|
| @@ -4839,6 +5195,9 @@
|
| HValue* after = NULL; // The result after incrementing or decrementing.
|
|
|
| if (var != NULL) {
|
| + if (var->mode() == Variable::CONST) {
|
| + return Bailout("unsupported count operation with const");
|
| + }
|
| // Argument of the count operation is a variable, not a property.
|
| ASSERT(prop == NULL);
|
| CHECK_ALIVE(VisitForValue(target));
|
| @@ -4855,6 +5214,20 @@
|
| } else if (var->IsStackAllocated()) {
|
| Bind(var, after);
|
| } else if (var->IsContextSlot()) {
|
| + // Bail out if we try to mutate a parameter value in a function using
|
| + // the arguments object. We do not (yet) correctly handle the
|
| + // arguments property of the function.
|
| + if (info()->scope()->arguments() != NULL) {
|
| + // Parameters will rewrite to context slots. We have no direct way
|
| + // to detect that the variable is a parameter.
|
| + int count = info()->scope()->num_parameters();
|
| + for (int i = 0; i < count; ++i) {
|
| + if (var == info()->scope()->parameter(i)) {
|
| + Bailout("assignment to parameter, function uses arguments object");
|
| + }
|
| + }
|
| + }
|
| +
|
| HValue* context = BuildContextChainWalk(var);
|
| int index = var->AsSlot()->index();
|
| HStoreContextSlot* instr =
|
| @@ -4910,16 +5283,22 @@
|
| HValue* obj = environment()->ExpressionStackAt(1);
|
| HValue* key = environment()->ExpressionStackAt(0);
|
|
|
| - HInstruction* load = BuildLoadKeyed(obj, key, prop);
|
| - PushAndAdd(load);
|
| - if (load->HasSideEffects()) AddSimulate(expr->CountId());
|
| + bool has_side_effects = false;
|
| + HValue* load = HandleKeyedElementAccess(
|
| + obj, key, NULL, prop, expr->CountId(), RelocInfo::kNoPosition,
|
| + false, // is_store
|
| + &has_side_effects);
|
| + Push(load);
|
| + if (has_side_effects) AddSimulate(expr->CountId());
|
|
|
| after = BuildIncrement(returns_original_input, expr);
|
| input = Pop();
|
|
|
| expr->RecordTypeFeedback(oracle());
|
| - HInstruction* store = BuildStoreKeyed(obj, key, after, expr);
|
| - AddInstruction(store);
|
| + HandleKeyedElementAccess(obj, key, after, expr, expr->AssignmentId(),
|
| + RelocInfo::kNoPosition,
|
| + true, // is_store
|
| + &has_side_effects);
|
|
|
| // Drop the key from the bailout environment. Overwrite the receiver
|
| // with the result of the operation, and the placeholder with the
|
| @@ -4927,43 +5306,86 @@
|
| Drop(1);
|
| environment()->SetExpressionStackAt(0, after);
|
| if (returns_original_input) environment()->SetExpressionStackAt(1, input);
|
| - if (store->HasSideEffects()) AddSimulate(expr->AssignmentId());
|
| + ASSERT(has_side_effects); // Stores always have side effects.
|
| + AddSimulate(expr->AssignmentId());
|
| }
|
| }
|
|
|
| Drop(returns_original_input ? 2 : 1);
|
| - ast_context()->ReturnValue(expr->is_postfix() ? input : after);
|
| + return ast_context()->ReturnValue(expr->is_postfix() ? input : after);
|
| }
|
|
|
|
|
| -HCompareSymbolEq* HGraphBuilder::BuildSymbolCompare(HValue* left,
|
| - HValue* right,
|
| - Token::Value op) {
|
| - ASSERT(op == Token::EQ || op == Token::EQ_STRICT);
|
| - AddInstruction(new(zone()) HCheckNonSmi(left));
|
| - AddInstruction(HCheckInstanceType::NewIsSymbol(left));
|
| - AddInstruction(new(zone()) HCheckNonSmi(right));
|
| - AddInstruction(HCheckInstanceType::NewIsSymbol(right));
|
| - return new(zone()) HCompareSymbolEq(left, right, op);
|
| -}
|
| -
|
| -
|
| -HStringCharCodeAt* HGraphBuilder::BuildStringCharCodeAt(HValue* string,
|
| +HStringCharCodeAt* HGraphBuilder::BuildStringCharCodeAt(HValue* context,
|
| + HValue* string,
|
| HValue* index) {
|
| AddInstruction(new(zone()) HCheckNonSmi(string));
|
| AddInstruction(HCheckInstanceType::NewIsString(string));
|
| HStringLength* length = new(zone()) HStringLength(string);
|
| AddInstruction(length);
|
| - AddInstruction(new(zone()) HBoundsCheck(index, length));
|
| - return new(zone()) HStringCharCodeAt(string, index);
|
| + HInstruction* checked_index =
|
| + AddInstruction(new(zone()) HBoundsCheck(index, length));
|
| + return new(zone()) HStringCharCodeAt(context, string, checked_index);
|
| }
|
|
|
|
|
| HInstruction* HGraphBuilder::BuildBinaryOperation(BinaryOperation* expr,
|
| HValue* left,
|
| HValue* right) {
|
| + HValue* context = environment()->LookupContext();
|
| TypeInfo info = oracle()->BinaryType(expr);
|
| - HInstruction* instr = BuildBinaryOperation(expr->op(), left, right, info);
|
| + if (info.IsUninitialized()) {
|
| + AddInstruction(new(zone()) HSoftDeoptimize);
|
| + current_block()->MarkAsDeoptimizing();
|
| + info = TypeInfo::Unknown();
|
| + }
|
| + HInstruction* instr = NULL;
|
| + switch (expr->op()) {
|
| + case Token::ADD:
|
| + if (info.IsString()) {
|
| + AddInstruction(new(zone()) HCheckNonSmi(left));
|
| + AddInstruction(HCheckInstanceType::NewIsString(left));
|
| + AddInstruction(new(zone()) HCheckNonSmi(right));
|
| + AddInstruction(HCheckInstanceType::NewIsString(right));
|
| + instr = new(zone()) HStringAdd(context, left, right);
|
| + } else {
|
| + instr = new(zone()) HAdd(context, left, right);
|
| + }
|
| + break;
|
| + case Token::SUB:
|
| + instr = new(zone()) HSub(context, left, right);
|
| + break;
|
| + case Token::MUL:
|
| + instr = new(zone()) HMul(context, left, right);
|
| + break;
|
| + case Token::MOD:
|
| + instr = new(zone()) HMod(context, left, right);
|
| + break;
|
| + case Token::DIV:
|
| + instr = new(zone()) HDiv(context, left, right);
|
| + break;
|
| + case Token::BIT_XOR:
|
| + instr = new(zone()) HBitXor(context, left, right);
|
| + break;
|
| + case Token::BIT_AND:
|
| + instr = new(zone()) HBitAnd(context, left, right);
|
| + break;
|
| + case Token::BIT_OR:
|
| + instr = new(zone()) HBitOr(context, left, right);
|
| + break;
|
| + case Token::SAR:
|
| + instr = new(zone()) HSar(context, left, right);
|
| + break;
|
| + case Token::SHR:
|
| + instr = new(zone()) HShr(context, left, right);
|
| + break;
|
| + case Token::SHL:
|
| + instr = new(zone()) HShl(context, left, right);
|
| + break;
|
| + default:
|
| + UNREACHABLE();
|
| + }
|
| +
|
| // 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.
|
| @@ -4983,36 +5405,6 @@
|
| }
|
|
|
|
|
| -HInstruction* HGraphBuilder::BuildBinaryOperation(
|
| - Token::Value op, HValue* left, HValue* right, TypeInfo info) {
|
| - switch (op) {
|
| - case Token::ADD:
|
| - if (info.IsString()) {
|
| - AddInstruction(new(zone()) HCheckNonSmi(left));
|
| - AddInstruction(HCheckInstanceType::NewIsString(left));
|
| - AddInstruction(new(zone()) HCheckNonSmi(right));
|
| - AddInstruction(HCheckInstanceType::NewIsString(right));
|
| - return new(zone()) HStringAdd(left, right);
|
| - } else {
|
| - return new(zone()) HAdd(left, right);
|
| - }
|
| - case Token::SUB: return new(zone()) HSub(left, right);
|
| - case Token::MUL: return new(zone()) HMul(left, right);
|
| - case Token::MOD: return new(zone()) HMod(left, right);
|
| - case Token::DIV: return new(zone()) HDiv(left, right);
|
| - case Token::BIT_XOR: return new(zone()) HBitXor(left, right);
|
| - case Token::BIT_AND: return new(zone()) HBitAnd(left, right);
|
| - case Token::BIT_OR: return new(zone()) HBitOr(left, right);
|
| - case Token::SAR: return new(zone()) HSar(left, right);
|
| - case Token::SHR: return new(zone()) HShr(left, right);
|
| - case Token::SHL: return new(zone()) HShl(left, right);
|
| - default:
|
| - UNREACHABLE();
|
| - return NULL;
|
| - }
|
| -}
|
| -
|
| -
|
| // Check for the form (%_ClassOf(foo) === 'BarClass').
|
| static bool IsClassOfTest(CompareOperation* expr) {
|
| if (expr->op() != Token::EQ_STRICT) return false;
|
| @@ -5032,10 +5424,13 @@
|
| ASSERT(current_block() != NULL);
|
| ASSERT(current_block()->HasPredecessor());
|
| switch (expr->op()) {
|
| - case Token::COMMA: return VisitComma(expr);
|
| - case Token::OR: return VisitAndOr(expr, false);
|
| - case Token::AND: return VisitAndOr(expr, true);
|
| - default: return VisitCommon(expr);
|
| + case Token::COMMA:
|
| + return VisitComma(expr);
|
| + case Token::OR:
|
| + case Token::AND:
|
| + return VisitLogicalExpression(expr);
|
| + default:
|
| + return VisitArithmeticExpression(expr);
|
| }
|
| }
|
|
|
| @@ -5048,7 +5443,8 @@
|
| }
|
|
|
|
|
| -void HGraphBuilder::VisitAndOr(BinaryOperation* expr, bool is_logical_and) {
|
| +void HGraphBuilder::VisitLogicalExpression(BinaryOperation* expr) {
|
| + bool is_logical_and = expr->op() == Token::AND;
|
| if (ast_context()->IsTest()) {
|
| TestContext* context = TestContext::cast(ast_context());
|
| // Translate left subexpression.
|
| @@ -5078,9 +5474,9 @@
|
| // We need an extra block to maintain edge-split form.
|
| HBasicBlock* empty_block = graph()->CreateBasicBlock();
|
| HBasicBlock* eval_right = graph()->CreateBasicBlock();
|
| - HTest* test = is_logical_and
|
| - ? new(zone()) HTest(Top(), eval_right, empty_block)
|
| - : new(zone()) HTest(Top(), empty_block, eval_right);
|
| + HBranch* test = is_logical_and
|
| + ? new(zone()) HBranch(Top(), eval_right, empty_block)
|
| + : new(zone()) HBranch(Top(), empty_block, eval_right);
|
| current_block()->Finish(test);
|
|
|
| set_current_block(eval_right);
|
| @@ -5090,7 +5486,7 @@
|
| HBasicBlock* join_block =
|
| CreateJoin(empty_block, current_block(), expr->id());
|
| set_current_block(join_block);
|
| - ast_context()->ReturnValue(Pop());
|
| + return ast_context()->ReturnValue(Pop());
|
|
|
| } else {
|
| ASSERT(ast_context()->IsEffect());
|
| @@ -5135,14 +5531,14 @@
|
| }
|
|
|
|
|
| -void HGraphBuilder::VisitCommon(BinaryOperation* expr) {
|
| +void HGraphBuilder::VisitArithmeticExpression(BinaryOperation* expr) {
|
| CHECK_ALIVE(VisitForValue(expr->left()));
|
| CHECK_ALIVE(VisitForValue(expr->right()));
|
| HValue* right = Pop();
|
| HValue* left = Pop();
|
| HInstruction* instr = BuildBinaryOperation(expr, left, right);
|
| instr->set_position(expr->position());
|
| - ast_context()->ReturnInstruction(instr, expr->id());
|
| + return ast_context()->ReturnInstruction(instr, expr->id());
|
| }
|
|
|
|
|
| @@ -5175,46 +5571,75 @@
|
| }
|
|
|
|
|
| +void HGraphBuilder::HandleLiteralCompareTypeof(CompareOperation* compare_expr,
|
| + Expression* expr,
|
| + Handle<String> check) {
|
| + CHECK_ALIVE(VisitForTypeOf(expr));
|
| + HValue* expr_value = Pop();
|
| + HTypeofIsAndBranch* instr = new(zone()) HTypeofIsAndBranch(expr_value, check);
|
| + instr->set_position(compare_expr->position());
|
| + return ast_context()->ReturnControl(instr, compare_expr->id());
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::HandleLiteralCompareUndefined(
|
| + CompareOperation* compare_expr, Expression* expr) {
|
| + CHECK_ALIVE(VisitForValue(expr));
|
| + HValue* lhs = Pop();
|
| + HValue* rhs = graph()->GetConstantUndefined();
|
| + HCompareObjectEqAndBranch* instr =
|
| + new(zone()) HCompareObjectEqAndBranch(lhs, rhs);
|
| + instr->set_position(compare_expr->position());
|
| + return ast_context()->ReturnControl(instr, compare_expr->id());
|
| +}
|
| +
|
| +
|
| void HGraphBuilder::VisitCompareOperation(CompareOperation* expr) {
|
| ASSERT(!HasStackOverflow());
|
| ASSERT(current_block() != NULL);
|
| ASSERT(current_block()->HasPredecessor());
|
| if (IsClassOfTest(expr)) {
|
| CallRuntime* call = expr->left()->AsCallRuntime();
|
| + ASSERT(call->arguments()->length() == 1);
|
| CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
|
| HValue* value = Pop();
|
| Literal* literal = expr->right()->AsLiteral();
|
| Handle<String> rhs = Handle<String>::cast(literal->handle());
|
| - HInstruction* instr = new(zone()) HClassOfTest(value, rhs);
|
| + HClassOfTestAndBranch* instr =
|
| + new(zone()) HClassOfTestAndBranch(value, rhs);
|
| instr->set_position(expr->position());
|
| - ast_context()->ReturnInstruction(instr, expr->id());
|
| + return ast_context()->ReturnControl(instr, expr->id());
|
| + }
|
| +
|
| + // Check for special cases that compare against literals.
|
| + Expression *sub_expr;
|
| + Handle<String> check;
|
| + if (expr->IsLiteralCompareTypeof(&sub_expr, &check)) {
|
| + HandleLiteralCompareTypeof(expr, sub_expr, check);
|
| 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()) {
|
| - CHECK_ALIVE(VisitForTypeOf(left_unary->expression()));
|
| - HValue* left = Pop();
|
| - HInstruction* instr = new(zone()) HTypeofIs(left,
|
| - Handle<String>::cast(right_literal->handle()));
|
| - instr->set_position(expr->position());
|
| - ast_context()->ReturnInstruction(instr, expr->id());
|
| + if (expr->IsLiteralCompareUndefined(&sub_expr)) {
|
| + HandleLiteralCompareUndefined(expr, sub_expr);
|
| return;
|
| }
|
|
|
| + TypeInfo type_info = oracle()->CompareType(expr);
|
| + // Check if this expression was ever executed according to type feedback.
|
| + if (type_info.IsUninitialized()) {
|
| + AddInstruction(new(zone()) HSoftDeoptimize);
|
| + current_block()->MarkAsDeoptimizing();
|
| + type_info = TypeInfo::Unknown();
|
| + }
|
| +
|
| CHECK_ALIVE(VisitForValue(expr->left()));
|
| CHECK_ALIVE(VisitForValue(expr->right()));
|
|
|
| + HValue* context = environment()->LookupContext();
|
| HValue* right = Pop();
|
| HValue* left = Pop();
|
| Token::Value op = expr->op();
|
|
|
| - TypeInfo type_info = oracle()->CompareType(expr);
|
| - HInstruction* instr = NULL;
|
| if (op == Token::INSTANCEOF) {
|
| // Check to see if the rhs of the instanceof is a global function not
|
| // residing in new space. If it is we assume that the function will stay the
|
| @@ -5244,40 +5669,61 @@
|
| // If the target is not null we have found a known global function that is
|
| // assumed to stay the same for this instanceof.
|
| if (target.is_null()) {
|
| - HValue* context = environment()->LookupContext();
|
| - instr = new(zone()) HInstanceOf(context, left, right);
|
| + HInstanceOf* result = new(zone()) HInstanceOf(context, left, right);
|
| + result->set_position(expr->position());
|
| + return ast_context()->ReturnInstruction(result, expr->id());
|
| } else {
|
| AddInstruction(new(zone()) HCheckFunction(right, target));
|
| - instr = new(zone()) HInstanceOfKnownGlobal(left, target);
|
| + HInstanceOfKnownGlobal* result =
|
| + new(zone()) HInstanceOfKnownGlobal(context, left, target);
|
| + result->set_position(expr->position());
|
| + return ast_context()->ReturnInstruction(result, expr->id());
|
| }
|
| } else if (op == Token::IN) {
|
| - instr = new(zone()) HIn(left, right);
|
| + HIn* result = new(zone()) HIn(context, left, right);
|
| + result->set_position(expr->position());
|
| + return ast_context()->ReturnInstruction(result, expr->id());
|
| } else if (type_info.IsNonPrimitive()) {
|
| switch (op) {
|
| case Token::EQ:
|
| case Token::EQ_STRICT: {
|
| AddInstruction(new(zone()) HCheckNonSmi(left));
|
| - AddInstruction(HCheckInstanceType::NewIsJSObjectOrJSFunction(left));
|
| + AddInstruction(HCheckInstanceType::NewIsSpecObject(left));
|
| AddInstruction(new(zone()) HCheckNonSmi(right));
|
| - AddInstruction(HCheckInstanceType::NewIsJSObjectOrJSFunction(right));
|
| - instr = new(zone()) HCompareJSObjectEq(left, right);
|
| - break;
|
| + AddInstruction(HCheckInstanceType::NewIsSpecObject(right));
|
| + HCompareObjectEqAndBranch* result =
|
| + new(zone()) HCompareObjectEqAndBranch(left, right);
|
| + result->set_position(expr->position());
|
| + return ast_context()->ReturnControl(result, expr->id());
|
| }
|
| default:
|
| return Bailout("Unsupported non-primitive compare");
|
| - break;
|
| }
|
| } else if (type_info.IsString() && oracle()->IsSymbolCompare(expr) &&
|
| (op == Token::EQ || op == Token::EQ_STRICT)) {
|
| - instr = BuildSymbolCompare(left, right, op);
|
| + AddInstruction(new(zone()) HCheckNonSmi(left));
|
| + AddInstruction(HCheckInstanceType::NewIsSymbol(left));
|
| + AddInstruction(new(zone()) HCheckNonSmi(right));
|
| + AddInstruction(HCheckInstanceType::NewIsSymbol(right));
|
| + HCompareObjectEqAndBranch* result =
|
| + new(zone()) HCompareObjectEqAndBranch(left, right);
|
| + result->set_position(expr->position());
|
| + return ast_context()->ReturnControl(result, expr->id());
|
| } else {
|
| - HCompare* compare = new(zone()) HCompare(left, right, op);
|
| Representation r = ToRepresentation(type_info);
|
| - compare->SetInputRepresentation(r);
|
| - instr = compare;
|
| + if (r.IsTagged()) {
|
| + HCompareGeneric* result =
|
| + new(zone()) HCompareGeneric(context, left, right, op);
|
| + result->set_position(expr->position());
|
| + return ast_context()->ReturnInstruction(result, expr->id());
|
| + } else {
|
| + HCompareIDAndBranch* result =
|
| + new(zone()) HCompareIDAndBranch(left, right, op);
|
| + result->set_position(expr->position());
|
| + result->SetInputRepresentation(r);
|
| + return ast_context()->ReturnControl(result, expr->id());
|
| + }
|
| }
|
| - instr->set_position(expr->position());
|
| - ast_context()->ReturnInstruction(instr, expr->id());
|
| }
|
|
|
|
|
| @@ -5286,10 +5732,10 @@
|
| ASSERT(current_block() != NULL);
|
| ASSERT(current_block()->HasPredecessor());
|
| CHECK_ALIVE(VisitForValue(expr->expression()));
|
| -
|
| HValue* value = Pop();
|
| - HIsNull* compare = new(zone()) HIsNull(value, expr->is_strict());
|
| - ast_context()->ReturnInstruction(compare, expr->id());
|
| + HIsNullAndBranch* instr =
|
| + new(zone()) HIsNullAndBranch(value, expr->is_strict());
|
| + return ast_context()->ReturnControl(instr, expr->id());
|
| }
|
|
|
|
|
| @@ -5297,23 +5743,22 @@
|
| ASSERT(!HasStackOverflow());
|
| ASSERT(current_block() != NULL);
|
| ASSERT(current_block()->HasPredecessor());
|
| - return Bailout("ThisFunction");
|
| + HThisFunction* self = new(zone()) HThisFunction;
|
| + return ast_context()->ReturnInstruction(self, expr->id());
|
| }
|
|
|
|
|
| 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.
|
| + // We support only declarations that do not require code generation.
|
| 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) {
|
| + if (!var->IsStackAllocated() || decl->fun() != NULL) {
|
| return Bailout("unsupported declaration");
|
| }
|
| +
|
| + if (decl->mode() == Variable::CONST) {
|
| + ASSERT(var->IsStackAllocated());
|
| + environment()->Bind(var, graph()->GetConstantHole());
|
| + }
|
| }
|
|
|
|
|
| @@ -5323,8 +5768,8 @@
|
| ASSERT(call->arguments()->length() == 1);
|
| CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
|
| HValue* value = Pop();
|
| - HIsSmi* result = new(zone()) HIsSmi(value);
|
| - ast_context()->ReturnInstruction(result, call->id());
|
| + HIsSmiAndBranch* result = new(zone()) HIsSmiAndBranch(value);
|
| + return ast_context()->ReturnControl(result, call->id());
|
| }
|
|
|
|
|
| @@ -5332,9 +5777,11 @@
|
| ASSERT(call->arguments()->length() == 1);
|
| CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
|
| HValue* value = Pop();
|
| - HHasInstanceType* result =
|
| - new(zone()) HHasInstanceType(value, FIRST_JS_OBJECT_TYPE, LAST_TYPE);
|
| - ast_context()->ReturnInstruction(result, call->id());
|
| + HHasInstanceTypeAndBranch* result =
|
| + new(zone()) HHasInstanceTypeAndBranch(value,
|
| + FIRST_SPEC_OBJECT_TYPE,
|
| + LAST_SPEC_OBJECT_TYPE);
|
| + return ast_context()->ReturnControl(result, call->id());
|
| }
|
|
|
|
|
| @@ -5342,9 +5789,9 @@
|
| ASSERT(call->arguments()->length() == 1);
|
| CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
|
| HValue* value = Pop();
|
| - HHasInstanceType* result =
|
| - new(zone()) HHasInstanceType(value, JS_FUNCTION_TYPE);
|
| - ast_context()->ReturnInstruction(result, call->id());
|
| + HHasInstanceTypeAndBranch* result =
|
| + new(zone()) HHasInstanceTypeAndBranch(value, JS_FUNCTION_TYPE);
|
| + return ast_context()->ReturnControl(result, call->id());
|
| }
|
|
|
|
|
| @@ -5352,8 +5799,9 @@
|
| ASSERT(call->arguments()->length() == 1);
|
| CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
|
| HValue* value = Pop();
|
| - HHasCachedArrayIndex* result = new(zone()) HHasCachedArrayIndex(value);
|
| - ast_context()->ReturnInstruction(result, call->id());
|
| + HHasCachedArrayIndexAndBranch* result =
|
| + new(zone()) HHasCachedArrayIndexAndBranch(value);
|
| + return ast_context()->ReturnControl(result, call->id());
|
| }
|
|
|
|
|
| @@ -5361,8 +5809,9 @@
|
| ASSERT(call->arguments()->length() == 1);
|
| CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
|
| HValue* value = Pop();
|
| - HHasInstanceType* result = new(zone()) HHasInstanceType(value, JS_ARRAY_TYPE);
|
| - ast_context()->ReturnInstruction(result, call->id());
|
| + HHasInstanceTypeAndBranch* result =
|
| + new(zone()) HHasInstanceTypeAndBranch(value, JS_ARRAY_TYPE);
|
| + return ast_context()->ReturnControl(result, call->id());
|
| }
|
|
|
|
|
| @@ -5370,9 +5819,9 @@
|
| ASSERT(call->arguments()->length() == 1);
|
| CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
|
| HValue* value = Pop();
|
| - HHasInstanceType* result =
|
| - new(zone()) HHasInstanceType(value, JS_REGEXP_TYPE);
|
| - ast_context()->ReturnInstruction(result, call->id());
|
| + HHasInstanceTypeAndBranch* result =
|
| + new(zone()) HHasInstanceTypeAndBranch(value, JS_REGEXP_TYPE);
|
| + return ast_context()->ReturnControl(result, call->id());
|
| }
|
|
|
|
|
| @@ -5380,8 +5829,8 @@
|
| ASSERT(call->arguments()->length() == 1);
|
| CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
|
| HValue* value = Pop();
|
| - HIsObject* test = new(zone()) HIsObject(value);
|
| - ast_context()->ReturnInstruction(test, call->id());
|
| + HIsObjectAndBranch* result = new(zone()) HIsObjectAndBranch(value);
|
| + return ast_context()->ReturnControl(result, call->id());
|
| }
|
|
|
|
|
| @@ -5394,8 +5843,9 @@
|
| ASSERT(call->arguments()->length() == 1);
|
| CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
|
| HValue* value = Pop();
|
| - ast_context()->ReturnInstruction(new(zone()) HIsUndetectable(value),
|
| - call->id());
|
| + HIsUndetectableAndBranch* result =
|
| + new(zone()) HIsUndetectableAndBranch(value);
|
| + return ast_context()->ReturnControl(result, call->id());
|
| }
|
|
|
|
|
| @@ -5413,23 +5863,32 @@
|
| // We are generating graph for inlined function. Currently
|
| // constructor inlining is not supported and we can just return
|
| // false from %_IsConstructCall().
|
| - ast_context()->ReturnValue(graph()->GetConstantFalse());
|
| + return ast_context()->ReturnValue(graph()->GetConstantFalse());
|
| } else {
|
| - ast_context()->ReturnInstruction(new(zone()) HIsConstructCall, call->id());
|
| + return ast_context()->ReturnControl(new(zone()) HIsConstructCallAndBranch,
|
| + call->id());
|
| }
|
| }
|
|
|
|
|
| // Support for arguments.length and arguments[?].
|
| void HGraphBuilder::GenerateArgumentsLength(CallRuntime* call) {
|
| + // Our implementation of arguments (based on this stack frame or an
|
| + // adapter below it) does not work for inlined functions. This runtime
|
| + // function is blacklisted by AstNode::IsInlineable.
|
| + ASSERT(function_state()->outer() == NULL);
|
| ASSERT(call->arguments()->length() == 0);
|
| HInstruction* elements = AddInstruction(new(zone()) HArgumentsElements);
|
| HArgumentsLength* result = new(zone()) HArgumentsLength(elements);
|
| - ast_context()->ReturnInstruction(result, call->id());
|
| + return ast_context()->ReturnInstruction(result, call->id());
|
| }
|
|
|
|
|
| void HGraphBuilder::GenerateArguments(CallRuntime* call) {
|
| + // Our implementation of arguments (based on this stack frame or an
|
| + // adapter below it) does not work for inlined functions. This runtime
|
| + // function is blacklisted by AstNode::IsInlineable.
|
| + ASSERT(function_state()->outer() == NULL);
|
| ASSERT(call->arguments()->length() == 1);
|
| CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
|
| HValue* index = Pop();
|
| @@ -5437,7 +5896,7 @@
|
| HInstruction* length = AddInstruction(new(zone()) HArgumentsLength(elements));
|
| HAccessArgumentsAt* result =
|
| new(zone()) HAccessArgumentsAt(elements, length, index);
|
| - ast_context()->ReturnInstruction(result, call->id());
|
| + return ast_context()->ReturnInstruction(result, call->id());
|
| }
|
|
|
|
|
| @@ -5454,7 +5913,7 @@
|
| CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
|
| HValue* value = Pop();
|
| HValueOf* result = new(zone()) HValueOf(value);
|
| - ast_context()->ReturnInstruction(result, call->id());
|
| + return ast_context()->ReturnInstruction(result, call->id());
|
| }
|
|
|
|
|
| @@ -5470,8 +5929,9 @@
|
| CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
|
| HValue* index = Pop();
|
| HValue* string = Pop();
|
| - HStringCharCodeAt* result = BuildStringCharCodeAt(string, index);
|
| - ast_context()->ReturnInstruction(result, call->id());
|
| + HValue* context = environment()->LookupContext();
|
| + HStringCharCodeAt* result = BuildStringCharCodeAt(context, string, index);
|
| + return ast_context()->ReturnInstruction(result, call->id());
|
| }
|
|
|
|
|
| @@ -5480,8 +5940,10 @@
|
| ASSERT(call->arguments()->length() == 1);
|
| CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
|
| HValue* char_code = Pop();
|
| - HStringCharFromCode* result = new(zone()) HStringCharFromCode(char_code);
|
| - ast_context()->ReturnInstruction(result, call->id());
|
| + HValue* context = environment()->LookupContext();
|
| + HStringCharFromCode* result =
|
| + new(zone()) HStringCharFromCode(context, char_code);
|
| + return ast_context()->ReturnInstruction(result, call->id());
|
| }
|
|
|
|
|
| @@ -5492,10 +5954,12 @@
|
| CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
|
| HValue* index = Pop();
|
| HValue* string = Pop();
|
| - HStringCharCodeAt* char_code = BuildStringCharCodeAt(string, index);
|
| + HValue* context = environment()->LookupContext();
|
| + HStringCharCodeAt* char_code = BuildStringCharCodeAt(context, string, index);
|
| AddInstruction(char_code);
|
| - HStringCharFromCode* result = new(zone()) HStringCharFromCode(char_code);
|
| - ast_context()->ReturnInstruction(result, call->id());
|
| + HStringCharFromCode* result =
|
| + new(zone()) HStringCharFromCode(context, char_code);
|
| + return ast_context()->ReturnInstruction(result, call->id());
|
| }
|
|
|
|
|
| @@ -5506,14 +5970,15 @@
|
| CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
|
| HValue* right = Pop();
|
| HValue* left = Pop();
|
| - HCompareJSObjectEq* result = new(zone()) HCompareJSObjectEq(left, right);
|
| - ast_context()->ReturnInstruction(result, call->id());
|
| + HCompareObjectEqAndBranch* result =
|
| + new(zone()) HCompareObjectEqAndBranch(left, right);
|
| + return ast_context()->ReturnControl(result, call->id());
|
| }
|
|
|
|
|
| void HGraphBuilder::GenerateLog(CallRuntime* call) {
|
| // %_Log is ignored in optimized code.
|
| - ast_context()->ReturnValue(graph()->GetConstantUndefined());
|
| + return ast_context()->ReturnValue(graph()->GetConstantUndefined());
|
| }
|
|
|
|
|
| @@ -5530,7 +5995,7 @@
|
| HValue* context = environment()->LookupContext();
|
| HCallStub* result = new(zone()) HCallStub(context, CodeStub::StringAdd, 2);
|
| Drop(2);
|
| - ast_context()->ReturnInstruction(result, call->id());
|
| + return ast_context()->ReturnInstruction(result, call->id());
|
| }
|
|
|
|
|
| @@ -5541,7 +6006,7 @@
|
| HValue* context = environment()->LookupContext();
|
| HCallStub* result = new(zone()) HCallStub(context, CodeStub::SubString, 3);
|
| Drop(3);
|
| - ast_context()->ReturnInstruction(result, call->id());
|
| + return ast_context()->ReturnInstruction(result, call->id());
|
| }
|
|
|
|
|
| @@ -5553,7 +6018,7 @@
|
| HCallStub* result =
|
| new(zone()) HCallStub(context, CodeStub::StringCompare, 2);
|
| Drop(2);
|
| - ast_context()->ReturnInstruction(result, call->id());
|
| + return ast_context()->ReturnInstruction(result, call->id());
|
| }
|
|
|
|
|
| @@ -5564,7 +6029,7 @@
|
| HValue* context = environment()->LookupContext();
|
| HCallStub* result = new(zone()) HCallStub(context, CodeStub::RegExpExec, 4);
|
| Drop(4);
|
| - ast_context()->ReturnInstruction(result, call->id());
|
| + return ast_context()->ReturnInstruction(result, call->id());
|
| }
|
|
|
|
|
| @@ -5576,7 +6041,7 @@
|
| HCallStub* result =
|
| new(zone()) HCallStub(context, CodeStub::RegExpConstructResult, 3);
|
| Drop(3);
|
| - ast_context()->ReturnInstruction(result, call->id());
|
| + return ast_context()->ReturnInstruction(result, call->id());
|
| }
|
|
|
|
|
| @@ -5594,7 +6059,7 @@
|
| HCallStub* result =
|
| new(zone()) HCallStub(context, CodeStub::NumberToString, 1);
|
| Drop(1);
|
| - ast_context()->ReturnInstruction(result, call->id());
|
| + return ast_context()->ReturnInstruction(result, call->id());
|
| }
|
|
|
|
|
| @@ -5621,7 +6086,7 @@
|
| HInvokeFunction* result =
|
| new(zone()) HInvokeFunction(context, function, arg_count);
|
| Drop(arg_count);
|
| - ast_context()->ReturnInstruction(result, call->id());
|
| + return ast_context()->ReturnInstruction(result, call->id());
|
| }
|
|
|
|
|
| @@ -5633,7 +6098,7 @@
|
| HValue* right = Pop();
|
| HValue* left = Pop();
|
| HPower* result = new(zone()) HPower(left, right);
|
| - ast_context()->ReturnInstruction(result, call->id());
|
| + return ast_context()->ReturnInstruction(result, call->id());
|
| }
|
|
|
|
|
| @@ -5645,7 +6110,7 @@
|
| new(zone()) HCallStub(context, CodeStub::TranscendentalCache, 1);
|
| result->set_transcendental_type(TranscendentalCache::SIN);
|
| Drop(1);
|
| - ast_context()->ReturnInstruction(result, call->id());
|
| + return ast_context()->ReturnInstruction(result, call->id());
|
| }
|
|
|
|
|
| @@ -5657,7 +6122,7 @@
|
| new(zone()) HCallStub(context, CodeStub::TranscendentalCache, 1);
|
| result->set_transcendental_type(TranscendentalCache::COS);
|
| Drop(1);
|
| - ast_context()->ReturnInstruction(result, call->id());
|
| + return ast_context()->ReturnInstruction(result, call->id());
|
| }
|
|
|
|
|
| @@ -5669,7 +6134,7 @@
|
| new(zone()) HCallStub(context, CodeStub::TranscendentalCache, 1);
|
| result->set_transcendental_type(TranscendentalCache::LOG);
|
| Drop(1);
|
| - ast_context()->ReturnInstruction(result, call->id());
|
| + return ast_context()->ReturnInstruction(result, call->id());
|
| }
|
|
|
|
|
| @@ -5689,7 +6154,7 @@
|
| CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
|
| HValue* value = Pop();
|
| HGetCachedArrayIndex* result = new(zone()) HGetCachedArrayIndex(value);
|
| - ast_context()->ReturnInstruction(result, call->id());
|
| + return ast_context()->ReturnInstruction(result, call->id());
|
| }
|
|
|
|
|
| @@ -5698,6 +6163,11 @@
|
| }
|
|
|
|
|
| +void HGraphBuilder::GenerateIsNativeOrStrictMode(CallRuntime* call) {
|
| + return Bailout("inlined runtime function: IsNativeOrStrictMode");
|
| +}
|
| +
|
| +
|
| #undef CHECK_BAILOUT
|
| #undef CHECK_ALIVE
|
|
|
| @@ -5860,7 +6330,6 @@
|
| HEnvironment* HEnvironment::CopyForInlining(
|
| Handle<JSFunction> target,
|
| FunctionLiteral* function,
|
| - CompilationPhase compilation_phase,
|
| HConstant* undefined,
|
| CallKind call_kind) const {
|
| // Outer environment is a copy of this one without the arguments.
|
| @@ -5872,22 +6341,15 @@
|
| HEnvironment* inner =
|
| new(zone) HEnvironment(outer, function->scope(), target);
|
| // Get the argument values from the original environment.
|
| - if (compilation_phase == HYDROGEN) {
|
| - for (int i = 0; i <= arity; ++i) { // Include receiver.
|
| - HValue* push = ExpressionStackAt(arity - i);
|
| - inner->SetValueAt(i, push);
|
| - }
|
| - } else {
|
| - ASSERT(compilation_phase == LITHIUM);
|
| - for (int i = 0; i <= arity; ++i) { // Include receiver.
|
| - HValue* push = ExpressionStackAt(arity - i);
|
| - inner->SetValueAt(i, push);
|
| - }
|
| + for (int i = 0; i <= arity; ++i) { // Include receiver.
|
| + HValue* push = ExpressionStackAt(arity - i);
|
| + inner->SetValueAt(i, push);
|
| }
|
| - // If the function we are inlining is a strict mode function, pass
|
| - // undefined as the receiver for function calls (instead of the
|
| - // global receiver).
|
| - if (function->strict_mode() && call_kind == CALL_AS_FUNCTION) {
|
| + // If the function we are inlining is a strict mode function or a
|
| + // builtin function, pass undefined as the receiver for function
|
| + // calls (instead of the global receiver).
|
| + if ((target->shared()->native() || function->strict_mode()) &&
|
| + call_kind == CALL_AS_FUNCTION) {
|
| inner->SetValueAt(0, undefined);
|
| }
|
| inner->SetValueAt(arity + 1, outer->LookupContext());
|
| @@ -5969,15 +6431,15 @@
|
| PrintEmptyProperty("predecessors");
|
| }
|
|
|
| - if (current->end() == NULL || current->end()->FirstSuccessor() == NULL) {
|
| + if (current->end()->SuccessorCount() == 0) {
|
| 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());
|
| + } else {
|
| + PrintIndent();
|
| + trace_.Add("successors");
|
| + for (HSuccessorIterator it(current->end()); !it.Done(); it.Advance()) {
|
| + trace_.Add(" \"B%d\"", it.Current()->block_id());
|
| + }
|
| + trace_.Add("\n");
|
| }
|
|
|
| PrintEmptyProperty("xhandlers");
|
|
|