diff options
author | Sergio Ahumada <sergio.ahumada@digia.com> | 2013-03-19 09:25:14 +0100 |
---|---|---|
committer | Sergio Ahumada <sergio.ahumada@digia.com> | 2013-03-19 09:56:31 +0100 |
commit | 6313e1fe4c27755adde87e62db1c2f9fac534ae4 (patch) | |
tree | c57bb29f65e02fbfcc07895a8cc2903fff9300ba /src/3rdparty/v8/src/hydrogen.cc | |
parent | b5a49a260d03249c386f1b63c249089383dd81fa (diff) | |
parent | cac65e7a222b848a735a974b0aeb43209b0cfa18 (diff) | |
download | qtjsbackend-6313e1fe4c27755adde87e62db1c2f9fac534ae4.tar.gz |
Merge branch 'dev' into stable
This starts Qt 5.1 release cycle
Change-Id: I892bbc73c276842894a720f761ce31ad1b015672
Diffstat (limited to 'src/3rdparty/v8/src/hydrogen.cc')
-rw-r--r-- | src/3rdparty/v8/src/hydrogen.cc | 2942 |
1 files changed, 2008 insertions, 934 deletions
diff --git a/src/3rdparty/v8/src/hydrogen.cc b/src/3rdparty/v8/src/hydrogen.cc index cfe80d2..043d567 100644 --- a/src/3rdparty/v8/src/hydrogen.cc +++ b/src/3rdparty/v8/src/hydrogen.cc @@ -55,19 +55,19 @@ namespace internal { HBasicBlock::HBasicBlock(HGraph* graph) : block_id_(graph->GetNextBlockID()), graph_(graph), - phis_(4), + phis_(4, graph->zone()), first_(NULL), last_(NULL), end_(NULL), loop_information_(NULL), - predecessors_(2), + predecessors_(2, graph->zone()), dominator_(NULL), - dominated_blocks_(4), + dominated_blocks_(4, graph->zone()), last_environment_(NULL), argument_count_(-1), first_instruction_index_(-1), last_instruction_index_(-1), - deleted_phis_(4), + deleted_phis_(4, graph->zone()), parent_loop_header_(NULL), is_inline_return_target_(false), is_deoptimizing_(false), @@ -76,7 +76,7 @@ HBasicBlock::HBasicBlock(HGraph* graph) void HBasicBlock::AttachLoopInformation() { ASSERT(!IsLoopHeader()); - loop_information_ = new(zone()) HLoopInformation(this); + loop_information_ = new(zone()) HLoopInformation(this, zone()); } @@ -88,7 +88,7 @@ void HBasicBlock::DetachLoopInformation() { void HBasicBlock::AddPhi(HPhi* phi) { ASSERT(!IsStartBlock()); - phis_.Add(phi); + phis_.Add(phi, zone()); phi->SetBlock(this); } @@ -119,29 +119,30 @@ void HBasicBlock::AddInstruction(HInstruction* instr) { HDeoptimize* HBasicBlock::CreateDeoptimize( HDeoptimize::UseEnvironment has_uses) { ASSERT(HasEnvironment()); - if (has_uses == HDeoptimize::kNoUses) return new(zone()) HDeoptimize(0); + if (has_uses == HDeoptimize::kNoUses) + return new(zone()) HDeoptimize(0, zone()); HEnvironment* environment = last_environment(); - HDeoptimize* instr = new(zone()) HDeoptimize(environment->length()); + HDeoptimize* instr = new(zone()) HDeoptimize(environment->length(), zone()); for (int i = 0; i < environment->length(); i++) { HValue* val = environment->values()->at(i); - instr->AddEnvironmentValue(val); + instr->AddEnvironmentValue(val, zone()); } return instr; } -HSimulate* HBasicBlock::CreateSimulate(int ast_id) { +HSimulate* HBasicBlock::CreateSimulate(BailoutId ast_id) { ASSERT(HasEnvironment()); HEnvironment* environment = last_environment(); - ASSERT(ast_id == AstNode::kNoNumber || + ASSERT(ast_id.IsNone() || environment->closure()->shared()->VerifyBailoutId(ast_id)); int push_count = environment->push_count(); int pop_count = environment->pop_count(); - HSimulate* instr = new(zone()) HSimulate(ast_id, pop_count); + HSimulate* instr = new(zone()) HSimulate(ast_id, pop_count, zone()); for (int i = push_count - 1; i >= 0; --i) { instr->AddPushedValue(environment->ExpressionStackAt(i)); } @@ -165,32 +166,31 @@ void HBasicBlock::Finish(HControlInstruction* end) { void HBasicBlock::Goto(HBasicBlock* block, FunctionState* state) { - bool drop_extra = state != NULL && state->drop_extra(); - bool arguments_pushed = state != NULL && state->arguments_pushed(); + bool drop_extra = state != NULL && + state->inlining_kind() == DROP_EXTRA_ON_RETURN; if (block->IsInlineReturnTarget()) { - AddInstruction(new(zone()) HLeaveInlined(arguments_pushed)); + AddInstruction(new(zone()) HLeaveInlined()); last_environment_ = last_environment()->DiscardInlined(drop_extra); } - AddSimulate(AstNode::kNoNumber); + AddSimulate(BailoutId::None()); HGoto* instr = new(zone()) HGoto(block); Finish(instr); } void HBasicBlock::AddLeaveInlined(HValue* return_value, - HBasicBlock* target, FunctionState* state) { - bool drop_extra = state != NULL && state->drop_extra(); - bool arguments_pushed = state != NULL && state->arguments_pushed(); + HBasicBlock* target = state->function_return(); + bool drop_extra = state->inlining_kind() == DROP_EXTRA_ON_RETURN; ASSERT(target->IsInlineReturnTarget()); ASSERT(return_value != NULL); - AddInstruction(new(zone()) HLeaveInlined(arguments_pushed)); + AddInstruction(new(zone()) HLeaveInlined()); last_environment_ = last_environment()->DiscardInlined(drop_extra); last_environment()->Push(return_value); - AddSimulate(AstNode::kNoNumber); + AddSimulate(BailoutId::None()); HGoto* instr = new(zone()) HGoto(target); Finish(instr); } @@ -203,7 +203,7 @@ void HBasicBlock::SetInitialEnvironment(HEnvironment* env) { } -void HBasicBlock::SetJoinId(int ast_id) { +void HBasicBlock::SetJoinId(BailoutId ast_id) { int length = predecessors_.length(); ASSERT(length > 0); for (int i = 0; i < length; i++) { @@ -278,7 +278,7 @@ void HBasicBlock::RegisterPredecessor(HBasicBlock* pred) { SetInitialEnvironment(pred->last_environment()->Copy()); } - predecessors_.Add(pred); + predecessors_.Add(pred, zone()); } @@ -291,7 +291,7 @@ void HBasicBlock::AddDominatedBlock(HBasicBlock* block) { dominated_blocks_[index]->block_id() < block->block_id()) { ++index; } - dominated_blocks_.InsertAt(index, block); + dominated_blocks_.InsertAt(index, block, zone()); } @@ -404,7 +404,7 @@ void HBasicBlock::Verify() { void HLoopInformation::RegisterBackEdge(HBasicBlock* block) { - this->back_edges_.Add(block); + this->back_edges_.Add(block, block->zone()); AddBlock(block); } @@ -430,7 +430,7 @@ void HLoopInformation::AddBlock(HBasicBlock* block) { AddBlock(block->parent_loop_header()); } else { block->set_parent_loop_header(loop_header()); - blocks_.Add(block); + blocks_.Add(block, block->zone()); for (int i = 0; i < block->predecessors()->length(); ++i) { AddBlock(block->predecessors()->at(i)); } @@ -451,8 +451,8 @@ class ReachabilityAnalyzer BASE_EMBEDDED { int block_count, HBasicBlock* dont_visit) : visited_count_(0), - stack_(16), - reachable_(block_count, ZONE), + stack_(16, entry_block->zone()), + reachable_(block_count, entry_block->zone()), dont_visit_(dont_visit) { PushBlock(entry_block); Analyze(); @@ -466,7 +466,7 @@ class ReachabilityAnalyzer BASE_EMBEDDED { if (block != NULL && block != dont_visit_ && !reachable_.Contains(block->block_id())) { reachable_.Add(block->block_id()); - stack_.Add(block); + stack_.Add(block, block->zone()); visited_count_++; } } @@ -526,7 +526,8 @@ void HGraph::Verify(bool do_full_verify) const { // Check that all join blocks have predecessors that end with an // unconditional goto and agree on their environment node id. if (block->predecessors()->length() >= 2) { - int id = block->predecessors()->first()->last_environment()->ast_id(); + BailoutId id = + block->predecessors()->first()->last_environment()->ast_id(); for (int k = 0; k < block->predecessors()->length(); k++) { HBasicBlock* predecessor = block->predecessors()->at(k); ASSERT(predecessor->end()->IsGoto()); @@ -567,9 +568,9 @@ void HGraph::Verify(bool do_full_verify) const { HConstant* HGraph::GetConstant(SetOncePointer<HConstant>* pointer, - Object* value) { + Handle<Object> value) { if (!pointer->is_set()) { - HConstant* constant = new(zone()) HConstant(Handle<Object>(value), + HConstant* constant = new(zone()) HConstant(value, Representation::Tagged()); constant->InsertAfter(GetConstantUndefined()); pointer->set(constant); @@ -578,28 +579,40 @@ HConstant* HGraph::GetConstant(SetOncePointer<HConstant>* pointer, } +HConstant* HGraph::GetConstantInt32(SetOncePointer<HConstant>* pointer, + int32_t value) { + if (!pointer->is_set()) { + HConstant* constant = + new(zone()) HConstant(value, Representation::Integer32()); + constant->InsertAfter(GetConstantUndefined()); + pointer->set(constant); + } + return pointer->get(); +} + + HConstant* HGraph::GetConstant1() { - return GetConstant(&constant_1_, Smi::FromInt(1)); + return GetConstantInt32(&constant_1_, 1); } HConstant* HGraph::GetConstantMinus1() { - return GetConstant(&constant_minus1_, Smi::FromInt(-1)); + return GetConstantInt32(&constant_minus1_, -1); } HConstant* HGraph::GetConstantTrue() { - return GetConstant(&constant_true_, isolate()->heap()->true_value()); + return GetConstant(&constant_true_, isolate()->factory()->true_value()); } HConstant* HGraph::GetConstantFalse() { - return GetConstant(&constant_false_, isolate()->heap()->false_value()); + return GetConstant(&constant_false_, isolate()->factory()->false_value()); } HConstant* HGraph::GetConstantHole() { - return GetConstant(&constant_hole_, isolate()->heap()->the_hole_value()); + return GetConstant(&constant_hole_, isolate()->factory()->the_hole_value()); } @@ -612,8 +625,8 @@ HGraphBuilder::HGraphBuilder(CompilationInfo* info, graph_(NULL), current_block_(NULL), inlined_count_(0), - globals_(10), - zone_(info->isolate()->zone()), + globals_(10, info->zone()), + zone_(info->zone()), inline_bailout_(false) { // This is not initialized in the initializer list because the // constructor for the initial state relies on function_state_ == NULL @@ -623,7 +636,7 @@ HGraphBuilder::HGraphBuilder(CompilationInfo* info, HBasicBlock* HGraphBuilder::CreateJoin(HBasicBlock* first, HBasicBlock* second, - int join_id) { + BailoutId join_id) { if (first == NULL) { return second; } else if (second == NULL) { @@ -676,61 +689,26 @@ HGraph::HGraph(CompilationInfo* info) : isolate_(info->isolate()), next_block_id_(0), entry_block_(NULL), - blocks_(8), - values_(16), - phi_list_(NULL) { + blocks_(8, info->zone()), + values_(16, info->zone()), + phi_list_(NULL), + uint32_instructions_(NULL), + info_(info), + zone_(info->zone()), + is_recursive_(false), + use_optimistic_licm_(false), + type_change_checksum_(0) { start_environment_ = - new(zone()) HEnvironment(NULL, info->scope(), info->closure()); - start_environment_->set_ast_id(AstNode::kFunctionEntryId); + new(zone_) HEnvironment(NULL, info->scope(), info->closure(), zone_); + start_environment_->set_ast_id(BailoutId::FunctionEntry()); entry_block_ = CreateBasicBlock(); entry_block_->SetInitialEnvironment(start_environment_); } -Handle<Code> HGraph::Compile(CompilationInfo* info) { - int values = GetMaximumValueID(); - if (values > LUnallocated::kMaxVirtualRegisters) { - if (FLAG_trace_bailout) { - PrintF("Not enough virtual registers for (values).\n"); - } - return Handle<Code>::null(); - } - LAllocator allocator(values, this); - LChunkBuilder builder(info, this, &allocator); - LChunk* chunk = builder.Build(); - if (chunk == NULL) return Handle<Code>::null(); - - if (!allocator.Allocate(chunk)) { - if (FLAG_trace_bailout) { - PrintF("Not enough virtual registers (regalloc).\n"); - } - return Handle<Code>::null(); - } - - MacroAssembler assembler(info->isolate(), NULL, 0); - LCodeGen generator(chunk, &assembler, info); - - chunk->MarkEmptyBlocks(); - - if (generator.GenerateCode()) { - if (FLAG_trace_codegen) { - PrintF("Crankshaft Compiler - "); - } - CodeGenerator::MakeCodePrologue(info); - Code::Flags flags = Code::ComputeFlags(Code::OPTIMIZED_FUNCTION); - Handle<Code> code = - CodeGenerator::MakeCodeEpilogue(&assembler, flags, info); - generator.FinishCode(code); - CodeGenerator::PrintCode(code, info); - return code; - } - return Handle<Code>::null(); -} - - HBasicBlock* HGraph::CreateBasicBlock() { HBasicBlock* result = new(zone()) HBasicBlock(this); - blocks_.Add(result); + blocks_.Add(result, zone()); return result; } @@ -748,66 +726,319 @@ void HGraph::Canonicalize() { } } +// Block ordering was implemented with two mutually recursive methods, +// HGraph::Postorder and HGraph::PostorderLoopBlocks. +// The recursion could lead to stack overflow so the algorithm has been +// implemented iteratively. +// At a high level the algorithm looks like this: +// +// Postorder(block, loop_header) : { +// if (block has already been visited or is of another loop) return; +// mark block as visited; +// if (block is a loop header) { +// VisitLoopMembers(block, loop_header); +// VisitSuccessorsOfLoopHeader(block); +// } else { +// VisitSuccessors(block) +// } +// put block in result list; +// } +// +// VisitLoopMembers(block, outer_loop_header) { +// foreach (block b in block loop members) { +// VisitSuccessorsOfLoopMember(b, outer_loop_header); +// if (b is loop header) VisitLoopMembers(b); +// } +// } +// +// VisitSuccessorsOfLoopMember(block, outer_loop_header) { +// foreach (block b in block successors) Postorder(b, outer_loop_header) +// } +// +// VisitSuccessorsOfLoopHeader(block) { +// foreach (block b in block successors) Postorder(b, block) +// } +// +// VisitSuccessors(block, loop_header) { +// foreach (block b in block successors) Postorder(b, loop_header) +// } +// +// The ordering is started calling Postorder(entry, NULL). +// +// Each instance of PostorderProcessor represents the "stack frame" of the +// recursion, and particularly keeps the state of the loop (iteration) of the +// "Visit..." function it represents. +// To recycle memory we keep all the frames in a double linked list but +// this means that we cannot use constructors to initialize the frames. +// +class PostorderProcessor : public ZoneObject { + public: + // Back link (towards the stack bottom). + PostorderProcessor* parent() {return father_; } + // Forward link (towards the stack top). + PostorderProcessor* child() {return child_; } + HBasicBlock* block() { return block_; } + HLoopInformation* loop() { return loop_; } + HBasicBlock* loop_header() { return loop_header_; } + + static PostorderProcessor* CreateEntryProcessor(Zone* zone, + HBasicBlock* block, + BitVector* visited) { + PostorderProcessor* result = new(zone) PostorderProcessor(NULL); + return result->SetupSuccessors(zone, block, NULL, visited); + } + + PostorderProcessor* PerformStep(Zone* zone, + BitVector* visited, + ZoneList<HBasicBlock*>* order) { + PostorderProcessor* next = + PerformNonBacktrackingStep(zone, visited, order); + if (next != NULL) { + return next; + } else { + return Backtrack(zone, visited, order); + } + } -void HGraph::OrderBlocks() { - HPhase phase("H_Block ordering"); - BitVector visited(blocks_.length(), zone()); - - ZoneList<HBasicBlock*> reverse_result(8); - HBasicBlock* start = blocks_[0]; - Postorder(start, &visited, &reverse_result, NULL); + private: + explicit PostorderProcessor(PostorderProcessor* father) + : father_(father), child_(NULL), successor_iterator(NULL) { } + + // Each enum value states the cycle whose state is kept by this instance. + enum LoopKind { + NONE, + SUCCESSORS, + SUCCESSORS_OF_LOOP_HEADER, + LOOP_MEMBERS, + SUCCESSORS_OF_LOOP_MEMBER + }; + + // Each "Setup..." method is like a constructor for a cycle state. + PostorderProcessor* SetupSuccessors(Zone* zone, + HBasicBlock* block, + HBasicBlock* loop_header, + BitVector* visited) { + if (block == NULL || visited->Contains(block->block_id()) || + block->parent_loop_header() != loop_header) { + kind_ = NONE; + block_ = NULL; + loop_ = NULL; + loop_header_ = NULL; + return this; + } else { + block_ = block; + loop_ = NULL; + visited->Add(block->block_id()); - blocks_.Rewind(0); - int index = 0; - for (int i = reverse_result.length() - 1; i >= 0; --i) { - HBasicBlock* b = reverse_result[i]; - blocks_.Add(b); - b->set_block_id(index++); + if (block->IsLoopHeader()) { + kind_ = SUCCESSORS_OF_LOOP_HEADER; + loop_header_ = block; + InitializeSuccessors(); + PostorderProcessor* result = Push(zone); + return result->SetupLoopMembers(zone, block, block->loop_information(), + loop_header); + } else { + ASSERT(block->IsFinished()); + kind_ = SUCCESSORS; + loop_header_ = loop_header; + InitializeSuccessors(); + return this; + } + } } -} + PostorderProcessor* SetupLoopMembers(Zone* zone, + HBasicBlock* block, + HLoopInformation* loop, + HBasicBlock* loop_header) { + kind_ = LOOP_MEMBERS; + block_ = block; + loop_ = loop; + loop_header_ = loop_header; + InitializeLoopMembers(); + return this; + } + + PostorderProcessor* SetupSuccessorsOfLoopMember( + HBasicBlock* block, + HLoopInformation* loop, + HBasicBlock* loop_header) { + kind_ = SUCCESSORS_OF_LOOP_MEMBER; + block_ = block; + loop_ = loop; + loop_header_ = loop_header; + InitializeSuccessors(); + return this; + } + + // This method "allocates" a new stack frame. + PostorderProcessor* Push(Zone* zone) { + if (child_ == NULL) { + child_ = new(zone) PostorderProcessor(this); + } + return child_; + } + + void ClosePostorder(ZoneList<HBasicBlock*>* order, Zone* zone) { + ASSERT(block_->end()->FirstSuccessor() == NULL || + order->Contains(block_->end()->FirstSuccessor()) || + block_->end()->FirstSuccessor()->IsLoopHeader()); + ASSERT(block_->end()->SecondSuccessor() == NULL || + order->Contains(block_->end()->SecondSuccessor()) || + block_->end()->SecondSuccessor()->IsLoopHeader()); + order->Add(block_, zone); + } + + // This method is the basic block to walk up the stack. + PostorderProcessor* Pop(Zone* zone, + BitVector* visited, + ZoneList<HBasicBlock*>* order) { + switch (kind_) { + case SUCCESSORS: + case SUCCESSORS_OF_LOOP_HEADER: + ClosePostorder(order, zone); + return father_; + case LOOP_MEMBERS: + return father_; + case SUCCESSORS_OF_LOOP_MEMBER: + if (block()->IsLoopHeader() && block() != loop_->loop_header()) { + // In this case we need to perform a LOOP_MEMBERS cycle so we + // initialize it and return this instead of father. + return SetupLoopMembers(zone, block(), + block()->loop_information(), loop_header_); + } else { + return father_; + } + case NONE: + return father_; + } + UNREACHABLE(); + return NULL; + } -void HGraph::PostorderLoopBlocks(HLoopInformation* loop, - BitVector* visited, - ZoneList<HBasicBlock*>* order, - HBasicBlock* loop_header) { - for (int i = 0; i < loop->blocks()->length(); ++i) { - HBasicBlock* b = loop->blocks()->at(i); - for (HSuccessorIterator it(b->end()); !it.Done(); it.Advance()) { - Postorder(it.Current(), visited, order, loop_header); + // Walks up the stack. + PostorderProcessor* Backtrack(Zone* zone, + BitVector* visited, + ZoneList<HBasicBlock*>* order) { + PostorderProcessor* parent = Pop(zone, visited, order); + while (parent != NULL) { + PostorderProcessor* next = + parent->PerformNonBacktrackingStep(zone, visited, order); + if (next != NULL) { + return next; + } else { + parent = parent->Pop(zone, visited, order); + } } - if (b->IsLoopHeader() && b != loop->loop_header()) { - PostorderLoopBlocks(b->loop_information(), visited, order, loop_header); + return NULL; + } + + PostorderProcessor* PerformNonBacktrackingStep( + Zone* zone, + BitVector* visited, + ZoneList<HBasicBlock*>* order) { + HBasicBlock* next_block; + switch (kind_) { + case SUCCESSORS: + next_block = AdvanceSuccessors(); + if (next_block != NULL) { + PostorderProcessor* result = Push(zone); + return result->SetupSuccessors(zone, next_block, + loop_header_, visited); + } + break; + case SUCCESSORS_OF_LOOP_HEADER: + next_block = AdvanceSuccessors(); + if (next_block != NULL) { + PostorderProcessor* result = Push(zone); + return result->SetupSuccessors(zone, next_block, + block(), visited); + } + break; + case LOOP_MEMBERS: + next_block = AdvanceLoopMembers(); + if (next_block != NULL) { + PostorderProcessor* result = Push(zone); + return result->SetupSuccessorsOfLoopMember(next_block, + loop_, loop_header_); + } + break; + case SUCCESSORS_OF_LOOP_MEMBER: + next_block = AdvanceSuccessors(); + if (next_block != NULL) { + PostorderProcessor* result = Push(zone); + return result->SetupSuccessors(zone, next_block, + loop_header_, visited); + } + break; + case NONE: + return NULL; } + return NULL; } -} + // The following two methods implement a "foreach b in successors" cycle. + void InitializeSuccessors() { + loop_index = 0; + loop_length = 0; + successor_iterator = HSuccessorIterator(block_->end()); + } -void HGraph::Postorder(HBasicBlock* block, - BitVector* visited, - ZoneList<HBasicBlock*>* order, - HBasicBlock* loop_header) { - if (block == NULL || visited->Contains(block->block_id())) return; - if (block->parent_loop_header() != loop_header) return; - visited->Add(block->block_id()); - if (block->IsLoopHeader()) { - PostorderLoopBlocks(block->loop_information(), visited, order, loop_header); - for (HSuccessorIterator it(block->end()); !it.Done(); it.Advance()) { - Postorder(it.Current(), visited, order, block); + HBasicBlock* AdvanceSuccessors() { + if (!successor_iterator.Done()) { + HBasicBlock* result = successor_iterator.Current(); + successor_iterator.Advance(); + return result; } - } else { - ASSERT(block->IsFinished()); - for (HSuccessorIterator it(block->end()); !it.Done(); it.Advance()) { - Postorder(it.Current(), visited, order, loop_header); + return NULL; + } + + // The following two methods implement a "foreach b in loop members" cycle. + void InitializeLoopMembers() { + loop_index = 0; + loop_length = loop_->blocks()->length(); + } + + HBasicBlock* AdvanceLoopMembers() { + if (loop_index < loop_length) { + HBasicBlock* result = loop_->blocks()->at(loop_index); + loop_index++; + return result; + } else { + return NULL; } } - ASSERT(block->end()->FirstSuccessor() == NULL || - order->Contains(block->end()->FirstSuccessor()) || - block->end()->FirstSuccessor()->IsLoopHeader()); - ASSERT(block->end()->SecondSuccessor() == NULL || - order->Contains(block->end()->SecondSuccessor()) || - block->end()->SecondSuccessor()->IsLoopHeader()); - order->Add(block); + + LoopKind kind_; + PostorderProcessor* father_; + PostorderProcessor* child_; + HLoopInformation* loop_; + HBasicBlock* block_; + HBasicBlock* loop_header_; + int loop_index; + int loop_length; + HSuccessorIterator successor_iterator; +}; + + +void HGraph::OrderBlocks() { + HPhase phase("H_Block ordering"); + BitVector visited(blocks_.length(), zone()); + + ZoneList<HBasicBlock*> reverse_result(8, zone()); + HBasicBlock* start = blocks_[0]; + PostorderProcessor* postorder = + PostorderProcessor::CreateEntryProcessor(zone(), start, &visited); + while (postorder != NULL) { + postorder = postorder->PerformStep(zone(), &visited, &reverse_result); + } + blocks_.Rewind(0); + int index = 0; + for (int i = reverse_result.length() - 1; i >= 0; --i) { + HBasicBlock* b = reverse_result[i]; + blocks_.Add(b, zone()); + b->set_block_id(index++); + } } @@ -849,9 +1080,9 @@ void HGraph::EliminateRedundantPhis() { // Worklist of phis that can potentially be eliminated. Initialized with // all phi nodes. When elimination of a phi node modifies another phi node // the modified phi node is added to the worklist. - ZoneList<HPhi*> worklist(blocks_.length()); + ZoneList<HPhi*> worklist(blocks_.length(), zone()); for (int i = 0; i < blocks_.length(); ++i) { - worklist.AddAll(*blocks_[i]->phis()); + worklist.AddAll(*blocks_[i]->phis(), zone()); } while (!worklist.is_empty()) { @@ -869,7 +1100,7 @@ void HGraph::EliminateRedundantPhis() { for (HUseIterator it(phi->uses()); !it.Done(); it.Advance()) { HValue* value = it.value(); value->SetOperandAt(it.index(), replacement); - if (value->IsPhi()) worklist.Add(HPhi::cast(value)); + if (value->IsPhi()) worklist.Add(HPhi::cast(value), zone()); } block->RemovePhi(phi); } @@ -881,18 +1112,18 @@ void HGraph::EliminateUnreachablePhis() { HPhase phase("H_Unreachable phi elimination", this); // Initialize worklist. - ZoneList<HPhi*> phi_list(blocks_.length()); - ZoneList<HPhi*> worklist(blocks_.length()); + ZoneList<HPhi*> phi_list(blocks_.length(), zone()); + ZoneList<HPhi*> worklist(blocks_.length(), zone()); for (int i = 0; i < blocks_.length(); ++i) { for (int j = 0; j < blocks_[i]->phis()->length(); j++) { HPhi* phi = blocks_[i]->phis()->at(j); - phi_list.Add(phi); + phi_list.Add(phi, zone()); // We can't eliminate phis in the receiver position in the environment // because in case of throwing an error we need this value to // construct a stack trace. if (phi->HasRealUses() || phi->IsReceiver()) { phi->set_is_live(true); - worklist.Add(phi); + worklist.Add(phi, zone()); } } } @@ -904,7 +1135,7 @@ void HGraph::EliminateUnreachablePhis() { HValue* operand = phi->OperandAt(i); if (operand->IsPhi() && !HPhi::cast(operand)->is_live()) { HPhi::cast(operand)->set_is_live(true); - worklist.Add(HPhi::cast(operand)); + worklist.Add(HPhi::cast(operand), zone()); } } } @@ -951,11 +1182,11 @@ bool HGraph::CheckConstPhiUses() { void HGraph::CollectPhis() { int block_count = blocks_.length(); - phi_list_ = new ZoneList<HPhi*>(block_count); + phi_list_ = new(zone()) ZoneList<HPhi*>(block_count, zone()); for (int i = 0; i < block_count; ++i) { for (int j = 0; j < blocks_[i]->phis()->length(); ++j) { HPhi* phi = blocks_[i]->phis()->at(j); - phi_list_->Add(phi); + phi_list_->Add(phi, zone()); } } } @@ -976,7 +1207,7 @@ void HGraph::InferTypes(ZoneList<HValue*>* worklist) { HValue* use = it.value(); if (!in_worklist.Contains(use->id())) { in_worklist.Add(use->id()); - worklist->Add(use); + worklist->Add(use, zone()); } } } @@ -987,7 +1218,7 @@ void HGraph::InferTypes(ZoneList<HValue*>* worklist) { class HRangeAnalysis BASE_EMBEDDED { public: explicit HRangeAnalysis(HGraph* graph) : - graph_(graph), zone_(graph->isolate()->zone()), changed_ranges_(16) { } + graph_(graph), zone_(graph->zone()), changed_ranges_(16, zone_) { } void Analyze(); @@ -1132,7 +1363,7 @@ void HRangeAnalysis::RollBackTo(int index) { void HRangeAnalysis::AddRange(HValue* value, Range* range) { Range* original_range = value->range(); value->AddNewRange(range, zone_); - changed_ranges_.Add(value); + changed_ranges_.Add(value, zone_); Range* new_range = value->range(); TraceRange("Updated range of %d set to [%d,%d]\n", value->id(), @@ -1260,18 +1491,18 @@ HValue* HValueMap::Lookup(HValue* value) const { } -void HValueMap::Resize(int new_size) { +void HValueMap::Resize(int new_size, Zone* zone) { ASSERT(new_size > count_); // Hashing the values into the new array has no more collisions than in the // old hash map, so we can use the existing lists_ array, if we are careful. // Make sure we have at least one free element. if (free_list_head_ == kNil) { - ResizeLists(lists_size_ << 1); + ResizeLists(lists_size_ << 1, zone); } HValueMapListElement* new_array = - ZONE->NewArray<HValueMapListElement>(new_size); + zone->NewArray<HValueMapListElement>(new_size); memset(new_array, 0, sizeof(HValueMapListElement) * new_size); HValueMapListElement* old_array = array_; @@ -1289,14 +1520,14 @@ void HValueMap::Resize(int new_size) { if (old_array[i].value != NULL) { int current = old_array[i].next; while (current != kNil) { - Insert(lists_[current].value); + Insert(lists_[current].value, zone); int next = lists_[current].next; lists_[current].next = free_list_head_; free_list_head_ = current; current = next; } // Rehash the directly stored value. - Insert(old_array[i].value); + Insert(old_array[i].value, zone); } } } @@ -1305,11 +1536,11 @@ void HValueMap::Resize(int new_size) { } -void HValueMap::ResizeLists(int new_size) { +void HValueMap::ResizeLists(int new_size, Zone* zone) { ASSERT(new_size > lists_size_); HValueMapListElement* new_lists = - ZONE->NewArray<HValueMapListElement>(new_size); + zone->NewArray<HValueMapListElement>(new_size); memset(new_lists, 0, sizeof(HValueMapListElement) * new_size); HValueMapListElement* old_lists = lists_; @@ -1328,10 +1559,10 @@ void HValueMap::ResizeLists(int new_size) { } -void HValueMap::Insert(HValue* value) { +void HValueMap::Insert(HValue* value, Zone* zone) { ASSERT(value != NULL); // Resizing when half of the hashtable is filled up. - if (count_ >= array_size_ >> 1) Resize(array_size_ << 1); + if (count_ >= array_size_ >> 1) Resize(array_size_ << 1, zone); ASSERT(count_ < array_size_); count_++; uint32_t pos = Bound(static_cast<uint32_t>(value->Hashcode())); @@ -1340,7 +1571,7 @@ void HValueMap::Insert(HValue* value) { array_[pos].next = kNil; } else { if (free_list_head_ == kNil) { - ResizeLists(lists_size_ << 1); + ResizeLists(lists_size_ << 1, zone); } int new_element_pos = free_list_head_; ASSERT(new_element_pos != kNil); @@ -1359,10 +1590,17 @@ HSideEffectMap::HSideEffectMap() : count_(0) { HSideEffectMap::HSideEffectMap(HSideEffectMap* other) : count_(other->count_) { - memcpy(data_, other->data_, kNumberOfTrackedSideEffects * kPointerSize); + *this = *other; // Calls operator=. } +HSideEffectMap& HSideEffectMap::operator= (const HSideEffectMap& other) { + if (this != &other) { + memcpy(data_, other.data_, kNumberOfTrackedSideEffects * kPointerSize); + } + return *this; +} + void HSideEffectMap::Kill(GVNFlagSet flags) { for (int i = 0; i < kNumberOfTrackedSideEffects; i++) { GVNFlag changes_flag = HValue::ChangesFlagFromInt(i); @@ -1473,15 +1711,17 @@ class HGlobalValueNumberer BASE_EMBEDDED { : graph_(graph), info_(info), removed_side_effects_(false), - block_side_effects_(graph->blocks()->length()), - loop_side_effects_(graph->blocks()->length()), + block_side_effects_(graph->blocks()->length(), graph->zone()), + loop_side_effects_(graph->blocks()->length(), graph->zone()), visited_on_paths_(graph->zone(), graph->blocks()->length()) { - ASSERT(info->isolate()->heap()->allow_allocation(false)); - block_side_effects_.AddBlock(GVNFlagSet(), graph_->blocks()->length()); - loop_side_effects_.AddBlock(GVNFlagSet(), graph_->blocks()->length()); - } - ~HGlobalValueNumberer() { - ASSERT(!info_->isolate()->heap()->allow_allocation(true)); +#ifdef DEBUG + ASSERT(info->isolate()->optimizing_compiler_thread()->IsOptimizerThread() || + !info->isolate()->heap()->IsAllocationAllowed()); +#endif + block_side_effects_.AddBlock(GVNFlagSet(), graph_->blocks()->length(), + graph_->zone()); + loop_side_effects_.AddBlock(GVNFlagSet(), graph_->blocks()->length(), + graph_->zone()); } // Returns true if values with side effects are removed. @@ -1491,9 +1731,7 @@ class HGlobalValueNumberer BASE_EMBEDDED { GVNFlagSet CollectSideEffectsOnPathsToDominatedBlock( HBasicBlock* dominator, HBasicBlock* dominated); - void AnalyzeBlock(HBasicBlock* block, - HValueMap* map, - HSideEffectMap* dominators); + void AnalyzeGraph(); void ComputeBlockSideEffects(); void LoopInvariantCodeMotion(); void ProcessLoopBlock(HBasicBlock* block, @@ -1506,7 +1744,7 @@ class HGlobalValueNumberer BASE_EMBEDDED { HGraph* graph() { return graph_; } CompilationInfo* info() { return info_; } - Zone* zone() { return graph_->zone(); } + Zone* zone() const { return graph_->zone(); } HGraph* graph_; CompilationInfo* info_; @@ -1530,9 +1768,7 @@ bool HGlobalValueNumberer::Analyze() { if (FLAG_loop_invariant_code_motion) { LoopInvariantCodeMotion(); } - HValueMap* map = new(zone()) HValueMap(); - HSideEffectMap side_effect_dominators; - AnalyzeBlock(graph_->entry_block(), map, &side_effect_dominators); + AnalyzeGraph(); return removed_side_effects_; } @@ -1664,6 +1900,8 @@ GVN_UNTRACKED_FLAG_LIST(DECLARE_FLAG) void HGlobalValueNumberer::LoopInvariantCodeMotion() { + TRACE_GVN_1("Using optimistic loop invariant code motion: %s\n", + graph_->use_optimistic_licm() ? "yes" : "no"); for (int i = graph_->blocks()->length() - 1; i >= 0; --i) { HBasicBlock* block = graph_->blocks()->at(i); if (block->IsLoopHeader()) { @@ -1707,51 +1945,8 @@ void HGlobalValueNumberer::ProcessLoopBlock( *GetGVNFlagsString(instr->gvn_flags()), *GetGVNFlagsString(loop_kills)); bool can_hoist = !instr->gvn_flags().ContainsAnyOf(depends_flags); - if (instr->IsTransitionElementsKind()) { - // It's possible to hoist transitions out of a loop as long as the - // hoisting wouldn't move the transition past a DependsOn of one of it's - // changes or any instructions that might change an objects map or - // elements contents. - GVNFlagSet changes = instr->ChangesFlags(); - GVNFlagSet hoist_depends_blockers = - HValue::ConvertChangesToDependsFlags(changes); - // In addition to not hoisting transitions above other instructions that - // change dependencies that the transition changes, it must not be - // hoisted above map changes and stores to an elements backing store - // that the transition might change. - GVNFlagSet hoist_change_blockers = changes; - hoist_change_blockers.Add(kChangesMaps); - HTransitionElementsKind* trans = HTransitionElementsKind::cast(instr); - if (trans->original_map()->has_fast_double_elements()) { - hoist_change_blockers.Add(kChangesDoubleArrayElements); - } - if (trans->transitioned_map()->has_fast_double_elements()) { - hoist_change_blockers.Add(kChangesArrayElements); - } - if (FLAG_trace_gvn) { - GVNFlagSet hoist_blockers = hoist_depends_blockers; - hoist_blockers.Add(hoist_change_blockers); - GVNFlagSet first_time = *first_time_changes; - first_time.Add(*first_time_depends); - TRACE_GVN_4("Checking dependencies on HTransitionElementsKind " - "%d (%s) hoist blockers: %s; " - "first-time accumulated: %s\n", - instr->id(), - instr->Mnemonic(), - *GetGVNFlagsString(hoist_blockers), - *GetGVNFlagsString(first_time)); - } - // It's possible to hoist transition from the current loop loop only if - // they dominate all of the successor blocks in the same loop and there - // are not any instructions that have Changes/DependsOn that intervene - // between it and the beginning of the loop header. - bool in_nested_loop = block != loop_header && - ((block->parent_loop_header() != loop_header) || - block->IsLoopHeader()); - can_hoist = !in_nested_loop && - block->IsLoopSuccessorDominator() && - !first_time_depends->ContainsAnyOf(hoist_depends_blockers) && - !first_time_changes->ContainsAnyOf(hoist_change_blockers); + if (can_hoist && !graph()->use_optimistic_licm()) { + can_hoist = block->IsLoopSuccessorDominator(); } if (can_hoist) { @@ -1794,7 +1989,7 @@ void HGlobalValueNumberer::ProcessLoopBlock( bool HGlobalValueNumberer::AllowCodeMotion() { - return info()->shared_info()->opt_count() + 1 < Compiler::kDefaultMaxOptCount; + return info()->shared_info()->opt_count() + 1 < FLAG_max_opt_count; } @@ -1826,89 +2021,220 @@ GVNFlagSet HGlobalValueNumberer::CollectSideEffectsOnPathsToDominatedBlock( } -void HGlobalValueNumberer::AnalyzeBlock(HBasicBlock* block, - HValueMap* map, - HSideEffectMap* dominators) { - TRACE_GVN_2("Analyzing block B%d%s\n", - block->block_id(), - block->IsLoopHeader() ? " (loop header)" : ""); +// Each instance of this class is like a "stack frame" for the recursive +// traversal of the dominator tree done during GVN (the stack is handled +// as a double linked list). +// We reuse frames when possible so the list length is limited by the depth +// of the dominator tree but this forces us to initialize each frame calling +// an explicit "Initialize" method instead of a using constructor. +class GvnBasicBlockState: public ZoneObject { + public: + static GvnBasicBlockState* CreateEntry(Zone* zone, + HBasicBlock* entry_block, + HValueMap* entry_map) { + return new(zone) + GvnBasicBlockState(NULL, entry_block, entry_map, NULL, zone); + } + + HBasicBlock* block() { return block_; } + HValueMap* map() { return map_; } + HSideEffectMap* dominators() { return &dominators_; } + + GvnBasicBlockState* next_in_dominator_tree_traversal( + Zone* zone, + HBasicBlock** dominator) { + // This assignment needs to happen before calling next_dominated() because + // that call can reuse "this" if we are at the last dominated block. + *dominator = block(); + GvnBasicBlockState* result = next_dominated(zone); + if (result == NULL) { + GvnBasicBlockState* dominator_state = pop(); + if (dominator_state != NULL) { + // This branch is guaranteed not to return NULL because pop() never + // returns a state where "is_done() == true". + *dominator = dominator_state->block(); + result = dominator_state->next_dominated(zone); + } else { + // Unnecessary (we are returning NULL) but done for cleanness. + *dominator = NULL; + } + } + return result; + } - // If this is a loop header kill everything killed by the loop. - if (block->IsLoopHeader()) { - map->Kill(loop_side_effects_[block->block_id()]); + private: + void Initialize(HBasicBlock* block, + HValueMap* map, + HSideEffectMap* dominators, + bool copy_map, + Zone* zone) { + block_ = block; + map_ = copy_map ? map->Copy(zone) : map; + dominated_index_ = -1; + length_ = block->dominated_blocks()->length(); + if (dominators != NULL) { + dominators_ = *dominators; + } + } + bool is_done() { return dominated_index_ >= length_; } + + GvnBasicBlockState(GvnBasicBlockState* previous, + HBasicBlock* block, + HValueMap* map, + HSideEffectMap* dominators, + Zone* zone) + : previous_(previous), next_(NULL) { + Initialize(block, map, dominators, true, zone); + } + + GvnBasicBlockState* next_dominated(Zone* zone) { + dominated_index_++; + if (dominated_index_ == length_ - 1) { + // No need to copy the map for the last child in the dominator tree. + Initialize(block_->dominated_blocks()->at(dominated_index_), + map(), + dominators(), + false, + zone); + return this; + } else if (dominated_index_ < length_) { + return push(zone, + block_->dominated_blocks()->at(dominated_index_), + dominators()); + } else { + return NULL; + } } - // Go through all instructions of the current block. - HInstruction* instr = block->first(); - while (instr != NULL) { - HInstruction* next = instr->next(); - GVNFlagSet flags = instr->ChangesFlags(); - if (!flags.IsEmpty()) { - // Clear all instructions in the map that are affected by side effects. - // Store instruction as the dominating one for tracked side effects. - map->Kill(flags); - dominators->Store(flags, instr); - TRACE_GVN_2("Instruction %d %s\n", instr->id(), - *GetGVNFlagsString(flags)); + GvnBasicBlockState* push(Zone* zone, + HBasicBlock* block, + HSideEffectMap* dominators) { + if (next_ == NULL) { + next_ = + new(zone) GvnBasicBlockState(this, block, map(), dominators, zone); + } else { + next_->Initialize(block, map(), dominators, true, zone); } - if (instr->CheckFlag(HValue::kUseGVN)) { - ASSERT(!instr->HasObservableSideEffects()); - HValue* other = map->Lookup(instr); - if (other != NULL) { - ASSERT(instr->Equals(other) && other->Equals(instr)); - TRACE_GVN_4("Replacing value %d (%s) with value %d (%s)\n", - instr->id(), - instr->Mnemonic(), - other->id(), - other->Mnemonic()); - if (instr->HasSideEffects()) removed_side_effects_ = true; - instr->DeleteAndReplaceWith(other); - } else { - map->Add(instr); - } + return next_; + } + GvnBasicBlockState* pop() { + GvnBasicBlockState* result = previous_; + while (result != NULL && result->is_done()) { + TRACE_GVN_2("Backtracking from block B%d to block b%d\n", + block()->block_id(), + previous_->block()->block_id()) + result = result->previous_; } - if (instr->CheckFlag(HValue::kTrackSideEffectDominators)) { - for (int i = 0; i < kNumberOfTrackedSideEffects; i++) { - HValue* other = dominators->at(i); - GVNFlag changes_flag = HValue::ChangesFlagFromInt(i); - GVNFlag depends_on_flag = HValue::DependsOnFlagFromInt(i); - if (instr->DependsOnFlags().Contains(depends_on_flag) && - (other != NULL)) { - TRACE_GVN_5("Side-effect #%d in %d (%s) is dominated by %d (%s)\n", - i, + return result; + } + + GvnBasicBlockState* previous_; + GvnBasicBlockState* next_; + HBasicBlock* block_; + HValueMap* map_; + HSideEffectMap dominators_; + int dominated_index_; + int length_; +}; + +// This is a recursive traversal of the dominator tree but it has been turned +// into a loop to avoid stack overflows. +// The logical "stack frames" of the recursion are kept in a list of +// GvnBasicBlockState instances. +void HGlobalValueNumberer::AnalyzeGraph() { + HBasicBlock* entry_block = graph_->entry_block(); + HValueMap* entry_map = new(zone()) HValueMap(zone()); + GvnBasicBlockState* current = + GvnBasicBlockState::CreateEntry(zone(), entry_block, entry_map); + + while (current != NULL) { + HBasicBlock* block = current->block(); + HValueMap* map = current->map(); + HSideEffectMap* dominators = current->dominators(); + + TRACE_GVN_2("Analyzing block B%d%s\n", + block->block_id(), + block->IsLoopHeader() ? " (loop header)" : ""); + + // If this is a loop header kill everything killed by the loop. + if (block->IsLoopHeader()) { + map->Kill(loop_side_effects_[block->block_id()]); + } + + // Go through all instructions of the current block. + HInstruction* instr = block->first(); + while (instr != NULL) { + HInstruction* next = instr->next(); + GVNFlagSet flags = instr->ChangesFlags(); + if (!flags.IsEmpty()) { + // Clear all instructions in the map that are affected by side effects. + // Store instruction as the dominating one for tracked side effects. + map->Kill(flags); + dominators->Store(flags, instr); + TRACE_GVN_2("Instruction %d %s\n", instr->id(), + *GetGVNFlagsString(flags)); + } + if (instr->CheckFlag(HValue::kUseGVN)) { + ASSERT(!instr->HasObservableSideEffects()); + HValue* other = map->Lookup(instr); + if (other != NULL) { + ASSERT(instr->Equals(other) && other->Equals(instr)); + TRACE_GVN_4("Replacing value %d (%s) with value %d (%s)\n", instr->id(), instr->Mnemonic(), other->id(), other->Mnemonic()); - instr->SetSideEffectDominator(changes_flag, other); + if (instr->HasSideEffects()) removed_side_effects_ = true; + instr->DeleteAndReplaceWith(other); + } else { + map->Add(instr, zone()); } } + if (instr->CheckFlag(HValue::kTrackSideEffectDominators)) { + for (int i = 0; i < kNumberOfTrackedSideEffects; i++) { + HValue* other = dominators->at(i); + GVNFlag changes_flag = HValue::ChangesFlagFromInt(i); + GVNFlag depends_on_flag = HValue::DependsOnFlagFromInt(i); + if (instr->DependsOnFlags().Contains(depends_on_flag) && + (other != NULL)) { + TRACE_GVN_5("Side-effect #%d in %d (%s) is dominated by %d (%s)\n", + i, + instr->id(), + instr->Mnemonic(), + other->id(), + other->Mnemonic()); + instr->SetSideEffectDominator(changes_flag, other); + } + } + } + instr = next; + } + + HBasicBlock* dominator_block; + GvnBasicBlockState* next = + current->next_in_dominator_tree_traversal(zone(), &dominator_block); + + if (next != NULL) { + HBasicBlock* dominated = next->block(); + HValueMap* successor_map = next->map(); + HSideEffectMap* successor_dominators = next->dominators(); + + // Kill everything killed on any path between this block and the + // dominated block. We don't have to traverse these paths if the + // value map and the dominators list is already empty. If the range + // of block ids (block_id, dominated_id) is empty there are no such + // paths. + if ((!successor_map->IsEmpty() || !successor_dominators->IsEmpty()) && + dominator_block->block_id() + 1 < dominated->block_id()) { + visited_on_paths_.Clear(); + GVNFlagSet side_effects_on_all_paths = + CollectSideEffectsOnPathsToDominatedBlock(dominator_block, + dominated); + successor_map->Kill(side_effects_on_all_paths); + successor_dominators->Kill(side_effects_on_all_paths); + } } - instr = next; - } - - // Recursively continue analysis for all immediately dominated blocks. - int length = block->dominated_blocks()->length(); - for (int i = 0; i < length; ++i) { - HBasicBlock* dominated = block->dominated_blocks()->at(i); - // No need to copy the map for the last child in the dominator tree. - HValueMap* successor_map = (i == length - 1) ? map : map->Copy(zone()); - HSideEffectMap successor_dominators(dominators); - - // Kill everything killed on any path between this block and the - // dominated block. We don't have to traverse these paths if the - // value map and the dominators list is already empty. If the range - // of block ids (block_id, dominated_id) is empty there are no such - // paths. - if ((!successor_map->IsEmpty() || !successor_dominators.IsEmpty()) && - block->block_id() + 1 < dominated->block_id()) { - visited_on_paths_.Clear(); - GVNFlagSet side_effects_on_all_paths = - CollectSideEffectsOnPathsToDominatedBlock(block, dominated); - successor_map->Kill(side_effects_on_all_paths); - successor_dominators.Kill(side_effects_on_all_paths); - } - AnalyzeBlock(dominated, successor_map, &successor_dominators); + current = next; } } @@ -1917,7 +2243,7 @@ class HInferRepresentation BASE_EMBEDDED { public: explicit HInferRepresentation(HGraph* graph) : graph_(graph), - worklist_(8), + worklist_(8, graph->zone()), in_worklist_(graph->GetMaximumValueID(), graph->zone()) { } void Analyze(); @@ -1929,7 +2255,7 @@ class HInferRepresentation BASE_EMBEDDED { void AddDependantsToWorklist(HValue* current); void InferBasedOnUses(HValue* current); - Zone* zone() { return graph_->zone(); } + Zone* zone() const { return graph_->zone(); } HGraph* graph_; ZoneList<HValue*> worklist_; @@ -1941,7 +2267,7 @@ void HInferRepresentation::AddToWorklist(HValue* current) { if (current->representation().IsSpecialization()) return; if (!current->CheckFlag(HValue::kFlexibleRepresentation)) return; if (in_worklist_.Contains(current->id())) return; - worklist_.Add(current); + worklist_.Add(current, zone()); in_worklist_.Add(current->id()); } @@ -2008,8 +2334,16 @@ Representation HInferRepresentation::TryChange(HValue* value) { for (HUseIterator it(value->uses()); !it.Done(); it.Advance()) { HValue* use = it.value(); - Representation rep = use->RequiredInputRepresentation(it.index()); + Representation rep = use->ObservedInputRepresentation(it.index()); if (rep.IsNone()) continue; + if (FLAG_trace_representation) { + PrintF("%d %s is used by %d %s as %s\n", + value->id(), + value->Mnemonic(), + use->id(), + use->Mnemonic(), + rep.Mnemonic()); + } if (use->IsPhi()) HPhi::cast(use)->AddIndirectUsesTo(&use_count[0]); use_count[rep.kind()] += use->LoopWeight(); } @@ -2044,12 +2378,12 @@ void HInferRepresentation::Analyze() { // bit-vector of length <number of phis>. const ZoneList<HPhi*>* phi_list = graph_->phi_list(); int phi_count = phi_list->length(); - ZoneList<BitVector*> connected_phis(phi_count); + ZoneList<BitVector*> connected_phis(phi_count, graph_->zone()); for (int i = 0; i < phi_count; ++i) { phi_list->at(i)->InitRealUses(i); BitVector* connected_set = new(zone()) BitVector(phi_count, graph_->zone()); connected_set->Add(i); - connected_phis.Add(connected_set); + connected_phis.Add(connected_set, zone()); } // (2) Do a fixed point iteration to find the set of connected phis. A @@ -2073,21 +2407,34 @@ void HInferRepresentation::Analyze() { } } - // (3) Use the phi reachability information from step 2 to - // (a) sum up the non-phi use counts of all connected phis. - // (b) push information about values which can't be converted to integer - // without deoptimization through the phi use-def chains, avoiding - // unnecessary deoptimizations later. + // (3a) Use the phi reachability information from step 2 to + // push information about values which can't be converted to integer + // without deoptimization through the phi use-def chains, avoiding + // unnecessary deoptimizations later. for (int i = 0; i < phi_count; ++i) { HPhi* phi = phi_list->at(i); bool cti = phi->AllOperandsConvertibleToInteger(); + if (cti) continue; + + for (BitVector::Iterator it(connected_phis.at(i)); + !it.Done(); + it.Advance()) { + HPhi* phi = phi_list->at(it.Current()); + phi->set_is_convertible_to_integer(false); + phi->ResetInteger32Uses(); + } + } + + // (3b) Use the phi reachability information from step 2 to + // sum up the non-phi use counts of all connected phis. + for (int i = 0; i < phi_count; ++i) { + HPhi* phi = phi_list->at(i); for (BitVector::Iterator it(connected_phis.at(i)); !it.Done(); it.Advance()) { int index = it.Current(); - HPhi* it_use = phi_list->at(it.Current()); - if (index != i) phi->AddNonPhiUsesFrom(it_use); // Don't count twice! - if (!cti) it_use->set_is_convertible_to_integer(false); + HPhi* it_use = phi_list->at(index); + if (index != i) phi->AddNonPhiUsesFrom(it_use); // Don't count twice. } } @@ -2145,9 +2492,9 @@ void HGraph::InitializeInferredTypes(int from_inclusive, int to_inclusive) { i = last_back_edge->block_id(); // Update phis of the loop header now after the whole loop body is // guaranteed to be processed. - ZoneList<HValue*> worklist(block->phis()->length()); + ZoneList<HValue*> worklist(block->phis()->length(), zone()); for (int j = 0; j < block->phis()->length(); ++j) { - worklist.Add(block->phis()->at(j)); + worklist.Add(block->phis()->at(j), zone()); } InferTypes(&worklist); } @@ -2170,8 +2517,8 @@ void HGraph::PropagateMinusZeroChecks(HValue* value, BitVector* visited) { break; } - // For multiplication and division, we must propagate to the left and - // the right side. + // For multiplication, division, and Math.min/max(), we must propagate + // to the left and the right side. if (current->IsMul()) { HMul* mul = HMul::cast(current); mul->EnsureAndPropagateNotMinusZero(visited); @@ -2182,6 +2529,11 @@ void HGraph::PropagateMinusZeroChecks(HValue* value, BitVector* visited) { div->EnsureAndPropagateNotMinusZero(visited); PropagateMinusZeroChecks(div->left(), visited); PropagateMinusZeroChecks(div->right(), visited); + } else if (current->IsMathMinMax()) { + HMathMinMax* minmax = HMathMinMax::cast(current); + visited->Add(minmax->id()); + PropagateMinusZeroChecks(minmax->left(), visited); + PropagateMinusZeroChecks(minmax->right(), visited); } current = current->EnsureAndPropagateNotMinusZero(visited); @@ -2214,8 +2566,8 @@ void HGraph::InsertRepresentationChangeForUse(HValue* value, HConstant* constant = HConstant::cast(value); // Try to create a new copy of the constant with the new representation. new_value = is_truncating - ? constant->CopyToTruncatedInt32() - : constant->CopyToRepresentation(to); + ? constant->CopyToTruncatedInt32(zone()) + : constant->CopyToRepresentation(to, zone()); } if (new_value == NULL) { @@ -2331,6 +2683,229 @@ void HGraph::MarkDeoptimizeOnUndefined() { } +// Discover instructions that can be marked with kUint32 flag allowing +// them to produce full range uint32 values. +class Uint32Analysis BASE_EMBEDDED { + public: + explicit Uint32Analysis(Zone* zone) : zone_(zone), phis_(4, zone) { } + + void Analyze(HInstruction* current); + + void UnmarkUnsafePhis(); + + private: + bool IsSafeUint32Use(HValue* val, HValue* use); + bool Uint32UsesAreSafe(HValue* uint32val); + bool CheckPhiOperands(HPhi* phi); + void UnmarkPhi(HPhi* phi, ZoneList<HPhi*>* worklist); + + Zone* zone_; + ZoneList<HPhi*> phis_; +}; + + +bool Uint32Analysis::IsSafeUint32Use(HValue* val, HValue* use) { + // Operations that operatate on bits are safe. + if (use->IsBitwise() || + use->IsShl() || + use->IsSar() || + use->IsShr() || + use->IsBitNot()) { + return true; + } else if (use->IsChange() || use->IsSimulate()) { + // Conversions and deoptimization have special support for unt32. + return true; + } else if (use->IsStoreKeyed()) { + HStoreKeyed* store = HStoreKeyed::cast(use); + if (store->is_external()) { + // Storing a value into an external integer array is a bit level + // operation. + if (store->value() == val) { + // Clamping or a conversion to double should have beed inserted. + ASSERT(store->elements_kind() != EXTERNAL_PIXEL_ELEMENTS); + ASSERT(store->elements_kind() != EXTERNAL_FLOAT_ELEMENTS); + ASSERT(store->elements_kind() != EXTERNAL_DOUBLE_ELEMENTS); + return true; + } + } + } + + return false; +} + + +// Iterate over all uses and verify that they are uint32 safe: either don't +// distinguish between int32 and uint32 due to their bitwise nature or +// have special support for uint32 values. +// Encountered phis are optimisitically treated as safe uint32 uses, +// marked with kUint32 flag and collected in the phis_ list. A separate +// path will be performed later by UnmarkUnsafePhis to clear kUint32 from +// phis that are not actually uint32-safe (it requries fix point iteration). +bool Uint32Analysis::Uint32UsesAreSafe(HValue* uint32val) { + bool collect_phi_uses = false; + for (HUseIterator it(uint32val->uses()); !it.Done(); it.Advance()) { + HValue* use = it.value(); + + if (use->IsPhi()) { + if (!use->CheckFlag(HInstruction::kUint32)) { + // There is a phi use of this value from a phis that is not yet + // collected in phis_ array. Separate pass is required. + collect_phi_uses = true; + } + + // Optimistically treat phis as uint32 safe. + continue; + } + + if (!IsSafeUint32Use(uint32val, use)) { + return false; + } + } + + if (collect_phi_uses) { + for (HUseIterator it(uint32val->uses()); !it.Done(); it.Advance()) { + HValue* use = it.value(); + + // There is a phi use of this value from a phis that is not yet + // collected in phis_ array. Separate pass is required. + if (use->IsPhi() && !use->CheckFlag(HInstruction::kUint32)) { + use->SetFlag(HInstruction::kUint32); + phis_.Add(HPhi::cast(use), zone_); + } + } + } + + return true; +} + + +// Analyze instruction and mark it with kUint32 if all its uses are uint32 +// safe. +void Uint32Analysis::Analyze(HInstruction* current) { + if (Uint32UsesAreSafe(current)) current->SetFlag(HInstruction::kUint32); +} + + +// Check if all operands to the given phi are marked with kUint32 flag. +bool Uint32Analysis::CheckPhiOperands(HPhi* phi) { + if (!phi->CheckFlag(HInstruction::kUint32)) { + // This phi is not uint32 safe. No need to check operands. + return false; + } + + for (int j = 0; j < phi->OperandCount(); j++) { + HValue* operand = phi->OperandAt(j); + if (!operand->CheckFlag(HInstruction::kUint32)) { + // Lazyly mark constants that fit into uint32 range with kUint32 flag. + if (operand->IsConstant() && + HConstant::cast(operand)->IsUint32()) { + operand->SetFlag(HInstruction::kUint32); + continue; + } + + // This phi is not safe, some operands are not uint32 values. + return false; + } + } + + return true; +} + + +// Remove kUint32 flag from the phi itself and its operands. If any operand +// was a phi marked with kUint32 place it into a worklist for +// transitive clearing of kUint32 flag. +void Uint32Analysis::UnmarkPhi(HPhi* phi, ZoneList<HPhi*>* worklist) { + phi->ClearFlag(HInstruction::kUint32); + for (int j = 0; j < phi->OperandCount(); j++) { + HValue* operand = phi->OperandAt(j); + if (operand->CheckFlag(HInstruction::kUint32)) { + operand->ClearFlag(HInstruction::kUint32); + if (operand->IsPhi()) { + worklist->Add(HPhi::cast(operand), zone_); + } + } + } +} + + +void Uint32Analysis::UnmarkUnsafePhis() { + // No phis were collected. Nothing to do. + if (phis_.length() == 0) return; + + // Worklist used to transitively clear kUint32 from phis that + // are used as arguments to other phis. + ZoneList<HPhi*> worklist(phis_.length(), zone_); + + // Phi can be used as a uint32 value if and only if + // all its operands are uint32 values and all its + // uses are uint32 safe. + + // Iterate over collected phis and unmark those that + // are unsafe. When unmarking phi unmark its operands + // and add it to the worklist if it is a phi as well. + // Phis that are still marked as safe are shifted down + // so that all safe phis form a prefix of the phis_ array. + int phi_count = 0; + for (int i = 0; i < phis_.length(); i++) { + HPhi* phi = phis_[i]; + + if (CheckPhiOperands(phi) && Uint32UsesAreSafe(phi)) { + phis_[phi_count++] = phi; + } else { + UnmarkPhi(phi, &worklist); + } + } + + // Now phis array contains only those phis that have safe + // non-phi uses. Start transitively clearing kUint32 flag + // from phi operands of discovered non-safe phies until + // only safe phies are left. + while (!worklist.is_empty()) { + while (!worklist.is_empty()) { + HPhi* phi = worklist.RemoveLast(); + UnmarkPhi(phi, &worklist); + } + + // Check if any operands to safe phies were unmarked + // turning a safe phi into unsafe. The same value + // can flow into several phis. + int new_phi_count = 0; + for (int i = 0; i < phi_count; i++) { + HPhi* phi = phis_[i]; + + if (CheckPhiOperands(phi)) { + phis_[new_phi_count++] = phi; + } else { + UnmarkPhi(phi, &worklist); + } + } + phi_count = new_phi_count; + } +} + + +void HGraph::ComputeSafeUint32Operations() { + if (!FLAG_opt_safe_uint32_operations || uint32_instructions_ == NULL) { + return; + } + + Uint32Analysis analysis(zone()); + for (int i = 0; i < uint32_instructions_->length(); ++i) { + HInstruction* current = uint32_instructions_->at(i); + if (current->IsLinked() && current->representation().IsInteger32()) { + analysis.Analyze(current); + } + } + + // Some phis might have been optimistically marked with kUint32 flag. + // Remove this flag from those phis that are unsafe and propagate + // this information transitively potentially clearing kUint32 flag + // from some non-phi operations that are used as operands to unsafe phis. + analysis.UnmarkUnsafePhis(); +} + + void HGraph::ComputeMinusZeroChecks() { BitVector visited(GetMaximumValueID(), zone()); for (int i = 0; i < blocks_.length(); ++i) { @@ -2360,12 +2935,12 @@ void HGraph::ComputeMinusZeroChecks() { FunctionState::FunctionState(HGraphBuilder* owner, CompilationInfo* info, TypeFeedbackOracle* oracle, - ReturnHandlingFlag return_handling) + InliningKind inlining_kind) : owner_(owner), compilation_info_(info), oracle_(oracle), call_context_(NULL), - return_handling_(return_handling), + inlining_kind_(inlining_kind), function_return_(NULL), test_context_(NULL), entry_(NULL), @@ -2378,10 +2953,13 @@ FunctionState::FunctionState(HGraphBuilder* owner, HBasicBlock* if_false = owner->graph()->CreateBasicBlock(); if_true->MarkAsInlineReturnTarget(); if_false->MarkAsInlineReturnTarget(); - Expression* cond = TestContext::cast(owner->ast_context())->condition(); + TestContext* outer_test_context = TestContext::cast(owner->ast_context()); + Expression* cond = outer_test_context->condition(); + TypeFeedbackOracle* outer_oracle = outer_test_context->oracle(); // 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, cond, if_true, if_false); + test_context_ = + new TestContext(owner, cond, outer_oracle, if_true, if_false); } else { function_return_ = owner->graph()->CreateBasicBlock(); function_return()->MarkAsInlineReturnTarget(); @@ -2457,14 +3035,15 @@ void TestContext::ReturnValue(HValue* value) { } -void EffectContext::ReturnInstruction(HInstruction* instr, int ast_id) { +void EffectContext::ReturnInstruction(HInstruction* instr, BailoutId ast_id) { ASSERT(!instr->IsControlInstruction()); owner()->AddInstruction(instr); if (instr->HasObservableSideEffects()) owner()->AddSimulate(ast_id); } -void EffectContext::ReturnControl(HControlInstruction* instr, int ast_id) { +void EffectContext::ReturnControl(HControlInstruction* instr, + BailoutId ast_id) { ASSERT(!instr->HasObservableSideEffects()); HBasicBlock* empty_true = owner()->graph()->CreateBasicBlock(); HBasicBlock* empty_false = owner()->graph()->CreateBasicBlock(); @@ -2476,7 +3055,7 @@ void EffectContext::ReturnControl(HControlInstruction* instr, int ast_id) { } -void ValueContext::ReturnInstruction(HInstruction* instr, int ast_id) { +void ValueContext::ReturnInstruction(HInstruction* instr, BailoutId ast_id) { ASSERT(!instr->IsControlInstruction()); if (!arguments_allowed() && instr->CheckFlag(HValue::kIsArguments)) { return owner()->Bailout("bad value context for arguments object value"); @@ -2487,7 +3066,7 @@ void ValueContext::ReturnInstruction(HInstruction* instr, int ast_id) { } -void ValueContext::ReturnControl(HControlInstruction* instr, int ast_id) { +void ValueContext::ReturnControl(HControlInstruction* instr, BailoutId ast_id) { ASSERT(!instr->HasObservableSideEffects()); if (!arguments_allowed() && instr->CheckFlag(HValue::kIsArguments)) { return owner()->Bailout("bad value context for arguments object value"); @@ -2507,7 +3086,7 @@ void ValueContext::ReturnControl(HControlInstruction* instr, int ast_id) { } -void TestContext::ReturnInstruction(HInstruction* instr, int ast_id) { +void TestContext::ReturnInstruction(HInstruction* instr, BailoutId ast_id) { ASSERT(!instr->IsControlInstruction()); HGraphBuilder* builder = owner(); builder->AddInstruction(instr); @@ -2522,7 +3101,7 @@ void TestContext::ReturnInstruction(HInstruction* instr, int ast_id) { } -void TestContext::ReturnControl(HControlInstruction* instr, int ast_id) { +void TestContext::ReturnControl(HControlInstruction* instr, BailoutId ast_id) { ASSERT(!instr->HasObservableSideEffects()); HBasicBlock* empty_true = owner()->graph()->CreateBasicBlock(); HBasicBlock* empty_false = owner()->graph()->CreateBasicBlock(); @@ -2546,8 +3125,8 @@ void TestContext::BuildBranch(HValue* value) { } HBasicBlock* empty_true = builder->graph()->CreateBasicBlock(); HBasicBlock* empty_false = builder->graph()->CreateBasicBlock(); - unsigned test_id = condition()->test_id(); - ToBooleanStub::Types expected(builder->oracle()->ToBooleanTypes(test_id)); + TypeFeedbackId test_id = condition()->test_id(); + ToBooleanStub::Types expected(oracle()->ToBooleanTypes(test_id)); HBranch* test = new(zone()) HBranch(value, empty_true, empty_false, expected); builder->current_block()->Finish(test); @@ -2573,11 +3152,7 @@ void TestContext::BuildBranch(HValue* value) { void HGraphBuilder::Bailout(const char* reason) { - if (FLAG_trace_bailout) { - SmartArrayPointer<char> name( - info()->shared_info()->DebugName()->ToCString()); - PrintF("Bailout in HGraphBuilder: @\"%s\": %s\n", *name, reason); - } + info()->set_bailout_reason(reason); SetStackOverflow(); } @@ -2605,17 +3180,14 @@ void HGraphBuilder::VisitForTypeOf(Expression* expr) { void HGraphBuilder::VisitForControl(Expression* expr, HBasicBlock* true_block, HBasicBlock* false_block) { - TestContext for_test(this, expr, true_block, false_block); + TestContext for_test(this, expr, oracle(), true_block, false_block); Visit(expr); } -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; +void HGraphBuilder::VisitArgument(Expression* expr) { + CHECK_ALIVE(VisitForValue(expr)); + Push(AddInstruction(new(zone()) HPushArgument(Pop()))); } @@ -2670,7 +3242,7 @@ HGraph* HGraphBuilder::CreateGraph() { HEnvironment* initial_env = environment()->CopyWithoutHistory(); HBasicBlock* body_entry = CreateBasicBlock(initial_env); current_block()->Goto(body_entry); - body_entry->SetJoinId(AstNode::kFunctionEntryId); + body_entry->SetJoinId(BailoutId::FunctionEntry()); set_current_block(body_entry); // Handle implicit declaration of the function name in named function @@ -2679,7 +3251,7 @@ HGraph* HGraphBuilder::CreateGraph() { VisitVariableDeclaration(scope->function()); } VisitDeclarations(scope->declarations()); - AddSimulate(AstNode::kDeclarationsId); + AddSimulate(BailoutId::Declarations()); HValue* context = environment()->LookupContext(); AddInstruction( @@ -2693,50 +3265,78 @@ HGraph* HGraphBuilder::CreateGraph() { current_block()->FinishExit(instr); set_current_block(NULL); } + + // If the checksum of the number of type info changes is the same as the + // last time this function was compiled, then this recompile is likely not + // due to missing/inadequate type feedback, but rather too aggressive + // optimization. Disable optimistic LICM in that case. + Handle<Code> unoptimized_code(info()->shared_info()->code()); + ASSERT(unoptimized_code->kind() == Code::FUNCTION); + Handle<Object> maybe_type_info(unoptimized_code->type_feedback_info()); + Handle<TypeFeedbackInfo> type_info( + Handle<TypeFeedbackInfo>::cast(maybe_type_info)); + int checksum = type_info->own_type_change_checksum(); + int composite_checksum = graph()->update_type_change_checksum(checksum); + graph()->set_use_optimistic_licm( + !type_info->matches_inlined_type_change_checksum(composite_checksum)); + type_info->set_inlined_type_change_checksum(composite_checksum); } - graph()->OrderBlocks(); - graph()->AssignDominators(); + return graph(); +} + +bool HGraph::Optimize(SmartArrayPointer<char>* bailout_reason) { + *bailout_reason = SmartArrayPointer<char>(); + OrderBlocks(); + AssignDominators(); #ifdef DEBUG // Do a full verify after building the graph and computing dominators. - graph()->Verify(true); + Verify(true); #endif - graph()->PropagateDeoptimizingMark(); - if (!graph()->CheckConstPhiUses()) { - Bailout("Unsupported phi use of const variable"); - return NULL; + PropagateDeoptimizingMark(); + if (!CheckConstPhiUses()) { + *bailout_reason = SmartArrayPointer<char>(StrDup( + "Unsupported phi use of const variable")); + return false; } - graph()->EliminateRedundantPhis(); - if (!graph()->CheckArgumentsPhiUses()) { - Bailout("Unsupported phi use of arguments"); - return NULL; + EliminateRedundantPhis(); + if (!CheckArgumentsPhiUses()) { + *bailout_reason = SmartArrayPointer<char>(StrDup( + "Unsupported phi use of arguments")); + return false; } - if (FLAG_eliminate_dead_phis) graph()->EliminateUnreachablePhis(); - graph()->CollectPhis(); + if (FLAG_eliminate_dead_phis) EliminateUnreachablePhis(); + CollectPhis(); - if (graph()->has_osr_loop_entry()) { - const ZoneList<HPhi*>* phis = graph()->osr_loop_entry()->phis(); + if (has_osr_loop_entry()) { + const ZoneList<HPhi*>* phis = osr_loop_entry()->phis(); for (int j = 0; j < phis->length(); j++) { HPhi* phi = phis->at(j); - graph()->osr_values()->at(phi->merged_index())->set_incoming_value(phi); + osr_values()->at(phi->merged_index())->set_incoming_value(phi); } } - HInferRepresentation rep(graph()); + HInferRepresentation rep(this); rep.Analyze(); - graph()->MarkDeoptimizeOnUndefined(); - graph()->InsertRepresentationChanges(); + MarkDeoptimizeOnUndefined(); + InsertRepresentationChanges(); + + InitializeInferredTypes(); - graph()->InitializeInferredTypes(); - graph()->Canonicalize(); + // Must be performed before canonicalization to ensure that Canonicalize + // will not remove semantically meaningful ToInt32 operations e.g. BIT_OR with + // zero. + ComputeSafeUint32Operations(); + + Canonicalize(); // Perform common subexpression elimination and loop-invariant code motion. if (FLAG_use_gvn) { - HPhase phase("H_Global value numbering", graph()); - HGlobalValueNumberer gvn(graph(), info()); + HPhase phase("H_Global value numbering", this); + HGlobalValueNumberer gvn(this, info()); bool removed_side_effects = gvn.Analyze(); // Trigger a second analysis pass to further eliminate duplicate values that // could only be discovered by removing side-effect-generating instructions @@ -2748,19 +3348,20 @@ HGraph* HGraphBuilder::CreateGraph() { } if (FLAG_use_range) { - HRangeAnalysis rangeAnalysis(graph()); + HRangeAnalysis rangeAnalysis(this); rangeAnalysis.Analyze(); } - graph()->ComputeMinusZeroChecks(); + ComputeMinusZeroChecks(); // Eliminate redundant stack checks on backwards branches. - HStackCheckEliminator sce(graph()); + HStackCheckEliminator sce(this); sce.Process(); - graph()->EliminateRedundantBoundsChecks(); - graph()->DehoistSimpleArrayIndexComputations(); + EliminateRedundantBoundsChecks(); + DehoistSimpleArrayIndexComputations(); + if (FLAG_dead_code_elimination) DeadCodeElimination(); - return graph(); + return true; } @@ -2785,6 +3386,8 @@ class BoundsCheckKey : public ZoneObject { static BoundsCheckKey* Create(Zone* zone, HBoundsCheck* check, int32_t* offset) { + if (!check->index()->representation().IsInteger32()) return NULL; + HValue* index_base = NULL; HConstant* constant = NULL; bool is_sub = false; @@ -2851,7 +3454,8 @@ class BoundsCheckBbData: public ZoneObject { int32_t LowerOffset() const { return lower_offset_; } int32_t UpperOffset() const { return upper_offset_; } HBasicBlock* BasicBlock() const { return basic_block_; } - HBoundsCheck* Check() const { return check_; } + HBoundsCheck* LowerCheck() const { return lower_check_; } + HBoundsCheck* UpperCheck() const { return upper_check_; } BoundsCheckBbData* NextInBasicBlock() const { return next_in_bb_; } BoundsCheckBbData* FatherInDominatorTree() const { return father_in_dt_; } @@ -2859,76 +3463,85 @@ class BoundsCheckBbData: public ZoneObject { return offset >= LowerOffset() && offset <= UpperOffset(); } - // This method removes new_check and modifies the current check so that it - // also "covers" what new_check covered. - // The obvious precondition is that new_check follows Check() in the - // same basic block, and that new_offset is not covered (otherwise we - // could simply remove new_check). - // As a consequence LowerOffset() or UpperOffset() change (the covered + bool HasSingleCheck() { return lower_check_ == upper_check_; } + + // The goal of this method is to modify either upper_offset_ or + // lower_offset_ so that also new_offset is covered (the covered // range grows). // - // In the general case the check covering the current range should be like - // these two checks: - // 0 <= Key()->IndexBase() + LowerOffset() - // Key()->IndexBase() + UpperOffset() < Key()->Length() - // - // We can transform the second check like this: - // Key()->IndexBase() + LowerOffset() < - // Key()->Length() + (LowerOffset() - UpperOffset()) - // so we can handle both checks with a single unsigned comparison. + // The precondition is that new_check follows UpperCheck() and + // LowerCheck() in the same basic block, and that new_offset is not + // covered (otherwise we could simply remove new_check). // - // The bulk of this method changes Check()->index() and Check()->length() - // replacing them with new HAdd instructions to perform the transformation - // described above. + // If HasSingleCheck() is true then new_check is added as "second check" + // (either upper or lower; note that HasSingleCheck() becomes false). + // Otherwise one of the current checks is modified so that it also covers + // new_offset, and new_check is removed. void CoverCheck(HBoundsCheck* new_check, int32_t new_offset) { ASSERT(new_check->index()->representation().IsInteger32()); + bool keep_new_check = false; if (new_offset > upper_offset_) { upper_offset_ = new_offset; + if (HasSingleCheck()) { + keep_new_check = true; + upper_check_ = new_check; + } else { + BuildOffsetAdd(upper_check_, + &added_upper_index_, + &added_upper_offset_, + Key()->IndexBase(), + new_check->index()->representation(), + new_offset); + upper_check_->SetOperandAt(0, added_upper_index_); + } } else if (new_offset < lower_offset_) { lower_offset_ = new_offset; + if (HasSingleCheck()) { + keep_new_check = true; + lower_check_ = new_check; + } else { + BuildOffsetAdd(lower_check_, + &added_lower_index_, + &added_lower_offset_, + Key()->IndexBase(), + new_check->index()->representation(), + new_offset); + lower_check_->SetOperandAt(0, added_lower_index_); + } } else { ASSERT(false); } - BuildOffsetAdd(&added_index_, - &added_index_offset_, - Key()->IndexBase(), - new_check->index()->representation(), - lower_offset_); - Check()->SetOperandAt(0, added_index_); - BuildOffsetAdd(&added_length_, - &added_length_offset_, - Key()->Length(), - new_check->length()->representation(), - lower_offset_ - upper_offset_); - Check()->SetOperandAt(1, added_length_); - - new_check->DeleteAndReplaceWith(NULL); + if (!keep_new_check) { + new_check->DeleteAndReplaceWith(NULL); + } } void RemoveZeroOperations() { - RemoveZeroAdd(&added_index_, &added_index_offset_); - RemoveZeroAdd(&added_length_, &added_length_offset_); + RemoveZeroAdd(&added_lower_index_, &added_lower_offset_); + RemoveZeroAdd(&added_upper_index_, &added_upper_offset_); } BoundsCheckBbData(BoundsCheckKey* key, int32_t lower_offset, int32_t upper_offset, HBasicBlock* bb, - HBoundsCheck* check, + HBoundsCheck* lower_check, + HBoundsCheck* upper_check, BoundsCheckBbData* next_in_bb, BoundsCheckBbData* father_in_dt) : key_(key), lower_offset_(lower_offset), upper_offset_(upper_offset), basic_block_(bb), - check_(check), - added_index_offset_(NULL), - added_index_(NULL), - added_length_offset_(NULL), - added_length_(NULL), + lower_check_(lower_check), + upper_check_(upper_check), + added_lower_index_(NULL), + added_lower_offset_(NULL), + added_upper_index_(NULL), + added_upper_offset_(NULL), next_in_bb_(next_in_bb), father_in_dt_(father_in_dt) { } @@ -2937,29 +3550,33 @@ class BoundsCheckBbData: public ZoneObject { int32_t lower_offset_; int32_t upper_offset_; HBasicBlock* basic_block_; - HBoundsCheck* check_; - HConstant* added_index_offset_; - HAdd* added_index_; - HConstant* added_length_offset_; - HAdd* added_length_; + HBoundsCheck* lower_check_; + HBoundsCheck* upper_check_; + HAdd* added_lower_index_; + HConstant* added_lower_offset_; + HAdd* added_upper_index_; + HConstant* added_upper_offset_; BoundsCheckBbData* next_in_bb_; BoundsCheckBbData* father_in_dt_; - void BuildOffsetAdd(HAdd** add, + void BuildOffsetAdd(HBoundsCheck* check, + HAdd** add, HConstant** constant, HValue* original_value, Representation representation, int32_t new_offset) { HConstant* new_constant = new(BasicBlock()->zone()) - HConstant(Handle<Object>(Smi::FromInt(new_offset)), - Representation::Integer32()); + HConstant(new_offset, Representation::Integer32()); if (*add == NULL) { - new_constant->InsertBefore(Check()); - *add = new(BasicBlock()->zone()) HAdd(NULL, + new_constant->InsertBefore(check); + // Because of the bounds checks elimination algorithm, the index is always + // an HAdd or an HSub here, so we can safely cast to an HBinaryOperation. + HValue* context = HBinaryOperation::cast(check->index())->context(); + *add = new(BasicBlock()->zone()) HAdd(context, original_value, new_constant); (*add)->AssumeRepresentation(representation); - (*add)->InsertBefore(Check()); + (*add)->InsertBefore(check); } else { new_constant->InsertBefore(*add); (*constant)->DeleteAndReplaceWith(new_constant); @@ -2985,20 +3602,22 @@ static bool BoundsCheckKeyMatch(void* key1, void* key2) { class BoundsCheckTable : private ZoneHashMap { public: - BoundsCheckBbData** LookupOrInsert(BoundsCheckKey* key) { + BoundsCheckBbData** LookupOrInsert(BoundsCheckKey* key, Zone* zone) { return reinterpret_cast<BoundsCheckBbData**>( - &(Lookup(key, key->Hash(), true)->value)); + &(Lookup(key, key->Hash(), true, ZoneAllocationPolicy(zone))->value)); } - void Insert(BoundsCheckKey* key, BoundsCheckBbData* data) { - Lookup(key, key->Hash(), true)->value = data; + void Insert(BoundsCheckKey* key, BoundsCheckBbData* data, Zone* zone) { + Lookup(key, key->Hash(), true, ZoneAllocationPolicy(zone))->value = data; } void Delete(BoundsCheckKey* key) { Remove(key, key->Hash()); } - BoundsCheckTable() : ZoneHashMap(BoundsCheckKeyMatch) { } + explicit BoundsCheckTable(Zone* zone) + : ZoneHashMap(BoundsCheckKeyMatch, ZoneHashMap::kDefaultHashMapCapacity, + ZoneAllocationPolicy(zone)) { } }; @@ -3021,8 +3640,9 @@ void HGraph::EliminateRedundantBoundsChecks(HBasicBlock* bb, int32_t offset; BoundsCheckKey* key = - BoundsCheckKey::Create(bb->zone(), check, &offset); - BoundsCheckBbData** data_p = table->LookupOrInsert(key); + BoundsCheckKey::Create(zone(), check, &offset); + if (key == NULL) continue; + BoundsCheckBbData** data_p = table->LookupOrInsert(key, zone()); BoundsCheckBbData* data = *data_p; if (data == NULL) { bb_data_list = new(zone()) BoundsCheckBbData(key, @@ -3030,6 +3650,7 @@ void HGraph::EliminateRedundantBoundsChecks(HBasicBlock* bb, offset, bb, check, + check, bb_data_list, NULL); *data_p = bb_data_list; @@ -3044,14 +3665,15 @@ void HGraph::EliminateRedundantBoundsChecks(HBasicBlock* bb, int32_t new_upper_offset = offset > data->UpperOffset() ? offset : data->UpperOffset(); - bb_data_list = new(bb->zone()) BoundsCheckBbData(key, - new_lower_offset, - new_upper_offset, - bb, - check, - bb_data_list, - data); - table->Insert(key, bb_data_list); + bb_data_list = new(zone()) BoundsCheckBbData(key, + new_lower_offset, + new_upper_offset, + bb, + data->LowerCheck(), + data->UpperCheck(), + bb_data_list, + data); + table->Insert(key, bb_data_list, zone()); } } @@ -3064,7 +3686,7 @@ void HGraph::EliminateRedundantBoundsChecks(HBasicBlock* bb, data = data->NextInBasicBlock()) { data->RemoveZeroOperations(); if (data->FatherInDominatorTree()) { - table->Insert(data->Key(), data->FatherInDominatorTree()); + table->Insert(data->Key(), data->FatherInDominatorTree(), zone()); } else { table->Delete(data->Key()); } @@ -3074,14 +3696,14 @@ void HGraph::EliminateRedundantBoundsChecks(HBasicBlock* bb, void HGraph::EliminateRedundantBoundsChecks() { HPhase phase("H_Eliminate bounds checks", this); - AssertNoAllocation no_gc; - BoundsCheckTable checks_table; + BoundsCheckTable checks_table(zone()); EliminateRedundantBoundsChecks(entry_block(), &checks_table); } static void DehoistArrayIndex(ArrayInstructionInterface* array_operation) { HValue* index = array_operation->GetKey(); + if (!index->representation().IsInteger32()) return; HConstant* constant; HValue* subexpression; @@ -3136,27 +3758,11 @@ void HGraph::DehoistSimpleArrayIndexComputations() { instr != NULL; instr = instr->next()) { ArrayInstructionInterface* array_instruction = NULL; - if (instr->IsLoadKeyedFastElement()) { - HLoadKeyedFastElement* op = HLoadKeyedFastElement::cast(instr); - array_instruction = static_cast<ArrayInstructionInterface*>(op); - } else if (instr->IsLoadKeyedFastDoubleElement()) { - HLoadKeyedFastDoubleElement* op = - HLoadKeyedFastDoubleElement::cast(instr); - array_instruction = static_cast<ArrayInstructionInterface*>(op); - } else if (instr->IsLoadKeyedSpecializedArrayElement()) { - HLoadKeyedSpecializedArrayElement* op = - HLoadKeyedSpecializedArrayElement::cast(instr); - array_instruction = static_cast<ArrayInstructionInterface*>(op); - } else if (instr->IsStoreKeyedFastElement()) { - HStoreKeyedFastElement* op = HStoreKeyedFastElement::cast(instr); + if (instr->IsLoadKeyed()) { + HLoadKeyed* op = HLoadKeyed::cast(instr); array_instruction = static_cast<ArrayInstructionInterface*>(op); - } else if (instr->IsStoreKeyedFastDoubleElement()) { - HStoreKeyedFastDoubleElement* op = - HStoreKeyedFastDoubleElement::cast(instr); - array_instruction = static_cast<ArrayInstructionInterface*>(op); - } else if (instr->IsStoreKeyedSpecializedArrayElement()) { - HStoreKeyedSpecializedArrayElement* op = - HStoreKeyedSpecializedArrayElement::cast(instr); + } else if (instr->IsStoreKeyed()) { + HStoreKeyed* op = HStoreKeyed::cast(instr); array_instruction = static_cast<ArrayInstructionInterface*>(op); } else { continue; @@ -3167,6 +3773,36 @@ void HGraph::DehoistSimpleArrayIndexComputations() { } +void HGraph::DeadCodeElimination() { + HPhase phase("H_Dead code elimination", this); + ZoneList<HInstruction*> worklist(blocks_.length(), zone()); + for (int i = 0; i < blocks()->length(); ++i) { + for (HInstruction* instr = blocks()->at(i)->first(); + instr != NULL; + instr = instr->next()) { + if (instr->IsDead()) worklist.Add(instr, zone()); + } + } + + while (!worklist.is_empty()) { + HInstruction* instr = worklist.RemoveLast(); + if (FLAG_trace_dead_code_elimination) { + HeapStringAllocator allocator; + StringStream stream(&allocator); + instr->PrintNameTo(&stream); + stream.Add(" = "); + instr->PrintTo(&stream); + PrintF("[removing dead instruction %s]\n", *stream.ToCString()); + } + instr->DeleteAndReplaceWith(NULL); + for (int i = 0; i < instr->OperandCount(); ++i) { + HValue* operand = instr->OperandAt(i); + if (operand->IsDead()) worklist.Add(HInstruction::cast(operand), zone()); + } + } +} + + HInstruction* HGraphBuilder::AddInstruction(HInstruction* instr) { ASSERT(current_block() != NULL); current_block()->AddInstruction(instr); @@ -3174,7 +3810,7 @@ HInstruction* HGraphBuilder::AddInstruction(HInstruction* instr) { } -void HGraphBuilder::AddSimulate(int ast_id) { +void HGraphBuilder::AddSimulate(BailoutId ast_id) { ASSERT(current_block() != NULL); current_block()->AddSimulate(ast_id); } @@ -3195,9 +3831,9 @@ void HGraphBuilder::PushAndAdd(HInstruction* instr) { template <class Instruction> HInstruction* HGraphBuilder::PreProcessCall(Instruction* call) { int count = call->argument_count(); - ZoneList<HValue*> arguments(count); + ZoneList<HValue*> arguments(count, zone()); for (int i = 0; i < count; ++i) { - arguments.Add(Pop()); + arguments.Add(Pop(), zone()); } while (!arguments.is_empty()) { @@ -3418,28 +4054,29 @@ void HGraphBuilder::VisitReturnStatement(ReturnStatement* stmt) { ASSERT(!HasStackOverflow()); ASSERT(current_block() != NULL); ASSERT(current_block()->HasPredecessor()); + FunctionState* state = function_state(); AstContext* context = call_context(); if (context == NULL) { // Not an inlined return, so an actual one. CHECK_ALIVE(VisitForValue(stmt->expression())); HValue* result = environment()->Pop(); current_block()->FinishExit(new(zone()) HReturn(result)); - } else if (function_state()->is_construct()) { - // Return from an inlined construct call. In a test context the return - // value will always evaluate to true, in a value context the return value - // needs to be a JSObject. + } else if (state->inlining_kind() == CONSTRUCT_CALL_RETURN) { + // Return from an inlined construct call. In a test context the return value + // will always evaluate to true, in a value context the return value needs + // to be a JSObject. if (context->IsTest()) { TestContext* test = TestContext::cast(context); CHECK_ALIVE(VisitForEffect(stmt->expression())); - current_block()->Goto(test->if_true(), function_state()); + current_block()->Goto(test->if_true(), state); } else if (context->IsEffect()) { CHECK_ALIVE(VisitForEffect(stmt->expression())); - current_block()->Goto(function_return(), function_state()); + current_block()->Goto(function_return(), state); } else { ASSERT(context->IsValue()); CHECK_ALIVE(VisitForValue(stmt->expression())); HValue* return_value = Pop(); - HValue* receiver = environment()->Lookup(0); + HValue* receiver = environment()->arguments_environment()->Lookup(0); HHasInstanceTypeAndBranch* typecheck = new(zone()) HHasInstanceTypeAndBranch(return_value, FIRST_SPEC_OBJECT_TYPE, @@ -3449,31 +4086,36 @@ void HGraphBuilder::VisitReturnStatement(ReturnStatement* stmt) { typecheck->SetSuccessorAt(0, if_spec_object); typecheck->SetSuccessorAt(1, not_spec_object); current_block()->Finish(typecheck); - if_spec_object->AddLeaveInlined(return_value, - function_return(), - function_state()); - not_spec_object->AddLeaveInlined(receiver, - function_return(), - function_state()); + if_spec_object->AddLeaveInlined(return_value, state); + not_spec_object->AddLeaveInlined(receiver, state); + } + } else if (state->inlining_kind() == SETTER_CALL_RETURN) { + // Return from an inlined setter call. The returned value is never used, the + // value of an assignment is always the value of the RHS of the assignment. + CHECK_ALIVE(VisitForEffect(stmt->expression())); + if (context->IsTest()) { + HValue* rhs = environment()->arguments_environment()->Lookup(1); + context->ReturnValue(rhs); + } else if (context->IsEffect()) { + current_block()->Goto(function_return(), state); + } else { + ASSERT(context->IsValue()); + HValue* rhs = environment()->arguments_environment()->Lookup(1); + current_block()->AddLeaveInlined(rhs, state); } } else { - // Return from an inlined function, visit the subexpression in the + // Return from a normal inlined function. Visit the subexpression in the // expression context of the call. if (context->IsTest()) { TestContext* test = TestContext::cast(context); - VisitForControl(stmt->expression(), - test->if_true(), - test->if_false()); + VisitForControl(stmt->expression(), test->if_true(), test->if_false()); } else if (context->IsEffect()) { CHECK_ALIVE(VisitForEffect(stmt->expression())); - current_block()->Goto(function_return(), function_state()); + current_block()->Goto(function_return(), state); } else { ASSERT(context->IsValue()); CHECK_ALIVE(VisitForValue(stmt->expression())); - HValue* return_value = Pop(); - current_block()->AddLeaveInlined(return_value, - function_return(), - function_state()); + current_block()->AddLeaveInlined(Pop(), state); } } set_current_block(NULL); @@ -3548,7 +4190,7 @@ void HGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) { } // 2. Build all the tests, with dangling true branches - int default_id = AstNode::kNoNumber; + BailoutId default_id = BailoutId::None(); for (int i = 0; i < clause_count; ++i) { CaseClause* clause = clauses->at(i); if (clause->is_default()) { @@ -3601,9 +4243,7 @@ void HGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) { HBasicBlock* last_block = current_block(); if (not_string_block != NULL) { - int join_id = (default_id != AstNode::kNoNumber) - ? default_id - : stmt->ExitId(); + BailoutId join_id = !default_id.IsNone() ? default_id : stmt->ExitId(); last_block = CreateJoin(last_block, not_string_block, join_id); } @@ -3693,17 +4333,17 @@ bool HGraphBuilder::PreProcessOsrEntry(IterationStatement* statement) { non_osr_entry->Goto(loop_predecessor); set_current_block(osr_entry); - int osr_entry_id = statement->OsrEntryId(); + BailoutId osr_entry_id = statement->OsrEntryId(); int first_expression_index = environment()->first_expression_index(); int length = environment()->length(); ZoneList<HUnknownOSRValue*>* osr_values = - new(zone()) ZoneList<HUnknownOSRValue*>(length); + new(zone()) ZoneList<HUnknownOSRValue*>(length, zone()); for (int i = 0; i < first_expression_index; ++i) { HUnknownOSRValue* osr_value = new(zone()) HUnknownOSRValue; AddInstruction(osr_value); environment()->Bind(i, osr_value); - osr_values->Add(osr_value); + osr_values->Add(osr_value, zone()); } if (first_expression_index != length) { @@ -3712,7 +4352,7 @@ bool HGraphBuilder::PreProcessOsrEntry(IterationStatement* statement) { HUnknownOSRValue* osr_value = new(zone()) HUnknownOSRValue; AddInstruction(osr_value); environment()->Push(osr_value); - osr_values->Add(osr_value); + osr_values->Add(osr_value, zone()); } } @@ -3917,15 +4557,14 @@ void HGraphBuilder::VisitForInStatement(ForInStatement* stmt) { map, DescriptorArray::kEnumCacheBridgeCacheIndex)); - HInstruction* array_length = AddInstruction( - new(zone()) HFixedArrayBaseLength(array)); + HInstruction* enum_length = AddInstruction(new(zone()) HMapEnumLength(map)); HInstruction* start_index = AddInstruction(new(zone()) HConstant( Handle<Object>(Smi::FromInt(0)), Representation::Integer32())); Push(map); Push(array); - Push(array_length); + Push(enum_length); Push(start_index); HInstruction* index_cache = AddInstruction( @@ -3963,10 +4602,11 @@ void HGraphBuilder::VisitForInStatement(ForInStatement* stmt) { set_current_block(loop_body); HValue* key = AddInstruction( - new(zone()) HLoadKeyedFastElement( + new(zone()) HLoadKeyed( environment()->ExpressionStackAt(2), // Enum cache. environment()->ExpressionStackAt(0), // Iteration index. - HLoadKeyedFastElement::OMIT_HOLE_CHECK)); + environment()->ExpressionStackAt(0), + FAST_ELEMENTS)); // Check if the expected map still matches that of the enumerable. // If not just deoptimize. @@ -4121,8 +4761,7 @@ HGraphBuilder::GlobalPropertyAccess HGraphBuilder::LookupGlobalProperty( } Handle<GlobalObject> global(info()->global_object()); global->Lookup(*var->name(), lookup); - if (!lookup->IsFound() || - lookup->type() != NORMAL || + if (!lookup->IsNormal() || (is_store && lookup->IsReadOnly()) || lookup->holder() != *global) { return kUseGeneric; @@ -4152,8 +4791,9 @@ void HGraphBuilder::VisitVariableProxy(VariableProxy* expr) { Variable* variable = expr->var(); switch (variable->location()) { case Variable::UNALLOCATED: { - if (variable->mode() == LET || variable->mode() == CONST_HARMONY) { - return Bailout("reference to global harmony declared variable"); + if (IsLexicalVariableMode(variable->mode())) { + // TODO(rossberg): should this be an ASSERT? + return Bailout("reference to global lexical variable"); } // Handle known global constants like 'undefined' specially to avoid a // load from a global cell for them. @@ -4199,9 +4839,8 @@ void HGraphBuilder::VisitVariableProxy(VariableProxy* expr) { case Variable::LOCAL: { HValue* value = environment()->Lookup(variable); if (value == graph()->GetConstantHole()) { - ASSERT(variable->mode() == CONST || - variable->mode() == CONST_HARMONY || - variable->mode() == LET); + ASSERT(IsDeclaredVariableMode(variable->mode()) && + variable->mode() != VAR); return Bailout("reference to uninitialized variable"); } return ast_context()->ReturnValue(value); @@ -4233,9 +4872,12 @@ void HGraphBuilder::VisitRegExpLiteral(RegExpLiteral* expr) { ASSERT(!HasStackOverflow()); ASSERT(current_block() != NULL); ASSERT(current_block()->HasPredecessor()); + Handle<JSFunction> closure = function_state()->compilation_info()->closure(); + Handle<FixedArray> literals(closure->literals()); HValue* context = environment()->LookupContext(); HRegExpLiteral* instr = new(zone()) HRegExpLiteral(context, + literals, expr->pattern(), expr->flags(), expr->literal_index()); @@ -4243,6 +4885,86 @@ void HGraphBuilder::VisitRegExpLiteral(RegExpLiteral* expr) { } +static void LookupInPrototypes(Handle<Map> map, + Handle<String> name, + LookupResult* lookup) { + while (map->prototype()->IsJSObject()) { + Handle<JSObject> holder(JSObject::cast(map->prototype())); + if (!holder->HasFastProperties()) break; + map = Handle<Map>(holder->map()); + map->LookupDescriptor(*holder, *name, lookup); + if (lookup->IsFound()) return; + } + lookup->NotFound(); +} + + +// Tries to find a JavaScript accessor of the given name in the prototype chain +// starting at the given map. Return true iff there is one, including the +// corresponding AccessorPair plus its holder (which could be null when the +// accessor is found directly in the given map). +static bool LookupAccessorPair(Handle<Map> map, + Handle<String> name, + Handle<AccessorPair>* accessors, + Handle<JSObject>* holder) { + LookupResult lookup(map->GetIsolate()); + + // Check for a JavaScript accessor directly in the map. + map->LookupDescriptor(NULL, *name, &lookup); + if (lookup.IsPropertyCallbacks()) { + Handle<Object> callback(lookup.GetValueFromMap(*map)); + if (!callback->IsAccessorPair()) return false; + *accessors = Handle<AccessorPair>::cast(callback); + *holder = Handle<JSObject>(); + return true; + } + + // Everything else, e.g. a field, can't be an accessor call. + if (lookup.IsFound()) return false; + + // Check for a JavaScript accessor somewhere in the proto chain. + LookupInPrototypes(map, name, &lookup); + if (lookup.IsPropertyCallbacks()) { + Handle<Object> callback(lookup.GetValue()); + if (!callback->IsAccessorPair()) return false; + *accessors = Handle<AccessorPair>::cast(callback); + *holder = Handle<JSObject>(lookup.holder()); + return true; + } + + // We haven't found a JavaScript accessor anywhere. + return false; +} + + +static bool LookupGetter(Handle<Map> map, + Handle<String> name, + Handle<JSFunction>* getter, + Handle<JSObject>* holder) { + Handle<AccessorPair> accessors; + if (LookupAccessorPair(map, name, &accessors, holder) && + accessors->getter()->IsJSFunction()) { + *getter = Handle<JSFunction>(JSFunction::cast(accessors->getter())); + return true; + } + return false; +} + + +static bool LookupSetter(Handle<Map> map, + Handle<String> name, + Handle<JSFunction>* setter, + Handle<JSObject>* holder) { + Handle<AccessorPair> accessors; + if (LookupAccessorPair(map, name, &accessors, holder) && + accessors->setter()->IsJSFunction()) { + *setter = Handle<JSFunction>(JSFunction::cast(accessors->setter())); + return true; + } + return false; +} + + // Determines whether the given array or object literal boilerplate satisfies // all limits to be considered for fast deep-copying and computes the total // size of all objects that are part of the graph. @@ -4258,7 +4980,7 @@ static bool IsFastLiteral(Handle<JSObject> boilerplate, elements->map() != boilerplate->GetHeap()->fixed_cow_array_map()) { if (boilerplate->HasFastDoubleElements()) { *total_size += FixedDoubleArray::SizeFor(elements->length()); - } else if (boilerplate->HasFastElements()) { + } else if (boilerplate->HasFastObjectElements()) { Handle<FixedArray> fast_elements = Handle<FixedArray>::cast(elements); int length = elements->length(); for (int i = 0; i < length; i++) { @@ -4341,7 +5063,7 @@ void HGraphBuilder::VisitObjectLiteral(ObjectLiteral* expr) { // of the property values and is the value of the entire expression. PushAndAdd(literal); - expr->CalculateEmitStore(); + expr->CalculateEmitStore(zone()); for (int i = 0; i < expr->properties()->length(); i++) { ObjectLiteral::Property* property = expr->properties()->at(i); @@ -4360,7 +5082,23 @@ void HGraphBuilder::VisitObjectLiteral(ObjectLiteral* expr) { property->RecordTypeFeedback(oracle()); CHECK_ALIVE(VisitForValue(value)); HValue* value = Pop(); - HInstruction* store = BuildStoreNamed(literal, value, property); + Handle<Map> map = property->GetReceiverType(); + Handle<String> name = property->key()->AsPropertyName(); + HInstruction* store; + if (map.is_null()) { + // If we don't know the monomorphic type, do a generic store. + CHECK_ALIVE(store = BuildStoreNamedGeneric(literal, name, value)); + } else { +#if DEBUG + Handle<JSFunction> setter; + Handle<JSObject> holder; + ASSERT(!LookupSetter(map, name, &setter, &holder)); +#endif + CHECK_ALIVE(store = BuildStoreNamedMonomorphic(literal, + name, + value, + map)); + } AddInstruction(store); if (store->HasObservableSideEffects()) AddSimulate(key->id()); } else { @@ -4457,7 +5195,9 @@ void HGraphBuilder::VisitArrayLiteral(ArrayLiteral* expr) { HValue* value = Pop(); if (!Smi::IsValid(i)) return Bailout("Non-smi key in array literal"); - elements = new(zone()) HLoadElements(literal); + // Pass in literal as dummy depedency, since the receiver always has + // elements. + elements = new(zone()) HLoadElements(literal, literal); AddInstruction(elements); HValue* key = AddInstruction( @@ -4465,22 +5205,21 @@ void HGraphBuilder::VisitArrayLiteral(ArrayLiteral* expr) { Representation::Integer32())); switch (boilerplate_elements_kind) { - case FAST_SMI_ONLY_ELEMENTS: + case FAST_SMI_ELEMENTS: + case FAST_HOLEY_SMI_ELEMENTS: // Smi-only arrays need a smi check. AddInstruction(new(zone()) HCheckSmi(value)); // Fall through. case FAST_ELEMENTS: - AddInstruction(new(zone()) HStoreKeyedFastElement( + case FAST_HOLEY_ELEMENTS: + case FAST_DOUBLE_ELEMENTS: + case FAST_HOLEY_DOUBLE_ELEMENTS: + AddInstruction(new(zone()) HStoreKeyed( elements, key, value, boilerplate_elements_kind)); break; - case FAST_DOUBLE_ELEMENTS: - AddInstruction(new(zone()) HStoreKeyedFastDoubleElement(elements, - key, - value)); - break; default: UNREACHABLE(); break; @@ -4497,10 +5236,17 @@ static bool ComputeLoadStoreField(Handle<Map> type, Handle<String> name, LookupResult* lookup, bool is_store) { - type->LookupInDescriptors(NULL, *name, lookup); - if (!lookup->IsFound()) return false; - if (lookup->type() == FIELD) return true; - return is_store && (lookup->type() == MAP_TRANSITION) && + // If we directly find a field, the access can be inlined. + type->LookupDescriptor(NULL, *name, lookup); + if (lookup->IsField()) return true; + + // For a load, we are out of luck if there is no such field. + if (!is_store) return false; + + // 2nd chance: A store into a non-existent field can still be inlined if we + // have a matching transition and some room left in the object. + type->LookupTransition(NULL, *name, lookup); + return lookup->IsTransitionToField(*type) && (type->unused_property_fields() > 0); } @@ -4508,8 +5254,8 @@ static bool ComputeLoadStoreField(Handle<Map> type, static int ComputeLoadStoreFieldIndex(Handle<Map> type, Handle<String> name, LookupResult* lookup) { - ASSERT(lookup->type() == FIELD || lookup->type() == MAP_TRANSITION); - if (lookup->type() == FIELD) { + ASSERT(lookup->IsField() || lookup->IsTransitionToField(*type)); + if (lookup->IsField()) { return lookup->GetLocalFieldIndexFromMap(*type); } else { Map* transition = lookup->GetTransitionMapFromMap(*type); @@ -4518,31 +5264,60 @@ static int ComputeLoadStoreFieldIndex(Handle<Map> type, } +void HGraphBuilder::AddCheckMapsWithTransitions(HValue* object, + Handle<Map> map) { + AddInstruction(new(zone()) HCheckNonSmi(object)); + AddInstruction(HCheckMaps::NewWithTransitions(object, map, zone())); +} + + HInstruction* HGraphBuilder::BuildStoreNamedField(HValue* object, Handle<String> name, HValue* value, - Handle<Map> type, - LookupResult* lookup, - bool smi_and_map_check) { - if (smi_and_map_check) { - AddInstruction(new(zone()) HCheckNonSmi(object)); - AddInstruction(HCheckMaps::NewWithTransitions(object, type)); + Handle<Map> map, + LookupResult* lookup) { + ASSERT(lookup->IsFound()); + // If the property does not exist yet, we have to check that it wasn't made + // readonly or turned into a setter by some meanwhile modifications on the + // prototype chain. + if (!lookup->IsProperty() && map->prototype()->IsJSReceiver()) { + Object* proto = map->prototype(); + // First check that the prototype chain isn't affected already. + LookupResult proto_result(isolate()); + proto->Lookup(*name, &proto_result); + if (proto_result.IsProperty()) { + // If the inherited property could induce readonly-ness, bail out. + if (proto_result.IsReadOnly() || !proto_result.IsCacheable()) { + Bailout("improper object on prototype chain for store"); + return NULL; + } + // We only need to check up to the preexisting property. + proto = proto_result.holder(); + } else { + // Otherwise, find the top prototype. + while (proto->GetPrototype()->IsJSObject()) proto = proto->GetPrototype(); + ASSERT(proto->GetPrototype()->IsNull()); + } + ASSERT(proto->IsJSObject()); + AddInstruction(new(zone()) HCheckPrototypeMaps( + Handle<JSObject>(JSObject::cast(map->prototype())), + Handle<JSObject>(JSObject::cast(proto)))); } - int index = ComputeLoadStoreFieldIndex(type, name, lookup); + int index = ComputeLoadStoreFieldIndex(map, name, lookup); bool is_in_object = index < 0; int offset = index * kPointerSize; if (index < 0) { // Negative property indices are in-object properties, indexed // from the end of the fixed part of the object. - offset += type->instance_size(); + offset += map->instance_size(); } else { offset += FixedArray::kHeaderSize; } HStoreNamedField* instr = new(zone()) HStoreNamedField(object, name, value, is_in_object, offset); - if (lookup->type() == MAP_TRANSITION) { - Handle<Map> transition(lookup->GetTransitionMapFromMap(*type)); + if (lookup->IsTransitionToField(*map)) { + Handle<Map> transition(lookup->GetTransitionMapFromMap(*map)); instr->set_transition(transition); // TODO(fschneider): Record the new map type of the object in the IR to // enable elimination of redundant checks after the transition store. @@ -4565,44 +5340,31 @@ HInstruction* HGraphBuilder::BuildStoreNamedGeneric(HValue* object, } -HInstruction* HGraphBuilder::BuildStoreNamed(HValue* object, +HInstruction* HGraphBuilder::BuildCallSetter(HValue* object, HValue* value, - ObjectLiteral::Property* prop) { - Literal* key = prop->key()->AsLiteral(); - Handle<String> name = Handle<String>::cast(key->handle()); - ASSERT(!name.is_null()); - - LookupResult lookup(isolate()); - Handle<Map> type = prop->GetReceiverType(); - bool is_monomorphic = prop->IsMonomorphic() && - ComputeLoadStoreField(type, name, &lookup, true); - - return is_monomorphic - ? BuildStoreNamedField(object, name, value, type, &lookup, - true) // Needs smi and map check. - : BuildStoreNamedGeneric(object, name, value); + Handle<Map> map, + Handle<JSFunction> setter, + Handle<JSObject> holder) { + AddCheckConstantFunction(holder, object, map); + AddInstruction(new(zone()) HPushArgument(object)); + AddInstruction(new(zone()) HPushArgument(value)); + return new(zone()) HCallConstantFunction(setter, 2); } -HInstruction* HGraphBuilder::BuildStoreNamed(HValue* object, - HValue* value, - Expression* expr) { - Property* prop = (expr->AsProperty() != NULL) - ? expr->AsProperty() - : expr->AsAssignment()->target()->AsProperty(); - Literal* key = prop->key()->AsLiteral(); - Handle<String> name = Handle<String>::cast(key->handle()); - ASSERT(!name.is_null()); - +HInstruction* HGraphBuilder::BuildStoreNamedMonomorphic(HValue* object, + Handle<String> name, + HValue* value, + Handle<Map> map) { + // Handle a store to a known field. LookupResult lookup(isolate()); - SmallMapList* types = expr->GetReceiverTypes(); - bool is_monomorphic = expr->IsMonomorphic() && - ComputeLoadStoreField(types->first(), name, &lookup, true); + if (ComputeLoadStoreField(map, name, &lookup, true)) { + AddCheckMapsWithTransitions(object, map); + return BuildStoreNamedField(object, name, value, map, &lookup); + } - return is_monomorphic - ? BuildStoreNamedField(object, name, value, types->first(), &lookup, - true) // Needs smi and map check. - : BuildStoreNamedGeneric(object, name, value); + // No luck, do a generic store. + return BuildStoreNamedGeneric(object, name, value); } @@ -4642,16 +5404,18 @@ void HGraphBuilder::HandlePolymorphicLoadNamedField(Property* expr, // Use monomorphic load if property lookup results in the same field index // for all maps. Requires special map check on the set of all handled maps. + AddInstruction(new(zone()) HCheckNonSmi(object)); HInstruction* instr; if (count == types->length() && is_monomorphic_field) { - AddInstruction(new(zone()) HCheckMaps(object, types)); - instr = BuildLoadNamedField(object, expr, map, &lookup, false); + AddInstruction(new(zone()) HCheckMaps(object, types, zone())); + instr = BuildLoadNamedField(object, map, &lookup); } else { HValue* context = environment()->LookupContext(); instr = new(zone()) HLoadNamedFieldPolymorphic(context, object, types, - name); + name, + zone()); } instr->set_position(expr->position()); @@ -4685,8 +5449,9 @@ void HGraphBuilder::HandlePolymorphicStoreNamedField(Assignment* expr, current_block()->Finish(compare); set_current_block(if_true); - HInstruction* instr = - BuildStoreNamedField(object, name, value, map, &lookup, false); + HInstruction* instr; + CHECK_ALIVE(instr = + BuildStoreNamedField(object, name, value, map, &lookup)); instr->set_position(expr->position()); // Goto will add the HSimulate for the store. AddInstruction(instr); @@ -4737,41 +5502,66 @@ void HGraphBuilder::HandlePolymorphicStoreNamedField(Assignment* expr, void HGraphBuilder::HandlePropertyAssignment(Assignment* expr) { Property* prop = expr->target()->AsProperty(); ASSERT(prop != NULL); - expr->RecordTypeFeedback(oracle()); + expr->RecordTypeFeedback(oracle(), zone()); CHECK_ALIVE(VisitForValue(prop->obj())); - HValue* value = NULL; - HInstruction* instr = NULL; - if (prop->key()->IsPropertyName()) { // Named store. CHECK_ALIVE(VisitForValue(expr->value())); - value = Pop(); - HValue* object = Pop(); + HValue* value = environment()->ExpressionStackAt(0); + HValue* object = environment()->ExpressionStackAt(1); Literal* key = prop->key()->AsLiteral(); Handle<String> name = Handle<String>::cast(key->handle()); ASSERT(!name.is_null()); + HInstruction* instr = NULL; SmallMapList* types = expr->GetReceiverTypes(); - LookupResult lookup(isolate()); - - if (expr->IsMonomorphic()) { - instr = BuildStoreNamed(object, value, expr); + bool monomorphic = expr->IsMonomorphic(); + Handle<Map> map; + if (monomorphic) { + map = types->first(); + if (map->is_dictionary_map()) monomorphic = false; + } + if (monomorphic) { + Handle<JSFunction> setter; + Handle<JSObject> holder; + if (LookupSetter(map, name, &setter, &holder)) { + AddCheckConstantFunction(holder, object, map); + if (FLAG_inline_accessors && TryInlineSetter(setter, expr, value)) { + return; + } + Drop(2); + AddInstruction(new(zone()) HPushArgument(object)); + AddInstruction(new(zone()) HPushArgument(value)); + instr = new(zone()) HCallConstantFunction(setter, 2); + } else { + Drop(2); + CHECK_ALIVE(instr = BuildStoreNamedMonomorphic(object, + name, + value, + map)); + } } else if (types != NULL && types->length() > 1) { - HandlePolymorphicStoreNamedField(expr, object, value, types, name); - return; - + Drop(2); + return HandlePolymorphicStoreNamedField(expr, object, value, types, name); } else { + Drop(2); instr = BuildStoreNamedGeneric(object, name, value); } + Push(value); + instr->set_position(expr->position()); + AddInstruction(instr); + if (instr->HasObservableSideEffects()) AddSimulate(expr->AssignmentId()); + return ast_context()->ReturnValue(Pop()); + } else { // Keyed store. CHECK_ALIVE(VisitForValue(prop->key())); CHECK_ALIVE(VisitForValue(expr->value())); - value = Pop(); + HValue* value = Pop(); HValue* key = Pop(); HValue* object = Pop(); bool has_side_effects = false; @@ -4784,11 +5574,6 @@ void HGraphBuilder::HandlePropertyAssignment(Assignment* expr) { AddSimulate(expr->AssignmentId()); return ast_context()->ReturnValue(Pop()); } - Push(value); - instr->set_position(expr->position()); - AddInstruction(instr); - if (instr->HasObservableSideEffects()) AddSimulate(expr->AssignmentId()); - return ast_context()->ReturnValue(Pop()); } @@ -4798,7 +5583,7 @@ void HGraphBuilder::HandlePropertyAssignment(Assignment* expr) { void HGraphBuilder::HandleGlobalVariableAssignment(Variable* var, HValue* value, int position, - int ast_id) { + BailoutId ast_id) { LookupResult lookup(isolate()); GlobalPropertyAccess type = LookupGlobalProperty(var, &lookup, true); if (type == kUseCell) { @@ -4911,23 +5696,36 @@ void HGraphBuilder::HandleCompoundAssignment(Assignment* expr) { return ast_context()->ReturnValue(Pop()); } else if (prop != NULL) { - prop->RecordTypeFeedback(oracle()); + prop->RecordTypeFeedback(oracle(), zone()); if (prop->key()->IsPropertyName()) { // Named property. CHECK_ALIVE(VisitForValue(prop->obj())); - HValue* obj = Top(); - - HInstruction* load = NULL; - if (prop->IsMonomorphic()) { - Handle<String> name = prop->key()->AsLiteral()->AsPropertyName(); - Handle<Map> map = prop->GetReceiverTypes()->first(); - load = BuildLoadNamed(obj, prop, map, name); + HValue* object = Top(); + + Handle<String> name = prop->key()->AsLiteral()->AsPropertyName(); + Handle<Map> map; + HInstruction* load; + bool monomorphic = prop->IsMonomorphic(); + if (monomorphic) { + map = prop->GetReceiverTypes()->first(); + // We can't generate code for a monomorphic dict mode load so + // just pretend it is not monomorphic. + if (map->is_dictionary_map()) monomorphic = false; + } + if (monomorphic) { + Handle<JSFunction> getter; + Handle<JSObject> holder; + if (LookupGetter(map, name, &getter, &holder)) { + load = BuildCallGetter(object, map, getter, holder); + } else { + load = BuildLoadNamedMonomorphic(object, name, prop, map); + } } else { - load = BuildLoadNamedGeneric(obj, prop); + load = BuildLoadNamedGeneric(object, name, prop); } PushAndAdd(load); - if (load->HasObservableSideEffects()) AddSimulate(expr->CompoundLoadId()); + if (load->HasObservableSideEffects()) AddSimulate(prop->LoadId()); CHECK_ALIVE(VisitForValue(expr->value())); HValue* right = Pop(); @@ -4937,7 +5735,22 @@ void HGraphBuilder::HandleCompoundAssignment(Assignment* expr) { PushAndAdd(instr); if (instr->HasObservableSideEffects()) AddSimulate(operation->id()); - HInstruction* store = BuildStoreNamed(obj, instr, prop); + HInstruction* store; + if (!monomorphic) { + // If we don't know the monomorphic type, do a generic store. + CHECK_ALIVE(store = BuildStoreNamedGeneric(object, name, instr)); + } else { + Handle<JSFunction> setter; + Handle<JSObject> holder; + if (LookupSetter(map, name, &setter, &holder)) { + store = BuildCallSetter(object, instr, map, setter, holder); + } else { + CHECK_ALIVE(store = BuildStoreNamedMonomorphic(object, + name, + instr, + map)); + } + } AddInstruction(store); // Drop the simulated receiver and value. Return the value. Drop(2); @@ -4954,11 +5767,11 @@ void HGraphBuilder::HandleCompoundAssignment(Assignment* expr) { bool has_side_effects = false; HValue* load = HandleKeyedElementAccess( - obj, key, NULL, prop, expr->CompoundLoadId(), RelocInfo::kNoPosition, + obj, key, NULL, prop, prop->LoadId(), RelocInfo::kNoPosition, false, // is_store &has_side_effects); Push(load); - if (has_side_effects) AddSimulate(expr->CompoundLoadId()); + if (has_side_effects) AddSimulate(prop->LoadId()); CHECK_ALIVE(VisitForValue(expr->value())); @@ -4969,7 +5782,7 @@ void HGraphBuilder::HandleCompoundAssignment(Assignment* expr) { PushAndAdd(instr); if (instr->HasObservableSideEffects()) AddSimulate(operation->id()); - expr->RecordTypeFeedback(oracle()); + expr->RecordTypeFeedback(oracle(), zone()); HandleKeyedElementAccess(obj, key, instr, expr, expr->AssignmentId(), RelocInfo::kNoPosition, true, // is_store @@ -5017,7 +5830,7 @@ void HGraphBuilder::VisitAssignment(Assignment* expr) { // 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)); + AddInstruction(new(zone()) HUseConst(old_value)); } } else if (var->mode() == CONST_HARMONY) { if (expr->op() != Token::INIT_CONST_HARMONY) { @@ -5138,20 +5951,13 @@ void HGraphBuilder::VisitThrow(Throw* expr) { HLoadNamedField* HGraphBuilder::BuildLoadNamedField(HValue* object, - Property* expr, - Handle<Map> type, - LookupResult* lookup, - bool smi_and_map_check) { - if (smi_and_map_check) { - AddInstruction(new(zone()) HCheckNonSmi(object)); - AddInstruction(HCheckMaps::NewWithTransitions(object, type)); - } - - int index = lookup->GetLocalFieldIndexFromMap(*type); + Handle<Map> map, + LookupResult* lookup) { + int index = lookup->GetLocalFieldIndexFromMap(*map); if (index < 0) { // Negative property indices are in-object properties, indexed // from the end of the fixed part of the object. - int offset = (index * kPointerSize) + type->instance_size(); + int offset = (index * kPointerSize) + map->instance_size(); return new(zone()) HLoadNamedField(object, true, offset); } else { // Non-negative property indices are in the properties array. @@ -5161,39 +5967,62 @@ HLoadNamedField* HGraphBuilder::BuildLoadNamedField(HValue* object, } -HInstruction* HGraphBuilder::BuildLoadNamedGeneric(HValue* obj, +HInstruction* HGraphBuilder::BuildLoadNamedGeneric(HValue* object, + Handle<String> name, Property* expr) { if (expr->IsUninitialized() && !FLAG_always_opt) { AddInstruction(new(zone()) HSoftDeoptimize); current_block()->MarkAsDeoptimizing(); } - ASSERT(expr->key()->IsPropertyName()); - Handle<Object> name = expr->key()->AsLiteral()->handle(); HValue* context = environment()->LookupContext(); - return new(zone()) HLoadNamedGeneric(context, obj, name); + return new(zone()) HLoadNamedGeneric(context, object, name); } -HInstruction* HGraphBuilder::BuildLoadNamed(HValue* obj, - Property* expr, - Handle<Map> map, - Handle<String> name) { +HInstruction* HGraphBuilder::BuildCallGetter(HValue* object, + Handle<Map> map, + Handle<JSFunction> getter, + Handle<JSObject> holder) { + AddCheckConstantFunction(holder, object, map); + AddInstruction(new(zone()) HPushArgument(object)); + return new(zone()) HCallConstantFunction(getter, 1); +} + + +HInstruction* HGraphBuilder::BuildLoadNamedMonomorphic(HValue* object, + Handle<String> name, + Property* expr, + Handle<Map> map) { + // Handle a load from a known field. + ASSERT(!map->is_dictionary_map()); LookupResult lookup(isolate()); - map->LookupInDescriptors(NULL, *name, &lookup); - if (lookup.IsFound() && lookup.type() == FIELD) { - return BuildLoadNamedField(obj, - expr, - map, - &lookup, - true); - } else if (lookup.IsFound() && lookup.type() == CONSTANT_FUNCTION) { - AddInstruction(new(zone()) HCheckNonSmi(obj)); - AddInstruction(HCheckMaps::NewWithTransitions(obj, map)); + map->LookupDescriptor(NULL, *name, &lookup); + if (lookup.IsField()) { + AddCheckMapsWithTransitions(object, map); + return BuildLoadNamedField(object, map, &lookup); + } + + // Handle a load of a constant known function. + if (lookup.IsConstantFunction()) { + AddCheckMapsWithTransitions(object, map); Handle<JSFunction> function(lookup.GetConstantFunctionFromMap(*map)); return new(zone()) HConstant(function, Representation::Tagged()); - } else { - return BuildLoadNamedGeneric(obj, expr); } + + // Handle a load from a known field somewhere in the protoype chain. + LookupInPrototypes(map, name, &lookup); + if (lookup.IsField()) { + Handle<JSObject> prototype(JSObject::cast(map->prototype())); + Handle<JSObject> holder(lookup.holder()); + Handle<Map> holder_map(holder->map()); + AddCheckMapsWithTransitions(object, map); + HInstruction* holder_value = + AddInstruction(new(zone()) HCheckPrototypeMaps(prototype, holder)); + return BuildLoadNamedField(holder_value, holder_map, &lookup); + } + + // No luck, do a generic load. + return BuildLoadNamedGeneric(object, name, expr); } @@ -5208,6 +6037,7 @@ HInstruction* HGraphBuilder::BuildExternalArrayElementAccess( HValue* external_elements, HValue* checked_key, HValue* val, + HValue* dependency, ElementsKind elements_kind, bool is_store) { if (is_store) { @@ -5235,20 +6065,31 @@ HInstruction* HGraphBuilder::BuildExternalArrayElementAccess( case EXTERNAL_FLOAT_ELEMENTS: case EXTERNAL_DOUBLE_ELEMENTS: break; - case FAST_SMI_ONLY_ELEMENTS: + case FAST_SMI_ELEMENTS: case FAST_ELEMENTS: case FAST_DOUBLE_ELEMENTS: + case FAST_HOLEY_SMI_ELEMENTS: + case FAST_HOLEY_ELEMENTS: + case FAST_HOLEY_DOUBLE_ELEMENTS: case DICTIONARY_ELEMENTS: case NON_STRICT_ARGUMENTS_ELEMENTS: UNREACHABLE(); break; } - return new(zone()) HStoreKeyedSpecializedArrayElement( - external_elements, checked_key, val, elements_kind); + return new(zone()) HStoreKeyed(external_elements, + checked_key, + val, + elements_kind); } else { ASSERT(val == NULL); - return new(zone()) HLoadKeyedSpecializedArrayElement( - external_elements, checked_key, elements_kind); + HLoadKeyed* load = + new(zone()) HLoadKeyed( + external_elements, checked_key, dependency, elements_kind); + if (FLAG_opt_safe_uint32_operations && + elements_kind == EXTERNAL_UNSIGNED_INT_ELEMENTS) { + graph()->RecordUint32Instruction(load); + } + return load; } } @@ -5256,20 +6097,22 @@ HInstruction* HGraphBuilder::BuildExternalArrayElementAccess( HInstruction* HGraphBuilder::BuildFastElementAccess(HValue* elements, HValue* checked_key, HValue* val, + HValue* load_dependency, ElementsKind elements_kind, bool is_store) { if (is_store) { ASSERT(val != NULL); switch (elements_kind) { - case FAST_DOUBLE_ELEMENTS: - return new(zone()) HStoreKeyedFastDoubleElement( - elements, checked_key, val); - case FAST_SMI_ONLY_ELEMENTS: + case FAST_SMI_ELEMENTS: + case FAST_HOLEY_SMI_ELEMENTS: // Smi-only arrays need a smi check. AddInstruction(new(zone()) HCheckSmi(val)); // Fall through. case FAST_ELEMENTS: - return new(zone()) HStoreKeyedFastElement( + case FAST_HOLEY_ELEMENTS: + case FAST_DOUBLE_ELEMENTS: + case FAST_HOLEY_DOUBLE_ELEMENTS: + return new(zone()) HStoreKeyed( elements, checked_key, val, elements_kind); default: UNREACHABLE(); @@ -5277,57 +6120,144 @@ HInstruction* HGraphBuilder::BuildFastElementAccess(HValue* elements, } } // It's an element load (!is_store). - if (elements_kind == FAST_DOUBLE_ELEMENTS) { - return new(zone()) HLoadKeyedFastDoubleElement(elements, checked_key); - } else { // FAST_ELEMENTS or FAST_SMI_ONLY_ELEMENTS. - return new(zone()) HLoadKeyedFastElement(elements, checked_key); - } + return new(zone()) HLoadKeyed(elements, + checked_key, + load_dependency, + elements_kind); } HInstruction* HGraphBuilder::BuildMonomorphicElementAccess(HValue* object, HValue* key, HValue* val, + HValue* dependency, Handle<Map> map, bool is_store) { - HInstruction* mapcheck = AddInstruction(new(zone()) HCheckMaps(object, map)); - bool fast_smi_only_elements = map->has_fast_smi_only_elements(); - bool fast_elements = map->has_fast_elements(); - HInstruction* elements = AddInstruction(new(zone()) HLoadElements(object)); + HCheckMaps* mapcheck = new(zone()) HCheckMaps(object, map, + zone(), dependency); + AddInstruction(mapcheck); + if (dependency) { + mapcheck->ClearGVNFlag(kDependsOnElementsKind); + } + return BuildUncheckedMonomorphicElementAccess(object, key, val, + mapcheck, map, is_store); +} + + +HInstruction* HGraphBuilder::BuildUncheckedMonomorphicElementAccess( + HValue* object, + HValue* key, + HValue* val, + HCheckMaps* mapcheck, + Handle<Map> map, + bool is_store) { + // No GVNFlag is necessary for ElementsKind if there is an explicit dependency + // on a HElementsTransition instruction. The flag can also be removed if the + // map to check has FAST_HOLEY_ELEMENTS, since there can be no further + // ElementsKind transitions. Finally, the dependency can be removed for stores + // for FAST_ELEMENTS, since a transition to HOLEY elements won't change the + // generated store code. + if ((map->elements_kind() == FAST_HOLEY_ELEMENTS) || + (map->elements_kind() == FAST_ELEMENTS && is_store)) { + mapcheck->ClearGVNFlag(kDependsOnElementsKind); + } + bool fast_smi_only_elements = map->has_fast_smi_elements(); + bool fast_elements = map->has_fast_object_elements(); + HInstruction* elements = + AddInstruction(new(zone()) HLoadElements(object, mapcheck)); if (is_store && (fast_elements || fast_smi_only_elements)) { - AddInstruction(new(zone()) HCheckMaps( - elements, isolate()->factory()->fixed_array_map())); + HCheckMaps* check_cow_map = new(zone()) HCheckMaps( + elements, isolate()->factory()->fixed_array_map(), zone()); + check_cow_map->ClearGVNFlag(kDependsOnElementsKind); + AddInstruction(check_cow_map); } HInstruction* length = NULL; HInstruction* checked_key = NULL; if (map->has_external_array_elements()) { length = AddInstruction(new(zone()) HFixedArrayBaseLength(elements)); - checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length)); + checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length, + ALLOW_SMI_KEY)); HLoadExternalArrayPointer* external_elements = new(zone()) HLoadExternalArrayPointer(elements); AddInstruction(external_elements); - return BuildExternalArrayElementAccess(external_elements, checked_key, - val, map->elements_kind(), is_store); + return BuildExternalArrayElementAccess( + external_elements, checked_key, val, mapcheck, + map->elements_kind(), is_store); } ASSERT(fast_smi_only_elements || fast_elements || map->has_fast_double_elements()); if (map->instance_type() == JS_ARRAY_TYPE) { - length = AddInstruction(new(zone()) HJSArrayLength(object, mapcheck)); + length = AddInstruction(new(zone()) HJSArrayLength(object, mapcheck, + HType::Smi())); } else { length = AddInstruction(new(zone()) HFixedArrayBaseLength(elements)); } - checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length)); - return BuildFastElementAccess(elements, checked_key, val, + checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length, + ALLOW_SMI_KEY)); + return BuildFastElementAccess(elements, checked_key, val, mapcheck, map->elements_kind(), is_store); } +HInstruction* HGraphBuilder::TryBuildConsolidatedElementLoad( + HValue* object, + HValue* key, + HValue* val, + SmallMapList* maps) { + // For polymorphic loads of similar elements kinds (i.e. all tagged or all + // double), always use the "worst case" code without a transition. This is + // much faster than transitioning the elements to the worst case, trading a + // HTransitionElements for a HCheckMaps, and avoiding mutation of the array. + bool has_double_maps = false; + bool has_smi_or_object_maps = false; + bool has_js_array_access = false; + bool has_non_js_array_access = false; + Handle<Map> most_general_consolidated_map; + for (int i = 0; i < maps->length(); ++i) { + Handle<Map> map = maps->at(i); + // Don't allow mixing of JSArrays with JSObjects. + if (map->instance_type() == JS_ARRAY_TYPE) { + if (has_non_js_array_access) return NULL; + has_js_array_access = true; + } else if (has_js_array_access) { + return NULL; + } else { + has_non_js_array_access = true; + } + // Don't allow mixed, incompatible elements kinds. + if (map->has_fast_double_elements()) { + if (has_smi_or_object_maps) return NULL; + has_double_maps = true; + } else if (map->has_fast_smi_or_object_elements()) { + if (has_double_maps) return NULL; + has_smi_or_object_maps = true; + } else { + return NULL; + } + // Remember the most general elements kind, the code for its load will + // properly handle all of the more specific cases. + if ((i == 0) || IsMoreGeneralElementsKindTransition( + most_general_consolidated_map->elements_kind(), + map->elements_kind())) { + most_general_consolidated_map = map; + } + } + if (!has_double_maps && !has_smi_or_object_maps) return NULL; + + HCheckMaps* check_maps = new(zone()) HCheckMaps(object, maps, zone()); + AddInstruction(check_maps); + HInstruction* instr = BuildUncheckedMonomorphicElementAccess( + object, key, val, check_maps, most_general_consolidated_map, false); + return instr; +} + + HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object, HValue* key, HValue* val, Expression* prop, - int ast_id, + BailoutId ast_id, int position, bool is_store, bool* has_side_effects) { @@ -5336,6 +6266,19 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object, SmallMapList* maps = prop->GetReceiverTypes(); bool todo_external_array = false; + if (!is_store) { + HInstruction* consolidated_load = + TryBuildConsolidatedElementLoad(object, key, val, maps); + if (consolidated_load != NULL) { + AddInstruction(consolidated_load); + *has_side_effects |= consolidated_load->HasObservableSideEffects(); + if (position != RelocInfo::kNoPosition) { + consolidated_load->set_position(position); + } + return consolidated_load; + } + } + static const int kNumElementTypes = kElementsKindCount; bool type_todo[kNumElementTypes]; for (int i = 0; i < kNumElementTypes; ++i) { @@ -5349,8 +6292,8 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object, for (int i = 0; i < maps->length(); ++i) { Handle<Map> map = maps->at(i); ElementsKind elements_kind = map->elements_kind(); - if (elements_kind == FAST_DOUBLE_ELEMENTS || - elements_kind == FAST_ELEMENTS) { + if (IsFastElementsKind(elements_kind) && + elements_kind != GetInitialFastElementsKind()) { possible_transitioned_maps.Add(map); } } @@ -5364,15 +6307,20 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object, int num_untransitionable_maps = 0; Handle<Map> untransitionable_map; + HTransitionElementsKind* transition = NULL; for (int i = 0; i < maps->length(); ++i) { Handle<Map> map = maps->at(i); ASSERT(map->IsMap()); if (!transition_target.at(i).is_null()) { - AddInstruction(new(zone()) HTransitionElementsKind( - object, map, transition_target.at(i))); + ASSERT(Map::IsValidElementsTransition( + map->elements_kind(), + transition_target.at(i)->elements_kind())); + transition = new(zone()) HTransitionElementsKind( + object, map, transition_target.at(i)); + AddInstruction(transition); } else { type_todo[map->elements_kind()] = true; - if (map->elements_kind() >= FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND) { + if (IsExternalArrayElementsKind(map->elements_kind())) { todo_external_array = true; } num_untransitionable_maps++; @@ -5389,37 +6337,36 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object, : BuildLoadKeyedGeneric(object, key)); } else { instr = AddInstruction(BuildMonomorphicElementAccess( - object, key, val, untransitionable_map, is_store)); + object, key, val, transition, untransitionable_map, is_store)); } *has_side_effects |= instr->HasObservableSideEffects(); - instr->set_position(position); + if (position != RelocInfo::kNoPosition) instr->set_position(position); return is_store ? NULL : instr; } - AddInstruction(HCheckInstanceType::NewIsSpecObject(object)); + HInstruction* checkspec = + AddInstruction(HCheckInstanceType::NewIsSpecObject(object, zone())); HBasicBlock* join = graph()->CreateBasicBlock(); HInstruction* elements_kind_instr = AddInstruction(new(zone()) HElementsKind(object)); - HCompareConstantEqAndBranch* elements_kind_branch = NULL; - HInstruction* elements = AddInstruction(new(zone()) HLoadElements(object)); + HInstruction* elements = + AddInstruction(new(zone()) HLoadElements(object, checkspec)); HLoadExternalArrayPointer* external_elements = NULL; HInstruction* checked_key = NULL; - // Generated code assumes that FAST_SMI_ONLY_ELEMENTS, FAST_ELEMENTS, - // FAST_DOUBLE_ELEMENTS and DICTIONARY_ELEMENTS are handled before external - // arrays. - STATIC_ASSERT(FAST_SMI_ONLY_ELEMENTS < FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND); - STATIC_ASSERT(FAST_ELEMENTS < FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND); + // Generated code assumes that FAST_* and DICTIONARY_ELEMENTS ElementsKinds + // are handled before external arrays. + STATIC_ASSERT(FAST_SMI_ELEMENTS < FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND); + STATIC_ASSERT(FAST_HOLEY_ELEMENTS < FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND); STATIC_ASSERT(FAST_DOUBLE_ELEMENTS < FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND); STATIC_ASSERT(DICTIONARY_ELEMENTS < FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND); for (ElementsKind elements_kind = FIRST_ELEMENTS_KIND; elements_kind <= LAST_ELEMENTS_KIND; elements_kind = ElementsKind(elements_kind + 1)) { - // After having handled FAST_ELEMENTS, FAST_SMI_ONLY_ELEMENTS, - // FAST_DOUBLE_ELEMENTS and DICTIONARY_ELEMENTS, we need to add some code - // that's executed for all external array cases. + // After having handled FAST_* and DICTIONARY_ELEMENTS, we need to add some + // code that's executed for all external array cases. STATIC_ASSERT(LAST_EXTERNAL_ARRAY_ELEMENTS_KIND == LAST_ELEMENTS_KIND); if (elements_kind == FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND @@ -5433,21 +6380,20 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object, if (type_todo[elements_kind]) { HBasicBlock* if_true = graph()->CreateBasicBlock(); HBasicBlock* if_false = graph()->CreateBasicBlock(); - elements_kind_branch = new(zone()) HCompareConstantEqAndBranch( - elements_kind_instr, elements_kind, Token::EQ_STRICT); + HCompareConstantEqAndBranch* elements_kind_branch = + new(zone()) HCompareConstantEqAndBranch( + elements_kind_instr, elements_kind, Token::EQ_STRICT); elements_kind_branch->SetSuccessorAt(0, if_true); elements_kind_branch->SetSuccessorAt(1, if_false); current_block()->Finish(elements_kind_branch); set_current_block(if_true); HInstruction* access; - if (elements_kind == FAST_SMI_ONLY_ELEMENTS || - elements_kind == FAST_ELEMENTS || - elements_kind == FAST_DOUBLE_ELEMENTS) { - if (is_store && elements_kind != FAST_DOUBLE_ELEMENTS) { + if (IsFastElementsKind(elements_kind)) { + if (is_store && !IsFastDoubleElementsKind(elements_kind)) { AddInstruction(new(zone()) HCheckMaps( elements, isolate()->factory()->fixed_array_map(), - elements_kind_branch)); + zone(), elements_kind_branch)); } // TODO(jkummerow): The need for these two blocks could be avoided // in one of two ways: @@ -5467,10 +6413,13 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object, set_current_block(if_jsarray); HInstruction* length; - length = AddInstruction(new(zone()) HJSArrayLength(object, typecheck)); - checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length)); + length = AddInstruction(new(zone()) HJSArrayLength(object, typecheck, + HType::Smi())); + checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length, + ALLOW_SMI_KEY)); access = AddInstruction(BuildFastElementAccess( - elements, checked_key, val, elements_kind, is_store)); + elements, checked_key, val, elements_kind_branch, + elements_kind, is_store)); if (!is_store) { Push(access); } @@ -5483,9 +6432,11 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object, set_current_block(if_fastobject); length = AddInstruction(new(zone()) HFixedArrayBaseLength(elements)); - checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length)); + checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length, + ALLOW_SMI_KEY)); access = AddInstruction(BuildFastElementAccess( - elements, checked_key, val, elements_kind, is_store)); + elements, checked_key, val, elements_kind_branch, + elements_kind, is_store)); } else if (elements_kind == DICTIONARY_ELEMENTS) { if (is_store) { access = AddInstruction(BuildStoreKeyedGeneric(object, key, val)); @@ -5494,10 +6445,11 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object, } } else { // External array elements. access = AddInstruction(BuildExternalArrayElementAccess( - external_elements, checked_key, val, elements_kind, is_store)); + external_elements, checked_key, val, elements_kind_branch, + elements_kind, is_store)); } *has_side_effects |= access->HasObservableSideEffects(); - access->set_position(position); + if (position != RelocInfo::kNoPosition) access->set_position(position); if (!is_store) { Push(access); } @@ -5518,7 +6470,7 @@ HValue* HGraphBuilder::HandleKeyedElementAccess(HValue* obj, HValue* key, HValue* val, Expression* expr, - int ast_id, + BailoutId ast_id, int position, bool is_store, bool* has_side_effects) { @@ -5531,7 +6483,7 @@ HValue* HGraphBuilder::HandleKeyedElementAccess(HValue* obj, : BuildLoadKeyedGeneric(obj, key); } else { AddInstruction(new(zone()) HCheckNonSmi(obj)); - instr = BuildMonomorphicElementAccess(obj, key, val, map, is_store); + instr = BuildMonomorphicElementAccess(obj, key, val, NULL, map, is_store); } } else if (expr->GetReceiverTypes() != NULL && !expr->GetReceiverTypes()->is_empty()) { @@ -5544,7 +6496,7 @@ HValue* HGraphBuilder::HandleKeyedElementAccess(HValue* obj, instr = BuildLoadKeyedGeneric(obj, key); } } - instr->set_position(position); + if (position != RelocInfo::kNoPosition) instr->set_position(position); AddInstruction(instr); *has_side_effects = instr->HasObservableSideEffects(); return instr; @@ -5572,6 +6524,7 @@ void HGraphBuilder::EnsureArgumentsArePushedForAccess() { // Push arguments when entering inlined function. HEnterInlined* entry = function_state()->entry(); + entry->set_arguments_pushed(); ZoneList<HValue*>* arguments_values = entry->arguments_values(); @@ -5654,7 +6607,7 @@ void HGraphBuilder::VisitProperty(Property* expr) { ASSERT(!HasStackOverflow()); ASSERT(current_block() != NULL); ASSERT(current_block()->HasPredecessor()); - expr->RecordTypeFeedback(oracle()); + expr->RecordTypeFeedback(oracle(), zone()); if (TryArgumentsAccess(expr)) return; @@ -5665,13 +6618,12 @@ void HGraphBuilder::VisitProperty(Property* expr) { HValue* array = Pop(); AddInstruction(new(zone()) HCheckNonSmi(array)); HInstruction* mapcheck = - AddInstruction(HCheckInstanceType::NewIsJSArray(array)); + AddInstruction(HCheckInstanceType::NewIsJSArray(array, zone())); instr = new(zone()) HJSArrayLength(array, mapcheck); - } else if (expr->IsStringLength()) { HValue* string = Pop(); AddInstruction(new(zone()) HCheckNonSmi(string)); - AddInstruction(HCheckInstanceType::NewIsString(string)); + AddInstruction(HCheckInstanceType::NewIsString(string, zone())); instr = new(zone()) HStringLength(string); } else if (expr->IsStringAccess()) { CHECK_ALIVE(VisitForValue(expr->key())); @@ -5692,15 +6644,27 @@ void HGraphBuilder::VisitProperty(Property* expr) { Handle<String> name = expr->key()->AsLiteral()->AsPropertyName(); SmallMapList* types = expr->GetReceiverTypes(); - HValue* obj = Pop(); + bool monomorphic = expr->IsMonomorphic(); + Handle<Map> map; if (expr->IsMonomorphic()) { - instr = BuildLoadNamed(obj, expr, types->first(), name); + map = types->first(); + if (map->is_dictionary_map()) monomorphic = false; + } + if (monomorphic) { + Handle<JSFunction> getter; + Handle<JSObject> holder; + if (LookupGetter(map, name, &getter, &holder)) { + AddCheckConstantFunction(holder, Top(), map); + if (FLAG_inline_accessors && TryInlineGetter(getter, expr)) return; + AddInstruction(new(zone()) HPushArgument(Pop())); + instr = new(zone()) HCallConstantFunction(getter, 1); + } else { + instr = BuildLoadNamedMonomorphic(Pop(), name, expr, map); + } } else if (types != NULL && types->length() > 1) { - AddInstruction(new(zone()) HCheckNonSmi(obj)); - HandlePolymorphicLoadNamedField(expr, obj, types, name); - return; + return HandlePolymorphicLoadNamedField(expr, Pop(), types, name); } else { - instr = BuildLoadNamedGeneric(obj, expr); + instr = BuildLoadNamedGeneric(Pop(), name, expr); } } else { @@ -5730,22 +6694,23 @@ void HGraphBuilder::VisitProperty(Property* expr) { } -void HGraphBuilder::AddCheckConstantFunction(Call* expr, +void HGraphBuilder::AddCheckPrototypeMaps(Handle<JSObject> holder, + Handle<Map> receiver_map) { + if (!holder.is_null()) { + AddInstruction(new(zone()) HCheckPrototypeMaps( + Handle<JSObject>(JSObject::cast(receiver_map->prototype())), holder)); + } +} + + +void HGraphBuilder::AddCheckConstantFunction(Handle<JSObject> holder, HValue* receiver, - Handle<Map> receiver_map, - bool smi_and_map_check) { + Handle<Map> receiver_map) { // Constant functions have the nice property that the map will change if they // are overwritten. Therefore it is enough to check the map of the holder and // its prototypes. - if (smi_and_map_check) { - AddInstruction(new(zone()) HCheckNonSmi(receiver)); - AddInstruction(HCheckMaps::NewWithTransitions(receiver, receiver_map)); - } - if (!expr->holder().is_null()) { - AddInstruction(new(zone()) HCheckPrototypeMaps( - Handle<JSObject>(JSObject::cast(receiver_map->prototype())), - expr->holder())); - } + AddCheckMapsWithTransitions(receiver, receiver_map); + AddCheckPrototypeMaps(holder, receiver_map); } @@ -5827,7 +6792,7 @@ void HGraphBuilder::HandlePolymorphicCallNamed(Call* expr, set_current_block(if_true); expr->ComputeTarget(map, name); - AddCheckConstantFunction(expr, receiver, map, false); + AddCheckPrototypeMaps(expr->holder(), map); if (FLAG_trace_inlining && FLAG_polymorphic_inlining) { Handle<JSFunction> caller = info()->closure(); SmartArrayPointer<char> caller_name = @@ -5941,11 +6906,11 @@ int HGraphBuilder::InliningAstSize(Handle<JSFunction> target) { bool HGraphBuilder::TryInline(CallKind call_kind, Handle<JSFunction> target, - ZoneList<Expression*>* arguments, - HValue* receiver, - int ast_id, - int return_id, - ReturnHandlingFlag return_handling) { + int arguments_count, + HValue* implicit_return_value, + BailoutId ast_id, + BailoutId return_id, + InliningKind inlining_kind) { int nodes_added = InliningAstSize(target); if (nodes_added == kNotInlinable) return false; @@ -6002,13 +6967,13 @@ bool HGraphBuilder::TryInline(CallKind call_kind, } // Parse and allocate variables. - CompilationInfo target_info(target); + CompilationInfo target_info(target, zone()); if (!ParserApi::Parse(&target_info, kNoParsingFlags) || !Scope::Analyze(&target_info)) { if (target_info.isolate()->has_pending_exception()) { // Parse or scope error, never optimize this function. SetStackOverflow(); - target_shared->DisableOptimization(); + target_shared->DisableOptimization("parse/scope error"); } TraceInline(target, caller, "parse failure"); return false; @@ -6074,7 +7039,7 @@ bool HGraphBuilder::TryInline(CallKind call_kind, // The scope info might not have been set if a lazily compiled // function is inlined before being called for the first time. Handle<ScopeInfo> target_scope_info = - ScopeInfo::Create(target_info.scope()); + ScopeInfo::Create(target_info.scope(), zone()); target_shared->set_scope_info(*target_scope_info); } target_shared->EnableDeoptimizationSupport(*target_info.code()); @@ -6090,31 +7055,34 @@ bool HGraphBuilder::TryInline(CallKind call_kind, // Save the pending call context and type feedback oracle. Set up new ones // for the inlined function. ASSERT(target_shared->has_deoptimization_support()); + Handle<Code> unoptimized_code(target_shared->code()); TypeFeedbackOracle target_oracle( - Handle<Code>(target_shared->code()), - Handle<Context>(target->context()->global_context()), - isolate()); + unoptimized_code, + Handle<Context>(target->context()->native_context()), + isolate(), + zone()); // The function state is new-allocated because we need to delete it // in two different places. FunctionState* target_state = new FunctionState( - this, &target_info, &target_oracle, return_handling); + this, &target_info, &target_oracle, inlining_kind); HConstant* undefined = graph()->GetConstantUndefined(); HEnvironment* inner_env = environment()->CopyForInlining(target, - arguments->length(), + arguments_count, function, undefined, call_kind, - function_state()->is_construct()); + function_state()->inlining_kind()); #ifdef V8_TARGET_ARCH_IA32 // IA32 only, overwrite the caller's context in the deoptimization // environment with the correct one. // // TODO(kmillikin): implement the same inlining on other platforms so we // can remove the unsightly ifdefs in this function. - HConstant* context = new HConstant(Handle<Context>(target->context()), - Representation::Tagged()); + HConstant* context = + new(zone()) HConstant(Handle<Context>(target->context()), + Representation::Tagged()); AddInstruction(context); inner_env->BindContext(context); #endif @@ -6129,18 +7097,18 @@ bool HGraphBuilder::TryInline(CallKind call_kind, if (function->scope()->arguments() != NULL) { HEnvironment* arguments_env = inner_env->arguments_environment(); int arguments_count = arguments_env->parameter_count(); - arguments_values = new(zone()) ZoneList<HValue*>(arguments_count); + arguments_values = new(zone()) ZoneList<HValue*>(arguments_count, zone()); for (int i = 0; i < arguments_count; i++) { - arguments_values->Add(arguments_env->Lookup(i)); + arguments_values->Add(arguments_env->Lookup(i), zone()); } } HEnterInlined* enter_inlined = new(zone()) HEnterInlined(target, - arguments->length(), + arguments_count, function, call_kind, - function_state()->is_construct(), + function_state()->inlining_kind(), function->scope()->arguments(), arguments_values); function_state()->set_entry(enter_inlined); @@ -6160,7 +7128,7 @@ bool HGraphBuilder::TryInline(CallKind call_kind, // Bail out if the inline function did, as we cannot residualize a call // instead. TraceInline(target, caller, "inline graph construction failed"); - target_shared->DisableOptimization(); + target_shared->DisableOptimization("inlining bailed out"); inline_bailout_ = true; delete target_state; return true; @@ -6169,30 +7137,51 @@ bool HGraphBuilder::TryInline(CallKind call_kind, // Update inlined nodes count. inlined_count_ += nodes_added; + ASSERT(unoptimized_code->kind() == Code::FUNCTION); + Handle<Object> maybe_type_info(unoptimized_code->type_feedback_info()); + Handle<TypeFeedbackInfo> type_info( + Handle<TypeFeedbackInfo>::cast(maybe_type_info)); + graph()->update_type_change_checksum(type_info->own_type_change_checksum()); + TraceInline(target, caller, NULL); if (current_block() != NULL) { - // Add default return value (i.e. undefined for normals calls or the newly - // allocated receiver for construct calls) if control can fall off the - // body. In a test context, undefined is false and any JSObject is true. - if (call_context()->IsValue()) { - ASSERT(function_return() != NULL); - HValue* return_value = function_state()->is_construct() - ? receiver - : undefined; - current_block()->AddLeaveInlined(return_value, - function_return(), - function_state()); - } else if (call_context()->IsEffect()) { - ASSERT(function_return() != NULL); - current_block()->Goto(function_return(), function_state()); + FunctionState* state = function_state(); + if (state->inlining_kind() == CONSTRUCT_CALL_RETURN) { + // Falling off the end of an inlined construct call. In a test context the + // return value will always evaluate to true, in a value context the + // return value is the newly allocated receiver. + if (call_context()->IsTest()) { + current_block()->Goto(inlined_test_context()->if_true(), state); + } else if (call_context()->IsEffect()) { + current_block()->Goto(function_return(), state); + } else { + ASSERT(call_context()->IsValue()); + current_block()->AddLeaveInlined(implicit_return_value, state); + } + } else if (state->inlining_kind() == SETTER_CALL_RETURN) { + // Falling off the end of an inlined setter call. The returned value is + // never used, the value of an assignment is always the value of the RHS + // of the assignment. + if (call_context()->IsTest()) { + inlined_test_context()->ReturnValue(implicit_return_value); + } else if (call_context()->IsEffect()) { + current_block()->Goto(function_return(), state); + } else { + ASSERT(call_context()->IsValue()); + current_block()->AddLeaveInlined(implicit_return_value, state); + } } else { - ASSERT(call_context()->IsTest()); - ASSERT(inlined_test_context() != NULL); - HBasicBlock* target = function_state()->is_construct() - ? inlined_test_context()->if_true() - : inlined_test_context()->if_false(); - current_block()->Goto(target, function_state()); + // Falling off the end of a normal inlined function. This basically means + // returning undefined. + if (call_context()->IsTest()) { + current_block()->Goto(inlined_test_context()->if_false(), state); + } else if (call_context()->IsEffect()) { + current_block()->Goto(function_return(), state); + } else { + ASSERT(call_context()->IsValue()); + current_block()->AddLeaveInlined(undefined, state); + } } } @@ -6240,7 +7229,7 @@ bool HGraphBuilder::TryInlineCall(Call* expr, bool drop_extra) { return TryInline(call_kind, expr->target(), - expr->arguments(), + expr->arguments()->length(), NULL, expr->id(), expr->ReturnId(), @@ -6248,17 +7237,43 @@ bool HGraphBuilder::TryInlineCall(Call* expr, bool drop_extra) { } -bool HGraphBuilder::TryInlineConstruct(CallNew* expr, HValue* receiver) { +bool HGraphBuilder::TryInlineConstruct(CallNew* expr, + HValue* implicit_return_value) { return TryInline(CALL_AS_FUNCTION, expr->target(), - expr->arguments(), - receiver, + expr->arguments()->length(), + implicit_return_value, expr->id(), expr->ReturnId(), CONSTRUCT_CALL_RETURN); } +bool HGraphBuilder::TryInlineGetter(Handle<JSFunction> getter, + Property* prop) { + return TryInline(CALL_AS_METHOD, + getter, + 0, + NULL, + prop->id(), + prop->LoadId(), + GETTER_CALL_RETURN); +} + + +bool HGraphBuilder::TryInlineSetter(Handle<JSFunction> setter, + Assignment* assignment, + HValue* implicit_return_value) { + return TryInline(CALL_AS_METHOD, + setter, + 1, + implicit_return_value, + assignment->id(), + assignment->AssignmentId(), + SETTER_CALL_RETURN); +} + + bool HGraphBuilder::TryInlineBuiltinFunctionCall(Call* expr, bool drop_extra) { if (!expr->target()->shared()->HasBuiltinFunctionId()) return false; BuiltinFunctionId id = expr->target()->shared()->builtin_function_id(); @@ -6332,7 +7347,7 @@ bool HGraphBuilder::TryInlineBuiltinMethodCall(Call* expr, case kMathCos: case kMathTan: if (argument_count == 2 && check_type == RECEIVER_MAP_CHECK) { - AddCheckConstantFunction(expr, receiver, receiver_map, true); + AddCheckConstantFunction(expr->holder(), receiver, receiver_map); HValue* argument = Pop(); HValue* context = environment()->LookupContext(); Drop(1); // Receiver. @@ -6345,7 +7360,7 @@ bool HGraphBuilder::TryInlineBuiltinMethodCall(Call* expr, break; case kMathPow: if (argument_count == 3 && check_type == RECEIVER_MAP_CHECK) { - AddCheckConstantFunction(expr, receiver, receiver_map, true); + AddCheckConstantFunction(expr->holder(), receiver, receiver_map); HValue* right = Pop(); HValue* left = Pop(); Pop(); // Pop receiver. @@ -6387,7 +7402,7 @@ bool HGraphBuilder::TryInlineBuiltinMethodCall(Call* expr, break; case kMathRandom: if (argument_count == 1 && check_type == RECEIVER_MAP_CHECK) { - AddCheckConstantFunction(expr, receiver, receiver_map, true); + AddCheckConstantFunction(expr->holder(), receiver, receiver_map); Drop(1); // Receiver. HValue* context = environment()->LookupContext(); HGlobalObject* global_object = new(zone()) HGlobalObject(context); @@ -6400,82 +7415,15 @@ bool HGraphBuilder::TryInlineBuiltinMethodCall(Call* expr, case kMathMax: case kMathMin: if (argument_count == 3 && check_type == RECEIVER_MAP_CHECK) { - AddCheckConstantFunction(expr, receiver, receiver_map, true); + AddCheckConstantFunction(expr->holder(), receiver, receiver_map); HValue* right = Pop(); HValue* left = Pop(); - Pop(); // Pop receiver. - - HValue* left_operand = left; - HValue* right_operand = right; - - // If we do not have two integers, we convert to double for comparison. - if (!left->representation().IsInteger32() || - !right->representation().IsInteger32()) { - if (!left->representation().IsDouble()) { - HChange* left_convert = new(zone()) HChange( - left, - Representation::Double(), - false, // Do not truncate when converting to double. - true); // Deoptimize for undefined. - left_convert->SetFlag(HValue::kBailoutOnMinusZero); - left_operand = AddInstruction(left_convert); - } - if (!right->representation().IsDouble()) { - HChange* right_convert = new(zone()) HChange( - right, - Representation::Double(), - false, // Do not truncate when converting to double. - true); // Deoptimize for undefined. - right_convert->SetFlag(HValue::kBailoutOnMinusZero); - right_operand = AddInstruction(right_convert); - } - } - - ASSERT(left_operand->representation().Equals( - right_operand->representation())); - ASSERT(!left_operand->representation().IsTagged()); - - Token::Value op = (id == kMathMin) ? Token::LT : Token::GT; - - HCompareIDAndBranch* compare = - new(zone()) HCompareIDAndBranch(left_operand, right_operand, op); - compare->SetInputRepresentation(left_operand->representation()); - - HBasicBlock* return_left = graph()->CreateBasicBlock(); - HBasicBlock* return_right = graph()->CreateBasicBlock(); - - compare->SetSuccessorAt(0, return_left); - compare->SetSuccessorAt(1, return_right); - current_block()->Finish(compare); - - set_current_block(return_left); - Push(left); - set_current_block(return_right); - // The branch above always returns the right operand if either of - // them is NaN, but the spec requires that max/min(NaN, X) = NaN. - // We add another branch that checks if the left operand is NaN or not. - if (left_operand->representation().IsDouble()) { - // If left_operand != left_operand then it is NaN. - HCompareIDAndBranch* compare_nan = new(zone()) HCompareIDAndBranch( - left_operand, left_operand, Token::EQ); - compare_nan->SetInputRepresentation(left_operand->representation()); - HBasicBlock* left_is_number = graph()->CreateBasicBlock(); - HBasicBlock* left_is_nan = graph()->CreateBasicBlock(); - compare_nan->SetSuccessorAt(0, left_is_number); - compare_nan->SetSuccessorAt(1, left_is_nan); - current_block()->Finish(compare_nan); - set_current_block(left_is_nan); - Push(left); - set_current_block(left_is_number); - Push(right); - return_right = CreateJoin(left_is_number, left_is_nan, expr->id()); - } else { - Push(right); - } - - HBasicBlock* join = CreateJoin(return_left, return_right, expr->id()); - set_current_block(join); - ast_context()->ReturnValue(Pop()); + Drop(1); // Receiver. + HValue* context = environment()->LookupContext(); + HMathMinMax::Operation op = (id == kMathMin) ? HMathMinMax::kMathMin + : HMathMinMax::kMathMax; + HMathMinMax* result = new(zone()) HMathMinMax(context, left, right, op); + ast_context()->ReturnInstruction(result, expr->id()); return true; } break; @@ -6516,7 +7464,7 @@ bool HGraphBuilder::TryCallApply(Call* expr) { VisitForValue(prop->obj()); if (HasStackOverflow() || current_block() == NULL) return true; HValue* function = Top(); - AddCheckConstantFunction(expr, function, function_map, true); + AddCheckConstantFunction(expr->holder(), function, function_map); Drop(1); VisitForValue(args->at(0)); @@ -6635,7 +7583,7 @@ void HGraphBuilder::VisitCall(Call* expr) { call = PreProcessCall( new(zone()) HCallNamed(context, name, argument_count)); } else { - AddCheckConstantFunction(expr, receiver, receiver_map, true); + AddCheckConstantFunction(expr->holder(), receiver, receiver_map); if (TryInlineCall(expr)) return; call = PreProcessCall( @@ -6706,6 +7654,11 @@ void HGraphBuilder::VisitCall(Call* expr) { return; } if (TryInlineCall(expr)) return; + + if (expr->target().is_identical_to(info()->closure())) { + graph()->MarkRecursive(); + } + call = PreProcessCall(new(zone()) HCallKnownGlobal(expr->target(), argument_count)); } else { @@ -6728,8 +7681,8 @@ void HGraphBuilder::VisitCall(Call* expr) { HValue* function = Top(); HValue* context = environment()->LookupContext(); HGlobalObject* global = new(zone()) HGlobalObject(context); - HGlobalReceiver* receiver = new(zone()) HGlobalReceiver(global); AddInstruction(global); + HGlobalReceiver* receiver = new(zone()) HGlobalReceiver(global); PushAndAdd(receiver); CHECK_ALIVE(VisitExpressions(expr->arguments())); AddInstruction(new(zone()) HCheckFunction(function, expr->target())); @@ -6759,8 +7712,8 @@ void HGraphBuilder::VisitCall(Call* expr) { HValue* function = Top(); HValue* context = environment()->LookupContext(); HGlobalObject* global_object = new(zone()) HGlobalObject(context); - HGlobalReceiver* receiver = new(zone()) HGlobalReceiver(global_object); AddInstruction(global_object); + HGlobalReceiver* receiver = new(zone()) HGlobalReceiver(global_object); AddInstruction(receiver); PushAndAdd(new(zone()) HPushArgument(receiver)); CHECK_ALIVE(VisitArgumentList(expr->arguments())); @@ -6833,8 +7786,8 @@ void HGraphBuilder::VisitCallNew(CallNew* expr) { } else { // The constructor function is both an operand to the instruction and an // argument to the construct call. - HValue* constructor = NULL; - CHECK_ALIVE(constructor = VisitArgument(expr->expression())); + CHECK_ALIVE(VisitArgument(expr->expression())); + HValue* constructor = HPushArgument::cast(Top())->argument(); CHECK_ALIVE(VisitArgumentList(expr->arguments())); HInstruction* call = new(zone()) HCallNew(context, constructor, argument_count); @@ -6892,7 +7845,6 @@ void HGraphBuilder::VisitCallRuntime(CallRuntime* expr) { int argument_count = expr->arguments()->length(); HCallRuntime* call = new(zone()) HCallRuntime(context, name, function, argument_count); - call->set_position(RelocInfo::kNoPosition); Drop(argument_count); return ast_context()->ReturnInstruction(call, expr->id()); } @@ -7147,8 +8099,7 @@ void HGraphBuilder::VisitCountOperation(CountOperation* expr) { } HValue* context = BuildContextChainWalk(var); - HStoreContextSlot::Mode mode = - (var->mode() == LET || var->mode() == CONST_HARMONY) + HStoreContextSlot::Mode mode = IsLexicalVariableMode(var->mode()) ? HStoreContextSlot::kCheckDeoptimize : HStoreContextSlot::kNoCheck; HStoreContextSlot* instr = new(zone()) HStoreContextSlot(context, var->index(), mode, after); @@ -7166,30 +8117,56 @@ void HGraphBuilder::VisitCountOperation(CountOperation* expr) { } else { // Argument of the count operation is a property. ASSERT(prop != NULL); - prop->RecordTypeFeedback(oracle()); + prop->RecordTypeFeedback(oracle(), zone()); if (prop->key()->IsPropertyName()) { // Named property. if (returns_original_input) Push(graph_->GetConstantUndefined()); CHECK_ALIVE(VisitForValue(prop->obj())); - HValue* obj = Top(); - - HInstruction* load = NULL; - if (prop->IsMonomorphic()) { - Handle<String> name = prop->key()->AsLiteral()->AsPropertyName(); - Handle<Map> map = prop->GetReceiverTypes()->first(); - load = BuildLoadNamed(obj, prop, map, name); + HValue* object = Top(); + + Handle<String> name = prop->key()->AsLiteral()->AsPropertyName(); + Handle<Map> map; + HInstruction* load; + bool monomorphic = prop->IsMonomorphic(); + if (monomorphic) { + map = prop->GetReceiverTypes()->first(); + if (map->is_dictionary_map()) monomorphic = false; + } + if (monomorphic) { + Handle<JSFunction> getter; + Handle<JSObject> holder; + if (LookupGetter(map, name, &getter, &holder)) { + load = BuildCallGetter(object, map, getter, holder); + } else { + load = BuildLoadNamedMonomorphic(object, name, prop, map); + } } else { - load = BuildLoadNamedGeneric(obj, prop); + load = BuildLoadNamedGeneric(object, name, prop); } PushAndAdd(load); - if (load->HasObservableSideEffects()) AddSimulate(expr->CountId()); + if (load->HasObservableSideEffects()) AddSimulate(prop->LoadId()); after = BuildIncrement(returns_original_input, expr); input = Pop(); - HInstruction* store = BuildStoreNamed(obj, after, prop); + HInstruction* store; + if (!monomorphic) { + // If we don't know the monomorphic type, do a generic store. + CHECK_ALIVE(store = BuildStoreNamedGeneric(object, name, after)); + } else { + Handle<JSFunction> setter; + Handle<JSObject> holder; + if (LookupSetter(map, name, &setter, &holder)) { + store = BuildCallSetter(object, after, map, setter, holder); + } else { + CHECK_ALIVE(store = BuildStoreNamedMonomorphic(object, + name, + after, + map)); + } + } AddInstruction(store); // Overwrite the receiver in the bailout environment with the result @@ -7210,16 +8187,16 @@ void HGraphBuilder::VisitCountOperation(CountOperation* expr) { bool has_side_effects = false; HValue* load = HandleKeyedElementAccess( - obj, key, NULL, prop, expr->CountId(), RelocInfo::kNoPosition, + obj, key, NULL, prop, prop->LoadId(), RelocInfo::kNoPosition, false, // is_store &has_side_effects); Push(load); - if (has_side_effects) AddSimulate(expr->CountId()); + if (has_side_effects) AddSimulate(prop->LoadId()); after = BuildIncrement(returns_original_input, expr); input = Pop(); - expr->RecordTypeFeedback(oracle()); + expr->RecordTypeFeedback(oracle(), zone()); HandleKeyedElementAccess(obj, key, after, expr, expr->AssignmentId(), RelocInfo::kNoPosition, true, // is_store @@ -7245,7 +8222,7 @@ HStringCharCodeAt* HGraphBuilder::BuildStringCharCodeAt(HValue* context, HValue* string, HValue* index) { AddInstruction(new(zone()) HCheckNonSmi(string)); - AddInstruction(HCheckInstanceType::NewIsString(string)); + AddInstruction(HCheckInstanceType::NewIsString(string, zone())); HStringLength* length = new(zone()) HStringLength(string); AddInstruction(length); HInstruction* checked_index = @@ -7253,6 +8230,61 @@ HStringCharCodeAt* HGraphBuilder::BuildStringCharCodeAt(HValue* context, return new(zone()) HStringCharCodeAt(context, string, checked_index); } +// Checks if the given shift amounts have form: (sa) and (32 - sa). +static bool ShiftAmountsAllowReplaceByRotate(HValue* sa, + HValue* const32_minus_sa) { + if (!const32_minus_sa->IsSub()) return false; + HSub* sub = HSub::cast(const32_minus_sa); + HValue* const32 = sub->left(); + if (!const32->IsConstant() || + HConstant::cast(const32)->Integer32Value() != 32) { + return false; + } + return (sub->right() == sa); +} + + +// Checks if the left and the right are shift instructions with the oposite +// directions that can be replaced by one rotate right instruction or not. +// Returns the operand and the shift amount for the rotate instruction in the +// former case. +bool HGraphBuilder::MatchRotateRight(HValue* left, + HValue* right, + HValue** operand, + HValue** shift_amount) { + HShl* shl; + HShr* shr; + if (left->IsShl() && right->IsShr()) { + shl = HShl::cast(left); + shr = HShr::cast(right); + } else if (left->IsShr() && right->IsShl()) { + shl = HShl::cast(right); + shr = HShr::cast(left); + } else { + return false; + } + + if (!ShiftAmountsAllowReplaceByRotate(shl->right(), shr->right()) && + !ShiftAmountsAllowReplaceByRotate(shr->right(), shl->right())) { + return false; + } + *operand= shr->left(); + *shift_amount = shr->right(); + return true; +} + + +bool CanBeZero(HValue *right) { + if (right->IsConstant()) { + HConstant* right_const = HConstant::cast(right); + if (right_const->HasInteger32Value() && + (right_const->Integer32Value() & 0x1f) != 0) { + return false; + } + } + return true; +} + HInstruction* HGraphBuilder::BuildBinaryOperation(BinaryOperation* expr, HValue* left, @@ -7269,9 +8301,9 @@ HInstruction* HGraphBuilder::BuildBinaryOperation(BinaryOperation* expr, case Token::ADD: if (info.IsString()) { AddInstruction(new(zone()) HCheckNonSmi(left)); - AddInstruction(HCheckInstanceType::NewIsString(left)); + AddInstruction(HCheckInstanceType::NewIsString(left, zone())); AddInstruction(new(zone()) HCheckNonSmi(right)); - AddInstruction(HCheckInstanceType::NewIsString(right)); + AddInstruction(HCheckInstanceType::NewIsString(right, zone())); instr = new(zone()) HStringAdd(context, left, right); } else { instr = HAdd::NewHAdd(zone(), context, left, right); @@ -7291,14 +8323,27 @@ HInstruction* HGraphBuilder::BuildBinaryOperation(BinaryOperation* expr, break; case Token::BIT_XOR: case Token::BIT_AND: - case Token::BIT_OR: instr = HBitwise::NewHBitwise(zone(), expr->op(), context, left, right); break; + case Token::BIT_OR: { + HValue* operand, *shift_amount; + if (info.IsInteger32() && + MatchRotateRight(left, right, &operand, &shift_amount)) { + instr = new(zone()) HRor(context, operand, shift_amount); + } else { + instr = HBitwise::NewHBitwise(zone(), expr->op(), context, left, right); + } + break; + } case Token::SAR: instr = HSar::NewHSar(zone(), context, left, right); break; case Token::SHR: instr = HShr::NewHShr(zone(), context, left, right); + if (FLAG_opt_safe_uint32_operations && instr->IsShr() && + CanBeZero(right)) { + graph()->RecordUint32Instruction(instr); + } break; case Token::SHL: instr = HShl::NewHShl(zone(), context, left, right); @@ -7311,14 +8356,16 @@ HInstruction* HGraphBuilder::BuildBinaryOperation(BinaryOperation* expr, // for a smi operation. If one of the operands is a constant string // do not generate code assuming it is a smi operation. if (info.IsSmi() && - ((left->IsConstant() && HConstant::cast(left)->HasStringValue()) || - (right->IsConstant() && HConstant::cast(right)->HasStringValue()))) { + ((left->IsConstant() && HConstant::cast(left)->handle()->IsString()) || + (right->IsConstant() && HConstant::cast(right)->handle()->IsString()))) { return instr; } Representation rep = ToRepresentation(info); // We only generate either int32 or generic tagged bitwise operations. - if (instr->IsBitwiseBinaryOperation() && rep.IsDouble()) { - rep = Representation::Integer32(); + if (instr->IsBitwiseBinaryOperation()) { + HBitwiseBinaryOperation::cast(instr)-> + InitializeObservedInputRepresentation(rep); + if (rep.IsDouble()) rep = Representation::Integer32(); } TraceRepresentation(expr->op(), info, instr, rep); instr->AssumeRepresentation(rep); @@ -7395,7 +8442,7 @@ void HGraphBuilder::VisitLogicalExpression(BinaryOperation* expr) { // We need an extra block to maintain edge-split form. HBasicBlock* empty_block = graph()->CreateBasicBlock(); HBasicBlock* eval_right = graph()->CreateBasicBlock(); - unsigned test_id = expr->left()->test_id(); + TypeFeedbackId test_id = expr->left()->test_id(); ToBooleanStub::Types expected(oracle()->ToBooleanTypes(test_id)); HBranch* test = is_logical_and ? new(zone()) HBranch(Top(), eval_right, empty_block, expected) @@ -7528,7 +8575,7 @@ static bool MatchLiteralCompareTypeof(HValue* left, if (left->IsTypeof() && Token::IsEqualityOp(op) && right->IsConstant() && - HConstant::cast(right)->HasStringValue()) { + HConstant::cast(right)->handle()->IsString()) { *typeof_expr = HTypeof::cast(left); *check = Handle<String>::cast(HConstant::cast(right)->handle()); return true; @@ -7634,9 +8681,7 @@ void HGraphBuilder::VisitCompareOperation(CompareOperation* expr) { Handle<GlobalObject> global(info()->global_object()); LookupResult lookup(isolate()); global->Lookup(*name, &lookup); - if (lookup.IsFound() && - lookup.type() == NORMAL && - lookup.GetValue()->IsJSFunction()) { + if (lookup.IsNormal() && lookup.GetValue()->IsJSFunction()) { Handle<JSFunction> candidate(JSFunction::cast(lookup.GetValue())); // If the function is in new space we assume it's more likely to // change and thus prefer the general IC code. @@ -7670,19 +8715,17 @@ void HGraphBuilder::VisitCompareOperation(CompareOperation* expr) { // Can we get away with map check and not instance type check? Handle<Map> map = oracle()->GetCompareMap(expr); if (!map.is_null()) { - AddInstruction(new(zone()) HCheckNonSmi(left)); - AddInstruction(HCheckMaps::NewWithTransitions(left, map)); - AddInstruction(new(zone()) HCheckNonSmi(right)); - AddInstruction(HCheckMaps::NewWithTransitions(right, map)); + AddCheckMapsWithTransitions(left, map); + AddCheckMapsWithTransitions(right, map); HCompareObjectEqAndBranch* result = new(zone()) HCompareObjectEqAndBranch(left, right); result->set_position(expr->position()); return ast_context()->ReturnControl(result, expr->id()); } else { AddInstruction(new(zone()) HCheckNonSmi(left)); - AddInstruction(HCheckInstanceType::NewIsSpecObject(left)); + AddInstruction(HCheckInstanceType::NewIsSpecObject(left, zone())); AddInstruction(new(zone()) HCheckNonSmi(right)); - AddInstruction(HCheckInstanceType::NewIsSpecObject(right)); + AddInstruction(HCheckInstanceType::NewIsSpecObject(right, zone())); HCompareObjectEqAndBranch* result = new(zone()) HCompareObjectEqAndBranch(left, right); result->set_position(expr->position()); @@ -7695,9 +8738,9 @@ void HGraphBuilder::VisitCompareOperation(CompareOperation* expr) { } else if (type_info.IsString() && oracle()->IsSymbolCompare(expr) && (op == Token::EQ || op == Token::EQ_STRICT)) { AddInstruction(new(zone()) HCheckNonSmi(left)); - AddInstruction(HCheckInstanceType::NewIsSymbol(left)); + AddInstruction(HCheckInstanceType::NewIsSymbol(left, zone())); AddInstruction(new(zone()) HCheckNonSmi(right)); - AddInstruction(HCheckInstanceType::NewIsSymbol(right)); + AddInstruction(HCheckInstanceType::NewIsSymbol(right, zone())); HCompareObjectEqAndBranch* result = new(zone()) HCompareObjectEqAndBranch(left, right); result->set_position(expr->position()); @@ -7734,13 +8777,25 @@ void HGraphBuilder::HandleLiteralCompareNil(CompareOperation* expr, } +HInstruction* HGraphBuilder::BuildThisFunction() { + // If we share optimized code between different closures, the + // this-function is not a constant, except inside an inlined body. + if (function_state()->outer() != NULL) { + return new(zone()) HConstant( + function_state()->compilation_info()->closure(), + Representation::Tagged()); + } else { + return new(zone()) HThisFunction; + } +} + + void HGraphBuilder::VisitThisFunction(ThisFunction* expr) { ASSERT(!HasStackOverflow()); ASSERT(current_block() != NULL); ASSERT(current_block()->HasPredecessor()); - HThisFunction* self = new(zone()) HThisFunction( - function_state()->compilation_info()->closure()); - return ast_context()->ReturnInstruction(self, expr->id()); + HInstruction* instr = BuildThisFunction(); + return ast_context()->ReturnInstruction(instr, expr->id()); } @@ -7769,11 +8824,11 @@ void HGraphBuilder::VisitVariableDeclaration(VariableDeclaration* declaration) { bool hole_init = mode == CONST || mode == CONST_HARMONY || mode == LET; switch (variable->location()) { case Variable::UNALLOCATED: - globals_.Add(variable->name()); + globals_.Add(variable->name(), zone()); globals_.Add(variable->binding_needs_init() ? isolate()->factory()->the_hole_value() - : isolate()->factory()->undefined_value()); - globals_.Add(isolate()->factory()->ToBoolean(variable->is_qml_global())); + : isolate()->factory()->undefined_value(), zone()); + globals_.Add(isolate()->factory()->ToBoolean(variable->is_qml_global()), zone()); return; case Variable::PARAMETER: case Variable::LOCAL: @@ -7786,7 +8841,7 @@ void HGraphBuilder::VisitVariableDeclaration(VariableDeclaration* declaration) { if (hole_init) { HValue* value = graph()->GetConstantHole(); HValue* context = environment()->LookupContext(); - HStoreContextSlot* store = new HStoreContextSlot( + HStoreContextSlot* store = new(zone()) HStoreContextSlot( context, variable->index(), HStoreContextSlot::kNoCheck, value); AddInstruction(store); if (store->HasObservableSideEffects()) AddSimulate(proxy->id()); @@ -7803,13 +8858,13 @@ void HGraphBuilder::VisitFunctionDeclaration(FunctionDeclaration* declaration) { Variable* variable = proxy->var(); switch (variable->location()) { case Variable::UNALLOCATED: { - globals_.Add(variable->name()); + globals_.Add(variable->name(), zone()); Handle<SharedFunctionInfo> function = Compiler::BuildFunctionInfo(declaration->fun(), info()->script()); // Check for stack-overflow exception. if (function.is_null()) return SetStackOverflow(); - globals_.Add(function); - globals_.Add(isolate()->factory()->ToBoolean(variable->is_qml_global())); + globals_.Add(function, zone()); + globals_.Add(isolate()->factory()->ToBoolean(variable->is_qml_global()), zone()); return; } case Variable::PARAMETER: @@ -7823,7 +8878,7 @@ void HGraphBuilder::VisitFunctionDeclaration(FunctionDeclaration* declaration) { CHECK_ALIVE(VisitForValue(declaration->fun())); HValue* value = Pop(); HValue* context = environment()->LookupContext(); - HStoreContextSlot* store = new HStoreContextSlot( + HStoreContextSlot* store = new(zone()) HStoreContextSlot( context, variable->index(), HStoreContextSlot::kNoCheck, value); AddInstruction(store); if (store->HasObservableSideEffects()) AddSimulate(proxy->id()); @@ -7969,7 +9024,7 @@ void HGraphBuilder::GenerateIsConstructCall(CallRuntime* call) { ASSERT(call->arguments()->length() == 0); if (function_state()->outer() != NULL) { // We are generating graph for inlined function. - HValue* value = function_state()->is_construct() + HValue* value = function_state()->inlining_kind() == CONSTRUCT_CALL_RETURN ? graph()->GetConstantTrue() : graph()->GetConstantFalse(); return ast_context()->ReturnValue(value); @@ -8005,8 +9060,10 @@ void HGraphBuilder::GenerateArguments(CallRuntime* call) { HInstruction* elements = AddInstruction( new(zone()) HArgumentsElements(false)); HInstruction* length = AddInstruction(new(zone()) HArgumentsLength(elements)); + HInstruction* checked_index = + AddInstruction(new(zone()) HBoundsCheck(index, length)); HAccessArgumentsAt* result = - new(zone()) HAccessArgumentsAt(elements, length, index); + new(zone()) HAccessArgumentsAt(elements, length, checked_index); return ast_context()->ReturnInstruction(result, call->id()); } @@ -8069,11 +9126,11 @@ void HGraphBuilder::GenerateSetValueOf(CallRuntime* call) { // Create in-object property store to kValueOffset. set_current_block(if_js_value); Handle<String> name = isolate()->factory()->undefined_symbol(); - AddInstruction(new HStoreNamedField(object, - name, - value, - true, // in-object store. - JSValue::kValueOffset)); + AddInstruction(new(zone()) HStoreNamedField(object, + name, + value, + true, // in-object store. + JSValue::kValueOffset)); if_js_value->Goto(join); join->SetJoinId(call->id()); set_current_block(join); @@ -8361,33 +9418,38 @@ void HGraphBuilder::GenerateFastAsciiArrayJoin(CallRuntime* call) { HEnvironment::HEnvironment(HEnvironment* outer, Scope* scope, - Handle<JSFunction> closure) + Handle<JSFunction> closure, + Zone* zone) : closure_(closure), - values_(0), - assigned_variables_(4), + values_(0, zone), + assigned_variables_(4, zone), frame_type_(JS_FUNCTION), parameter_count_(0), specials_count_(1), local_count_(0), outer_(outer), + entry_(NULL), pop_count_(0), push_count_(0), - ast_id_(AstNode::kNoNumber) { + ast_id_(BailoutId::None()), + zone_(zone) { Initialize(scope->num_parameters() + 1, scope->num_stack_slots(), 0); } -HEnvironment::HEnvironment(const HEnvironment* other) - : values_(0), - assigned_variables_(0), +HEnvironment::HEnvironment(const HEnvironment* other, Zone* zone) + : values_(0, zone), + assigned_variables_(0, zone), frame_type_(JS_FUNCTION), parameter_count_(0), specials_count_(1), local_count_(0), outer_(NULL), + entry_(NULL), pop_count_(0), push_count_(0), - ast_id_(other->ast_id()) { + ast_id_(other->ast_id()), + zone_(zone) { Initialize(other); } @@ -8395,17 +9457,20 @@ HEnvironment::HEnvironment(const HEnvironment* other) HEnvironment::HEnvironment(HEnvironment* outer, Handle<JSFunction> closure, FrameType frame_type, - int arguments) + int arguments, + Zone* zone) : closure_(closure), - values_(arguments), - assigned_variables_(0), + values_(arguments, zone), + assigned_variables_(0, zone), frame_type_(frame_type), parameter_count_(arguments), local_count_(0), outer_(outer), + entry_(NULL), pop_count_(0), push_count_(0), - ast_id_(AstNode::kNoNumber) { + ast_id_(BailoutId::None()), + zone_(zone) { } @@ -8417,19 +9482,20 @@ void HEnvironment::Initialize(int parameter_count, // Avoid reallocating the temporaries' backing store on the first Push. int total = parameter_count + specials_count_ + local_count + stack_height; - values_.Initialize(total + 4); - for (int i = 0; i < total; ++i) values_.Add(NULL); + values_.Initialize(total + 4, zone()); + for (int i = 0; i < total; ++i) values_.Add(NULL, zone()); } void HEnvironment::Initialize(const HEnvironment* other) { closure_ = other->closure(); - values_.AddAll(other->values_); - assigned_variables_.AddAll(other->assigned_variables_); + values_.AddAll(other->values_, zone()); + assigned_variables_.AddAll(other->assigned_variables_, zone()); frame_type_ = other->frame_type_; parameter_count_ = other->parameter_count_; local_count_ = other->local_count_; if (other->outer_ != NULL) outer_ = other->outer_->Copy(); // Deep copy. + entry_ = other->entry_; pop_count_ = other->pop_count_; push_count_ = other->push_count_; ast_id_ = other->ast_id_; @@ -8453,7 +9519,7 @@ void HEnvironment::AddIncomingEdge(HBasicBlock* block, HEnvironment* other) { } else if (values_[i] != other->values_[i]) { // There is a fresh value on the incoming edge, a phi is needed. ASSERT(values_[i] != NULL && other->values_[i] != NULL); - HPhi* phi = new(block->zone()) HPhi(i); + HPhi* phi = new(zone()) HPhi(i, zone()); HValue* old_value = values_[i]; for (int j = 0; j < block->predecessors()->length(); j++) { phi->AddInput(old_value); @@ -8469,7 +9535,7 @@ void HEnvironment::AddIncomingEdge(HBasicBlock* block, HEnvironment* other) { void HEnvironment::Bind(int index, HValue* value) { ASSERT(value != NULL); if (!assigned_variables_.Contains(index)) { - assigned_variables_.Add(index); + assigned_variables_.Add(index, zone()); } values_[index] = value; } @@ -8509,7 +9575,7 @@ void HEnvironment::Drop(int count) { HEnvironment* HEnvironment::Copy() const { - return new(closure()->GetIsolate()->zone()) HEnvironment(this); + return new(zone()) HEnvironment(this, zone()); } @@ -8523,7 +9589,7 @@ HEnvironment* HEnvironment::CopyWithoutHistory() const { HEnvironment* HEnvironment::CopyAsLoopHeader(HBasicBlock* loop_header) const { HEnvironment* new_env = Copy(); for (int i = 0; i < values_.length(); ++i) { - HPhi* phi = new(loop_header->zone()) HPhi(i); + HPhi* phi = new(zone()) HPhi(i, zone()); phi->AddInput(values_[i]); new_env->values_[i] = phi; loop_header->AddPhi(phi); @@ -8537,8 +9603,9 @@ HEnvironment* HEnvironment::CreateStubEnvironment(HEnvironment* outer, Handle<JSFunction> target, FrameType frame_type, int arguments) const { - HEnvironment* new_env = new(closure()->GetIsolate()->zone()) - HEnvironment(outer, target, frame_type, arguments + 1); + HEnvironment* new_env = + new(zone()) HEnvironment(outer, target, frame_type, + arguments + 1, zone()); for (int i = 0; i <= arguments; ++i) { // Include receiver. new_env->Push(ExpressionStackAt(arguments - i)); } @@ -8553,11 +9620,9 @@ HEnvironment* HEnvironment::CopyForInlining( FunctionLiteral* function, HConstant* undefined, CallKind call_kind, - bool is_construct) const { + InliningKind inlining_kind) const { ASSERT(frame_type() == JS_FUNCTION); - Zone* zone = closure()->GetIsolate()->zone(); - // Outer environment is a copy of this one without the arguments. int arity = function->scope()->num_parameters(); @@ -8565,11 +9630,19 @@ HEnvironment* HEnvironment::CopyForInlining( outer->Drop(arguments + 1); // Including receiver. outer->ClearHistory(); - if (is_construct) { + if (inlining_kind == CONSTRUCT_CALL_RETURN) { // Create artificial constructor stub environment. The receiver should // actually be the constructor function, but we pass the newly allocated // object instead, DoComputeConstructStubFrame() relies on that. outer = CreateStubEnvironment(outer, target, JS_CONSTRUCT, arguments); + } else if (inlining_kind == GETTER_CALL_RETURN) { + // We need an additional StackFrame::INTERNAL frame for restoring the + // correct context. + outer = CreateStubEnvironment(outer, target, JS_GETTER, arguments); + } else if (inlining_kind == SETTER_CALL_RETURN) { + // We need an additional StackFrame::INTERNAL frame for temporarily saving + // the argument of the setter, see StoreStubCompiler::CompileStoreViaSetter. + outer = CreateStubEnvironment(outer, target, JS_SETTER, arguments); } if (arity != arguments) { @@ -8578,7 +9651,7 @@ HEnvironment* HEnvironment::CopyForInlining( } HEnvironment* inner = - new(zone) HEnvironment(outer, function->scope(), target); + new(zone()) HEnvironment(outer, function->scope(), target, zone()); // Get the argument values from the original environment. for (int i = 0; i <= arity; ++i) { // Include receiver. HValue* push = (i <= arguments) ? @@ -8589,7 +9662,7 @@ HEnvironment* HEnvironment::CopyForInlining( // builtin function, pass undefined as the receiver for function // calls (instead of the global receiver). if ((target->shared()->native() || !function->is_classic_mode()) && - call_kind == CALL_AS_FUNCTION && !is_construct) { + call_kind == CALL_AS_FUNCTION && inlining_kind != CONSTRUCT_CALL_RETURN) { inner->SetValueAt(0, undefined); } inner->SetValueAt(arity + 1, LookupContext()); @@ -8597,7 +9670,7 @@ HEnvironment* HEnvironment::CopyForInlining( inner->SetValueAt(i, undefined); } - inner->set_ast_id(AstNode::kFunctionEntryId); + inner->set_ast_id(BailoutId::FunctionEntry()); return inner; } @@ -8768,27 +9841,28 @@ void HTracer::TraceLiveRanges(const char* name, LAllocator* allocator) { const Vector<LiveRange*>* fixed_d = allocator->fixed_double_live_ranges(); for (int i = 0; i < fixed_d->length(); ++i) { - TraceLiveRange(fixed_d->at(i), "fixed"); + TraceLiveRange(fixed_d->at(i), "fixed", allocator->zone()); } const Vector<LiveRange*>* fixed = allocator->fixed_live_ranges(); for (int i = 0; i < fixed->length(); ++i) { - TraceLiveRange(fixed->at(i), "fixed"); + TraceLiveRange(fixed->at(i), "fixed", allocator->zone()); } const ZoneList<LiveRange*>* live_ranges = allocator->live_ranges(); for (int i = 0; i < live_ranges->length(); ++i) { - TraceLiveRange(live_ranges->at(i), "object"); + TraceLiveRange(live_ranges->at(i), "object", allocator->zone()); } } -void HTracer::TraceLiveRange(LiveRange* range, const char* type) { +void HTracer::TraceLiveRange(LiveRange* range, const char* type, + Zone* zone) { if (range != NULL && !range->IsEmpty()) { PrintIndent(); trace_.Add("%d %s", range->id(), type); if (range->HasRegisterAssigned()) { - LOperand* op = range->CreateAssignedOperand(ZONE); + LOperand* op = range->CreateAssignedOperand(zone); int assigned_reg = op->index(); if (op->IsDoubleRegister()) { trace_.Add(" \"%s\"", |