blob: 8f21f3ceb86e29d6bdfaa55545c4065928f92262 [file] [log] [blame]
// Copyright 2013 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/crankshaft/hydrogen.h"
#include <memory>
#include <sstream>
#include "src/allocation-site-scopes.h"
#include "src/ast/ast-numbering.h"
#include "src/ast/compile-time-value.h"
#include "src/ast/scopes.h"
#include "src/code-factory.h"
#include "src/crankshaft/hydrogen-bce.h"
#include "src/crankshaft/hydrogen-canonicalize.h"
#include "src/crankshaft/hydrogen-check-elimination.h"
#include "src/crankshaft/hydrogen-dce.h"
#include "src/crankshaft/hydrogen-dehoist.h"
#include "src/crankshaft/hydrogen-environment-liveness.h"
#include "src/crankshaft/hydrogen-escape-analysis.h"
#include "src/crankshaft/hydrogen-gvn.h"
#include "src/crankshaft/hydrogen-infer-representation.h"
#include "src/crankshaft/hydrogen-infer-types.h"
#include "src/crankshaft/hydrogen-load-elimination.h"
#include "src/crankshaft/hydrogen-mark-unreachable.h"
#include "src/crankshaft/hydrogen-osr.h"
#include "src/crankshaft/hydrogen-range-analysis.h"
#include "src/crankshaft/hydrogen-redundant-phi.h"
#include "src/crankshaft/hydrogen-removable-simulates.h"
#include "src/crankshaft/hydrogen-representation-changes.h"
#include "src/crankshaft/hydrogen-sce.h"
#include "src/crankshaft/hydrogen-store-elimination.h"
#include "src/crankshaft/hydrogen-uint32-analysis.h"
#include "src/crankshaft/lithium-allocator.h"
#include "src/crankshaft/typing.h"
#include "src/field-type.h"
#include "src/full-codegen/full-codegen.h"
#include "src/globals.h"
#include "src/ic/call-optimization.h"
#include "src/ic/ic.h"
// GetRootConstructor
#include "src/ic/ic-inl.h"
#include "src/isolate-inl.h"
#include "src/runtime/runtime.h"
#if V8_TARGET_ARCH_IA32
#include "src/crankshaft/ia32/lithium-codegen-ia32.h" // NOLINT
#elif V8_TARGET_ARCH_X64
#include "src/crankshaft/x64/lithium-codegen-x64.h" // NOLINT
#elif V8_TARGET_ARCH_ARM64
#include "src/crankshaft/arm64/lithium-codegen-arm64.h" // NOLINT
#elif V8_TARGET_ARCH_ARM
#include "src/crankshaft/arm/lithium-codegen-arm.h" // NOLINT
#elif V8_TARGET_ARCH_PPC
#include "src/crankshaft/ppc/lithium-codegen-ppc.h" // NOLINT
#elif V8_TARGET_ARCH_MIPS
#include "src/crankshaft/mips/lithium-codegen-mips.h" // NOLINT
#elif V8_TARGET_ARCH_MIPS64
#include "src/crankshaft/mips64/lithium-codegen-mips64.h" // NOLINT
#elif V8_TARGET_ARCH_S390
#include "src/crankshaft/s390/lithium-codegen-s390.h" // NOLINT
#elif V8_TARGET_ARCH_X87
#include "src/crankshaft/x87/lithium-codegen-x87.h" // NOLINT
#else
#error Unsupported target architecture.
#endif
namespace v8 {
namespace internal {
const auto GetRegConfig = RegisterConfiguration::Crankshaft;
class HOptimizedGraphBuilderWithPositions : public HOptimizedGraphBuilder {
public:
explicit HOptimizedGraphBuilderWithPositions(CompilationInfo* info)
: HOptimizedGraphBuilder(info, true) {
SetSourcePosition(info->shared_info()->start_position());
}
#define DEF_VISIT(type) \
void Visit##type(type* node) override { \
SourcePosition old_position = SourcePosition::Unknown(); \
if (node->position() != kNoSourcePosition) { \
old_position = source_position(); \
SetSourcePosition(node->position()); \
} \
HOptimizedGraphBuilder::Visit##type(node); \
if (old_position.IsKnown()) { \
set_source_position(old_position); \
} \
}
EXPRESSION_NODE_LIST(DEF_VISIT)
#undef DEF_VISIT
#define DEF_VISIT(type) \
void Visit##type(type* node) override { \
SourcePosition old_position = SourcePosition::Unknown(); \
if (node->position() != kNoSourcePosition) { \
old_position = source_position(); \
SetSourcePosition(node->position()); \
} \
HOptimizedGraphBuilder::Visit##type(node); \
if (old_position.IsKnown()) { \
set_source_position(old_position); \
} \
}
STATEMENT_NODE_LIST(DEF_VISIT)
#undef DEF_VISIT
#define DEF_VISIT(type) \
void Visit##type(type* node) override { \
HOptimizedGraphBuilder::Visit##type(node); \
}
DECLARATION_NODE_LIST(DEF_VISIT)
#undef DEF_VISIT
};
HCompilationJob::Status HCompilationJob::PrepareJobImpl() {
if (!isolate()->use_crankshaft() ||
info()->shared_info()->must_use_ignition_turbo()) {
// Crankshaft is entirely disabled.
return FAILED;
}
// Optimization requires a version of fullcode with deoptimization support.
// Recompile the unoptimized version of the code if the current version
// doesn't have deoptimization support already.
// Otherwise, if we are gathering compilation time and space statistics
// for hydrogen, gather baseline statistics for a fullcode compilation.
bool should_recompile = !info()->shared_info()->has_deoptimization_support();
if (should_recompile || FLAG_hydrogen_stats) {
base::ElapsedTimer timer;
if (FLAG_hydrogen_stats) {
timer.Start();
}
if (!Compiler::EnsureDeoptimizationSupport(info())) {
return FAILED;
}
if (FLAG_hydrogen_stats) {
isolate()->GetHStatistics()->IncrementFullCodeGen(timer.Elapsed());
}
}
DCHECK(info()->shared_info()->has_deoptimization_support());
// Check the whitelist for Crankshaft.
if (!info()->shared_info()->PassesFilter(FLAG_hydrogen_filter)) {
return AbortOptimization(kHydrogenFilter);
}
Scope* scope = info()->scope();
if (LUnallocated::TooManyParameters(scope->num_parameters())) {
// Crankshaft would require too many Lithium operands.
return AbortOptimization(kTooManyParameters);
}
if (info()->is_osr() &&
LUnallocated::TooManyParametersOrStackSlots(scope->num_parameters(),
scope->num_stack_slots())) {
// Crankshaft would require too many Lithium operands.
return AbortOptimization(kTooManyParametersLocals);
}
if (IsGeneratorFunction(info()->shared_info()->kind())) {
// Crankshaft does not support generators.
return AbortOptimization(kGenerator);
}
if (FLAG_trace_hydrogen) {
isolate()->GetHTracer()->TraceCompilation(info());
}
// Optimization could have been disabled by the parser. Note that this check
// is only needed because the Hydrogen graph builder is missing some bailouts.
if (info()->shared_info()->optimization_disabled()) {
return AbortOptimization(
info()->shared_info()->disable_optimization_reason());
}
HOptimizedGraphBuilder* graph_builder =
(FLAG_hydrogen_track_positions || isolate()->is_profiling() ||
FLAG_trace_ic)
? new (info()->zone()) HOptimizedGraphBuilderWithPositions(info())
: new (info()->zone()) HOptimizedGraphBuilder(info(), false);
// Type-check the function.
AstTyper(info()->isolate(), info()->zone(), info()->closure(),
info()->scope(), info()->osr_ast_id(), info()->literal(),
graph_builder->bounds())
.Run();
graph_ = graph_builder->CreateGraph();
if (isolate()->has_pending_exception()) {
return FAILED;
}
if (graph_ == NULL) return FAILED;
if (info()->dependencies()->HasAborted()) {
// Dependency has changed during graph creation. Let's try again later.
return RetryOptimization(kBailedOutDueToDependencyChange);
}
return SUCCEEDED;
}
HCompilationJob::Status HCompilationJob::ExecuteJobImpl() {
DCHECK(graph_ != NULL);
BailoutReason bailout_reason = kNoReason;
if (graph_->Optimize(&bailout_reason)) {
chunk_ = LChunk::NewChunk(graph_);
if (chunk_ != NULL) return SUCCEEDED;
} else if (bailout_reason != kNoReason) {
info()->AbortOptimization(bailout_reason);
}
return FAILED;
}
HCompilationJob::Status HCompilationJob::FinalizeJobImpl() {
DCHECK(chunk_ != NULL);
DCHECK(graph_ != NULL);
{
// Deferred handles reference objects that were accessible during
// graph creation. To make sure that we don't encounter inconsistencies
// between graph creation and code generation, we disallow accessing
// objects through deferred handles during the latter, with exceptions.
DisallowDeferredHandleDereference no_deferred_handle_deref;
Handle<Code> optimized_code = chunk_->Codegen();
if (optimized_code.is_null()) {
if (info()->bailout_reason() == kNoReason) {
return AbortOptimization(kCodeGenerationFailed);
}
return FAILED;
}
RegisterWeakObjectsInOptimizedCode(optimized_code);
info()->SetCode(optimized_code);
}
// Add to the weak list of optimized code objects.
info()->context()->native_context()->AddOptimizedCode(*info()->code());
return SUCCEEDED;
}
HBasicBlock::HBasicBlock(HGraph* graph)
: block_id_(graph->GetNextBlockID()),
graph_(graph),
phis_(4, graph->zone()),
first_(NULL),
last_(NULL),
end_(NULL),
loop_information_(NULL),
predecessors_(2, graph->zone()),
dominator_(NULL),
dominated_blocks_(4, graph->zone()),
last_environment_(NULL),
argument_count_(-1),
first_instruction_index_(-1),
last_instruction_index_(-1),
deleted_phis_(4, graph->zone()),
parent_loop_header_(NULL),
inlined_entry_block_(NULL),
is_inline_return_target_(false),
is_reachable_(true),
dominates_loop_successors_(false),
is_osr_entry_(false),
is_ordered_(false) { }
Isolate* HBasicBlock::isolate() const {
return graph_->isolate();
}
void HBasicBlock::MarkUnreachable() {
is_reachable_ = false;
}
void HBasicBlock::AttachLoopInformation() {
DCHECK(!IsLoopHeader());
loop_information_ = new(zone()) HLoopInformation(this, zone());
}
void HBasicBlock::DetachLoopInformation() {
DCHECK(IsLoopHeader());
loop_information_ = NULL;
}
void HBasicBlock::AddPhi(HPhi* phi) {
DCHECK(!IsStartBlock());
phis_.Add(phi, zone());
phi->SetBlock(this);
}
void HBasicBlock::RemovePhi(HPhi* phi) {
DCHECK(phi->block() == this);
DCHECK(phis_.Contains(phi));
phi->Kill();
phis_.RemoveElement(phi);
phi->SetBlock(NULL);
}
void HBasicBlock::AddInstruction(HInstruction* instr, SourcePosition position) {
DCHECK(!IsStartBlock() || !IsFinished());
DCHECK(!instr->IsLinked());
DCHECK(!IsFinished());
if (position.IsKnown()) {
instr->set_position(position);
}
if (first_ == NULL) {
DCHECK(last_environment() != NULL);
DCHECK(!last_environment()->ast_id().IsNone());
HBlockEntry* entry = new(zone()) HBlockEntry();
entry->InitializeAsFirst(this);
if (position.IsKnown()) {
entry->set_position(position);
} else {
DCHECK(!FLAG_hydrogen_track_positions ||
!graph()->info()->IsOptimizing() || instr->IsAbnormalExit());
}
first_ = last_ = entry;
}
instr->InsertAfter(last_);
}
HPhi* HBasicBlock::AddNewPhi(int merged_index) {
if (graph()->IsInsideNoSideEffectsScope()) {
merged_index = HPhi::kInvalidMergedIndex;
}
HPhi* phi = new(zone()) HPhi(merged_index, zone());
AddPhi(phi);
return phi;
}
HSimulate* HBasicBlock::CreateSimulate(BailoutId ast_id,
RemovableSimulate removable) {
DCHECK(HasEnvironment());
HEnvironment* environment = last_environment();
DCHECK(ast_id.IsNone() ||
ast_id == BailoutId::StubEntry() ||
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, zone(), removable);
#ifdef DEBUG
instr->set_closure(environment->closure());
#endif
// Order of pushed values: newest (top of stack) first. This allows
// HSimulate::MergeWith() to easily append additional pushed values
// that are older (from further down the stack).
for (int i = 0; i < push_count; ++i) {
instr->AddPushedValue(environment->ExpressionStackAt(i));
}
for (GrowableBitVector::Iterator it(environment->assigned_variables(),
zone());
!it.Done();
it.Advance()) {
int index = it.Current();
instr->AddAssignedValue(index, environment->Lookup(index));
}
environment->ClearHistory();
return instr;
}
void HBasicBlock::Finish(HControlInstruction* end, SourcePosition position) {
DCHECK(!IsFinished());
AddInstruction(end, position);
end_ = end;
for (HSuccessorIterator it(end); !it.Done(); it.Advance()) {
it.Current()->RegisterPredecessor(this);
}
}
void HBasicBlock::Goto(HBasicBlock* block, SourcePosition position,
FunctionState* state, bool add_simulate) {
bool drop_extra = state != NULL &&
state->inlining_kind() == NORMAL_RETURN;
if (block->IsInlineReturnTarget()) {
HEnvironment* env = last_environment();
int argument_count = env->arguments_environment()->parameter_count();
AddInstruction(new(zone())
HLeaveInlined(state->entry(), argument_count),
position);
UpdateEnvironment(last_environment()->DiscardInlined(drop_extra));
}
if (add_simulate) AddNewSimulate(BailoutId::None(), position);
HGoto* instr = new(zone()) HGoto(block);
Finish(instr, position);
}
void HBasicBlock::AddLeaveInlined(HValue* return_value, FunctionState* state,
SourcePosition position) {
HBasicBlock* target = state->function_return();
bool drop_extra = state->inlining_kind() == NORMAL_RETURN;
DCHECK(target->IsInlineReturnTarget());
DCHECK(return_value != NULL);
HEnvironment* env = last_environment();
int argument_count = env->arguments_environment()->parameter_count();
AddInstruction(new(zone()) HLeaveInlined(state->entry(), argument_count),
position);
UpdateEnvironment(last_environment()->DiscardInlined(drop_extra));
last_environment()->Push(return_value);
AddNewSimulate(BailoutId::None(), position);
HGoto* instr = new(zone()) HGoto(target);
Finish(instr, position);
}
void HBasicBlock::SetInitialEnvironment(HEnvironment* env) {
DCHECK(!HasEnvironment());
DCHECK(first() == NULL);
UpdateEnvironment(env);
}
void HBasicBlock::UpdateEnvironment(HEnvironment* env) {
last_environment_ = env;
graph()->update_maximum_environment_size(env->first_expression_index());
}
void HBasicBlock::SetJoinId(BailoutId ast_id) {
int length = predecessors_.length();
DCHECK(length > 0);
for (int i = 0; i < length; i++) {
HBasicBlock* predecessor = predecessors_[i];
DCHECK(predecessor->end()->IsGoto());
HSimulate* simulate = HSimulate::cast(predecessor->end()->previous());
DCHECK(i != 0 ||
(predecessor->last_environment()->closure().is_null() ||
predecessor->last_environment()->closure()->shared()
->VerifyBailoutId(ast_id)));
simulate->set_ast_id(ast_id);
predecessor->last_environment()->set_ast_id(ast_id);
}
}
bool HBasicBlock::Dominates(HBasicBlock* other) const {
HBasicBlock* current = other->dominator();
while (current != NULL) {
if (current == this) return true;
current = current->dominator();
}
return false;
}
bool HBasicBlock::EqualToOrDominates(HBasicBlock* other) const {
if (this == other) return true;
return Dominates(other);
}
int HBasicBlock::LoopNestingDepth() const {
const HBasicBlock* current = this;
int result = (current->IsLoopHeader()) ? 1 : 0;
while (current->parent_loop_header() != NULL) {
current = current->parent_loop_header();
result++;
}
return result;
}
void HBasicBlock::PostProcessLoopHeader(IterationStatement* stmt) {
DCHECK(IsLoopHeader());
SetJoinId(stmt->EntryId());
if (predecessors()->length() == 1) {
// This is a degenerated loop.
DetachLoopInformation();
return;
}
// Only the first entry into the loop is from outside the loop. All other
// entries must be back edges.
for (int i = 1; i < predecessors()->length(); ++i) {
loop_information()->RegisterBackEdge(predecessors()->at(i));
}
}
void HBasicBlock::MarkSuccEdgeUnreachable(int succ) {
DCHECK(IsFinished());
HBasicBlock* succ_block = end()->SuccessorAt(succ);
DCHECK(succ_block->predecessors()->length() == 1);
succ_block->MarkUnreachable();
}
void HBasicBlock::RegisterPredecessor(HBasicBlock* pred) {
if (HasPredecessor()) {
// Only loop header blocks can have a predecessor added after
// instructions have been added to the block (they have phis for all
// values in the environment, these phis may be eliminated later).
DCHECK(IsLoopHeader() || first_ == NULL);
HEnvironment* incoming_env = pred->last_environment();
if (IsLoopHeader()) {
DCHECK_EQ(phis()->length(), incoming_env->length());
for (int i = 0; i < phis_.length(); ++i) {
phis_[i]->AddInput(incoming_env->values()->at(i));
}
} else {
last_environment()->AddIncomingEdge(this, pred->last_environment());
}
} else if (!HasEnvironment() && !IsFinished()) {
DCHECK(!IsLoopHeader());
SetInitialEnvironment(pred->last_environment()->Copy());
}
predecessors_.Add(pred, zone());
}
void HBasicBlock::AddDominatedBlock(HBasicBlock* block) {
DCHECK(!dominated_blocks_.Contains(block));
// Keep the list of dominated blocks sorted such that if there is two
// succeeding block in this list, the predecessor is before the successor.
int index = 0;
while (index < dominated_blocks_.length() &&
dominated_blocks_[index]->block_id() < block->block_id()) {
++index;
}
dominated_blocks_.InsertAt(index, block, zone());
}
void HBasicBlock::AssignCommonDominator(HBasicBlock* other) {
if (dominator_ == NULL) {
dominator_ = other;
other->AddDominatedBlock(this);
} else if (other->dominator() != NULL) {
HBasicBlock* first = dominator_;
HBasicBlock* second = other;
while (first != second) {
if (first->block_id() > second->block_id()) {
first = first->dominator();
} else {
second = second->dominator();
}
DCHECK(first != NULL && second != NULL);
}
if (dominator_ != first) {
DCHECK(dominator_->dominated_blocks_.Contains(this));
dominator_->dominated_blocks_.RemoveElement(this);
dominator_ = first;
first->AddDominatedBlock(this);
}
}
}
void HBasicBlock::AssignLoopSuccessorDominators() {
// Mark blocks that dominate all subsequent reachable blocks inside their
// loop. Exploit the fact that blocks are sorted in reverse post order. When
// the loop is visited in increasing block id order, if the number of
// non-loop-exiting successor edges at the dominator_candidate block doesn't
// exceed the number of previously encountered predecessor edges, there is no
// path from the loop header to any block with higher id that doesn't go
// through the dominator_candidate block. In this case, the
// dominator_candidate block is guaranteed to dominate all blocks reachable
// from it with higher ids.
HBasicBlock* last = loop_information()->GetLastBackEdge();
int outstanding_successors = 1; // one edge from the pre-header
// Header always dominates everything.
MarkAsLoopSuccessorDominator();
for (int j = block_id(); j <= last->block_id(); ++j) {
HBasicBlock* dominator_candidate = graph_->blocks()->at(j);
for (HPredecessorIterator it(dominator_candidate); !it.Done();
it.Advance()) {
HBasicBlock* predecessor = it.Current();
// Don't count back edges.
if (predecessor->block_id() < dominator_candidate->block_id()) {
outstanding_successors--;
}
}
// If more successors than predecessors have been seen in the loop up to
// now, it's not possible to guarantee that the current block dominates
// all of the blocks with higher IDs. In this case, assume conservatively
// that those paths through loop that don't go through the current block
// contain all of the loop's dependencies. Also be careful to record
// dominator information about the current loop that's being processed,
// and not nested loops, which will be processed when
// AssignLoopSuccessorDominators gets called on their header.
DCHECK(outstanding_successors >= 0);
HBasicBlock* parent_loop_header = dominator_candidate->parent_loop_header();
if (outstanding_successors == 0 &&
(parent_loop_header == this && !dominator_candidate->IsLoopHeader())) {
dominator_candidate->MarkAsLoopSuccessorDominator();
}
HControlInstruction* end = dominator_candidate->end();
for (HSuccessorIterator it(end); !it.Done(); it.Advance()) {
HBasicBlock* successor = it.Current();
// Only count successors that remain inside the loop and don't loop back
// to a loop header.
if (successor->block_id() > dominator_candidate->block_id() &&
successor->block_id() <= last->block_id()) {
// Backwards edges must land on loop headers.
DCHECK(successor->block_id() > dominator_candidate->block_id() ||
successor->IsLoopHeader());
outstanding_successors++;
}
}
}
}
int HBasicBlock::PredecessorIndexOf(HBasicBlock* predecessor) const {
for (int i = 0; i < predecessors_.length(); ++i) {
if (predecessors_[i] == predecessor) return i;
}
UNREACHABLE();
return -1;
}
#ifdef DEBUG
void HBasicBlock::Verify() {
// Check that every block is finished.
DCHECK(IsFinished());
DCHECK(block_id() >= 0);
// Check that the incoming edges are in edge split form.
if (predecessors_.length() > 1) {
for (int i = 0; i < predecessors_.length(); ++i) {
DCHECK(predecessors_[i]->end()->SecondSuccessor() == NULL);
}
}
}
#endif
void HLoopInformation::RegisterBackEdge(HBasicBlock* block) {
this->back_edges_.Add(block, block->zone());
AddBlock(block);
}
HBasicBlock* HLoopInformation::GetLastBackEdge() const {
int max_id = -1;
HBasicBlock* result = NULL;
for (int i = 0; i < back_edges_.length(); ++i) {
HBasicBlock* cur = back_edges_[i];
if (cur->block_id() > max_id) {
max_id = cur->block_id();
result = cur;
}
}
return result;
}
void HLoopInformation::AddBlock(HBasicBlock* block) {
if (block == loop_header()) return;
if (block->parent_loop_header() == loop_header()) return;
if (block->parent_loop_header() != NULL) {
AddBlock(block->parent_loop_header());
} else {
block->set_parent_loop_header(loop_header());
blocks_.Add(block, block->zone());
for (int i = 0; i < block->predecessors()->length(); ++i) {
AddBlock(block->predecessors()->at(i));
}
}
}
#ifdef DEBUG
// Checks reachability of the blocks in this graph and stores a bit in
// the BitVector "reachable()" for every block that can be reached
// from the start block of the graph. If "dont_visit" is non-null, the given
// block is treated as if it would not be part of the graph. "visited_count()"
// returns the number of reachable blocks.
class ReachabilityAnalyzer BASE_EMBEDDED {
public:
ReachabilityAnalyzer(HBasicBlock* entry_block,
int block_count,
HBasicBlock* dont_visit)
: visited_count_(0),
stack_(16, entry_block->zone()),
reachable_(block_count, entry_block->zone()),
dont_visit_(dont_visit) {
PushBlock(entry_block);
Analyze();
}
int visited_count() const { return visited_count_; }
const BitVector* reachable() const { return &reachable_; }
private:
void PushBlock(HBasicBlock* block) {
if (block != NULL && block != dont_visit_ &&
!reachable_.Contains(block->block_id())) {
reachable_.Add(block->block_id());
stack_.Add(block, block->zone());
visited_count_++;
}
}
void Analyze() {
while (!stack_.is_empty()) {
HControlInstruction* end = stack_.RemoveLast()->end();
for (HSuccessorIterator it(end); !it.Done(); it.Advance()) {
PushBlock(it.Current());
}
}
}
int visited_count_;
ZoneList<HBasicBlock*> stack_;
BitVector reachable_;
HBasicBlock* dont_visit_;
};
void HGraph::Verify(bool do_full_verify) const {
Heap::RelocationLock relocation_lock(isolate()->heap());
AllowHandleDereference allow_deref;
AllowDeferredHandleDereference allow_deferred_deref;
for (int i = 0; i < blocks_.length(); i++) {
HBasicBlock* block = blocks_.at(i);
block->Verify();
// Check that every block contains at least one node and that only the last
// node is a control instruction.
HInstruction* current = block->first();
DCHECK(current != NULL && current->IsBlockEntry());
while (current != NULL) {
DCHECK((current->next() == NULL) == current->IsControlInstruction());
DCHECK(current->block() == block);
current->Verify();
current = current->next();
}
// Check that successors are correctly set.
HBasicBlock* first = block->end()->FirstSuccessor();
HBasicBlock* second = block->end()->SecondSuccessor();
DCHECK(second == NULL || first != NULL);
// Check that the predecessor array is correct.
if (first != NULL) {
DCHECK(first->predecessors()->Contains(block));
if (second != NULL) {
DCHECK(second->predecessors()->Contains(block));
}
}
// Check that phis have correct arguments.
for (int j = 0; j < block->phis()->length(); j++) {
HPhi* phi = block->phis()->at(j);
phi->Verify();
}
// Check that all join blocks have predecessors that end with an
// unconditional goto and agree on their environment node id.
if (block->predecessors()->length() >= 2) {
BailoutId id =
block->predecessors()->first()->last_environment()->ast_id();
for (int k = 0; k < block->predecessors()->length(); k++) {
HBasicBlock* predecessor = block->predecessors()->at(k);
DCHECK(predecessor->end()->IsGoto() ||
predecessor->end()->IsDeoptimize());
DCHECK(predecessor->last_environment()->ast_id() == id);
}
}
}
// Check special property of first block to have no predecessors.
DCHECK(blocks_.at(0)->predecessors()->is_empty());
if (do_full_verify) {
// Check that the graph is fully connected.
ReachabilityAnalyzer analyzer(entry_block_, blocks_.length(), NULL);
DCHECK(analyzer.visited_count() == blocks_.length());
// Check that entry block dominator is NULL.
DCHECK(entry_block_->dominator() == NULL);
// Check dominators.
for (int i = 0; i < blocks_.length(); ++i) {
HBasicBlock* block = blocks_.at(i);
if (block->dominator() == NULL) {
// Only start block may have no dominator assigned to.
DCHECK(i == 0);
} else {
// Assert that block is unreachable if dominator must not be visited.
ReachabilityAnalyzer dominator_analyzer(entry_block_,
blocks_.length(),
block->dominator());
DCHECK(!dominator_analyzer.reachable()->Contains(block->block_id()));
}
}
}
}
#endif
HConstant* HGraph::GetConstant(SetOncePointer<HConstant>* pointer,
int32_t value) {
if (!pointer->is_set()) {
// Can't pass GetInvalidContext() to HConstant::New, because that will
// recursively call GetConstant
HConstant* constant = HConstant::New(isolate(), zone(), NULL, value);
constant->InsertAfter(entry_block()->first());
pointer->set(constant);
return constant;
}
return ReinsertConstantIfNecessary(pointer->get());
}
HConstant* HGraph::ReinsertConstantIfNecessary(HConstant* constant) {
if (!constant->IsLinked()) {
// The constant was removed from the graph. Reinsert.
constant->ClearFlag(HValue::kIsDead);
constant->InsertAfter(entry_block()->first());
}
return constant;
}
HConstant* HGraph::GetConstant0() {
return GetConstant(&constant_0_, 0);
}
HConstant* HGraph::GetConstant1() {
return GetConstant(&constant_1_, 1);
}
HConstant* HGraph::GetConstantMinus1() {
return GetConstant(&constant_minus1_, -1);
}
HConstant* HGraph::GetConstantBool(bool value) {
return value ? GetConstantTrue() : GetConstantFalse();
}
#define DEFINE_GET_CONSTANT(Name, name, constant, type, htype, boolean_value, \
undetectable) \
HConstant* HGraph::GetConstant##Name() { \
if (!constant_##name##_.is_set()) { \
HConstant* constant = new (zone()) HConstant( \
Unique<Object>::CreateImmovable(isolate()->factory()->constant()), \
Unique<Map>::CreateImmovable(isolate()->factory()->type##_map()), \
false, Representation::Tagged(), htype, true, boolean_value, \
undetectable, ODDBALL_TYPE); \
constant->InsertAfter(entry_block()->first()); \
constant_##name##_.set(constant); \
} \
return ReinsertConstantIfNecessary(constant_##name##_.get()); \
}
DEFINE_GET_CONSTANT(Undefined, undefined, undefined_value, undefined,
HType::Undefined(), false, true)
DEFINE_GET_CONSTANT(True, true, true_value, boolean, HType::Boolean(), true,
false)
DEFINE_GET_CONSTANT(False, false, false_value, boolean, HType::Boolean(), false,
false)
DEFINE_GET_CONSTANT(Hole, the_hole, the_hole_value, the_hole, HType::None(),
false, false)
DEFINE_GET_CONSTANT(Null, null, null_value, null, HType::Null(), false, true)
DEFINE_GET_CONSTANT(OptimizedOut, optimized_out, optimized_out, optimized_out,
HType::None(), false, false)
#undef DEFINE_GET_CONSTANT
#define DEFINE_IS_CONSTANT(Name, name) \
bool HGraph::IsConstant##Name(HConstant* constant) { \
return constant_##name##_.is_set() && constant == constant_##name##_.get(); \
}
DEFINE_IS_CONSTANT(Undefined, undefined)
DEFINE_IS_CONSTANT(0, 0)
DEFINE_IS_CONSTANT(1, 1)
DEFINE_IS_CONSTANT(Minus1, minus1)
DEFINE_IS_CONSTANT(True, true)
DEFINE_IS_CONSTANT(False, false)
DEFINE_IS_CONSTANT(Hole, the_hole)
DEFINE_IS_CONSTANT(Null, null)
#undef DEFINE_IS_CONSTANT
HConstant* HGraph::GetInvalidContext() {
return GetConstant(&constant_invalid_context_, 0xFFFFC0C7);
}
bool HGraph::IsStandardConstant(HConstant* constant) {
if (IsConstantUndefined(constant)) return true;
if (IsConstant0(constant)) return true;
if (IsConstant1(constant)) return true;
if (IsConstantMinus1(constant)) return true;
if (IsConstantTrue(constant)) return true;
if (IsConstantFalse(constant)) return true;
if (IsConstantHole(constant)) return true;
if (IsConstantNull(constant)) return true;
return false;
}
HGraphBuilder::IfBuilder::IfBuilder() : builder_(NULL), needs_compare_(true) {}
HGraphBuilder::IfBuilder::IfBuilder(HGraphBuilder* builder)
: needs_compare_(true) {
Initialize(builder);
}
HGraphBuilder::IfBuilder::IfBuilder(HGraphBuilder* builder,
HIfContinuation* continuation)
: needs_compare_(false), first_true_block_(NULL), first_false_block_(NULL) {
InitializeDontCreateBlocks(builder);
continuation->Continue(&first_true_block_, &first_false_block_);
}
void HGraphBuilder::IfBuilder::InitializeDontCreateBlocks(
HGraphBuilder* builder) {
builder_ = builder;
finished_ = false;
did_then_ = false;
did_else_ = false;
did_else_if_ = false;
did_and_ = false;
did_or_ = false;
captured_ = false;
pending_merge_block_ = false;
split_edge_merge_block_ = NULL;
merge_at_join_blocks_ = NULL;
normal_merge_at_join_block_count_ = 0;
deopt_merge_at_join_block_count_ = 0;
}
void HGraphBuilder::IfBuilder::Initialize(HGraphBuilder* builder) {
InitializeDontCreateBlocks(builder);
HEnvironment* env = builder->environment();
first_true_block_ = builder->CreateBasicBlock(env->Copy());
first_false_block_ = builder->CreateBasicBlock(env->Copy());
}
HControlInstruction* HGraphBuilder::IfBuilder::AddCompare(
HControlInstruction* compare) {
DCHECK(did_then_ == did_else_);
if (did_else_) {
// Handle if-then-elseif
did_else_if_ = true;
did_else_ = false;
did_then_ = false;
did_and_ = false;
did_or_ = false;
pending_merge_block_ = false;
split_edge_merge_block_ = NULL;
HEnvironment* env = builder()->environment();
first_true_block_ = builder()->CreateBasicBlock(env->Copy());
first_false_block_ = builder()->CreateBasicBlock(env->Copy());
}
if (split_edge_merge_block_ != NULL) {
HEnvironment* env = first_false_block_->last_environment();
HBasicBlock* split_edge = builder()->CreateBasicBlock(env->Copy());
if (did_or_) {
compare->SetSuccessorAt(0, split_edge);
compare->SetSuccessorAt(1, first_false_block_);
} else {
compare->SetSuccessorAt(0, first_true_block_);
compare->SetSuccessorAt(1, split_edge);
}
builder()->GotoNoSimulate(split_edge, split_edge_merge_block_);
} else {
compare->SetSuccessorAt(0, first_true_block_);
compare->SetSuccessorAt(1, first_false_block_);
}
builder()->FinishCurrentBlock(compare);
needs_compare_ = false;
return compare;
}
void HGraphBuilder::IfBuilder::Or() {
DCHECK(!needs_compare_);
DCHECK(!did_and_);
did_or_ = true;
HEnvironment* env = first_false_block_->last_environment();
if (split_edge_merge_block_ == NULL) {
split_edge_merge_block_ = builder()->CreateBasicBlock(env->Copy());
builder()->GotoNoSimulate(first_true_block_, split_edge_merge_block_);
first_true_block_ = split_edge_merge_block_;
}
builder()->set_current_block(first_false_block_);
first_false_block_ = builder()->CreateBasicBlock(env->Copy());
}
void HGraphBuilder::IfBuilder::And() {
DCHECK(!needs_compare_);
DCHECK(!did_or_);
did_and_ = true;
HEnvironment* env = first_false_block_->last_environment();
if (split_edge_merge_block_ == NULL) {
split_edge_merge_block_ = builder()->CreateBasicBlock(env->Copy());
builder()->GotoNoSimulate(first_false_block_, split_edge_merge_block_);
first_false_block_ = split_edge_merge_block_;
}
builder()->set_current_block(first_true_block_);
first_true_block_ = builder()->CreateBasicBlock(env->Copy());
}
void HGraphBuilder::IfBuilder::CaptureContinuation(
HIfContinuation* continuation) {
DCHECK(!did_else_if_);
DCHECK(!finished_);
DCHECK(!captured_);
HBasicBlock* true_block = NULL;
HBasicBlock* false_block = NULL;
Finish(&true_block, &false_block);
DCHECK(true_block != NULL);
DCHECK(false_block != NULL);
continuation->Capture(true_block, false_block);
captured_ = true;
builder()->set_current_block(NULL);
End();
}
void HGraphBuilder::IfBuilder::JoinContinuation(HIfContinuation* continuation) {
DCHECK(!did_else_if_);
DCHECK(!finished_);
DCHECK(!captured_);
HBasicBlock* true_block = NULL;
HBasicBlock* false_block = NULL;
Finish(&true_block, &false_block);
merge_at_join_blocks_ = NULL;
if (true_block != NULL && !true_block->IsFinished()) {
DCHECK(continuation->IsTrueReachable());
builder()->GotoNoSimulate(true_block, continuation->true_branch());
}
if (false_block != NULL && !false_block->IsFinished()) {
DCHECK(continuation->IsFalseReachable());
builder()->GotoNoSimulate(false_block, continuation->false_branch());
}
captured_ = true;
End();
}
void HGraphBuilder::IfBuilder::Then() {
DCHECK(!captured_);
DCHECK(!finished_);
did_then_ = true;
if (needs_compare_) {
// Handle if's without any expressions, they jump directly to the "else"
// branch. However, we must pretend that the "then" branch is reachable,
// so that the graph builder visits it and sees any live range extending
// constructs within it.
HConstant* constant_false = builder()->graph()->GetConstantFalse();
ToBooleanHints boolean_type = ToBooleanHint::kBoolean;
HBranch* branch = builder()->New<HBranch>(
constant_false, boolean_type, first_true_block_, first_false_block_);
builder()->FinishCurrentBlock(branch);
}
builder()->set_current_block(first_true_block_);
pending_merge_block_ = true;
}
void HGraphBuilder::IfBuilder::Else() {
DCHECK(did_then_);
DCHECK(!captured_);
DCHECK(!finished_);
AddMergeAtJoinBlock(false);
builder()->set_current_block(first_false_block_);
pending_merge_block_ = true;
did_else_ = true;
}
void HGraphBuilder::IfBuilder::Deopt(DeoptimizeReason reason) {
DCHECK(did_then_);
builder()->Add<HDeoptimize>(reason, Deoptimizer::EAGER);
AddMergeAtJoinBlock(true);
}
void HGraphBuilder::IfBuilder::Return(HValue* value) {
HValue* parameter_count = builder()->graph()->GetConstantMinus1();
builder()->FinishExitCurrentBlock(
builder()->New<HReturn>(value, parameter_count));
AddMergeAtJoinBlock(false);
}
void HGraphBuilder::IfBuilder::AddMergeAtJoinBlock(bool deopt) {
if (!pending_merge_block_) return;
HBasicBlock* block = builder()->current_block();
DCHECK(block == NULL || !block->IsFinished());
MergeAtJoinBlock* record = new (builder()->zone())
MergeAtJoinBlock(block, deopt, merge_at_join_blocks_);
merge_at_join_blocks_ = record;
if (block != NULL) {
DCHECK(block->end() == NULL);
if (deopt) {
normal_merge_at_join_block_count_++;
} else {
deopt_merge_at_join_block_count_++;
}
}
builder()->set_current_block(NULL);
pending_merge_block_ = false;
}
void HGraphBuilder::IfBuilder::Finish() {
DCHECK(!finished_);
if (!did_then_) {
Then();
}
AddMergeAtJoinBlock(false);
if (!did_else_) {
Else();
AddMergeAtJoinBlock(false);
}
finished_ = true;
}
void HGraphBuilder::IfBuilder::Finish(HBasicBlock** then_continuation,
HBasicBlock** else_continuation) {
Finish();
MergeAtJoinBlock* else_record = merge_at_join_blocks_;
if (else_continuation != NULL) {
*else_continuation = else_record->block_;
}
MergeAtJoinBlock* then_record = else_record->next_;
if (then_continuation != NULL) {
*then_continuation = then_record->block_;
}
DCHECK(then_record->next_ == NULL);
}
void HGraphBuilder::IfBuilder::EndUnreachable() {
if (captured_) return;
Finish();
builder()->set_current_block(nullptr);
}
void HGraphBuilder::IfBuilder::End() {
if (captured_) return;
Finish();
int total_merged_blocks = normal_merge_at_join_block_count_ +
deopt_merge_at_join_block_count_;
DCHECK(total_merged_blocks >= 1);
HBasicBlock* merge_block =
total_merged_blocks == 1 ? NULL : builder()->graph()->CreateBasicBlock();
// Merge non-deopt blocks first to ensure environment has right size for
// padding.
MergeAtJoinBlock* current = merge_at_join_blocks_;
while (current != NULL) {
if (!current->deopt_ && current->block_ != NULL) {
// If there is only one block that makes it through to the end of the
// if, then just set it as the current block and continue rather then
// creating an unnecessary merge block.
if (total_merged_blocks == 1) {
builder()->set_current_block(current->block_);
return;
}
builder()->GotoNoSimulate(current->block_, merge_block);
}
current = current->next_;
}
// Merge deopt blocks, padding when necessary.
current = merge_at_join_blocks_;
while (current != NULL) {
if (current->deopt_ && current->block_ != NULL) {
current->block_->FinishExit(
HAbnormalExit::New(builder()->isolate(), builder()->zone(), NULL),
SourcePosition::Unknown());
}
current = current->next_;
}
builder()->set_current_block(merge_block);
}
HGraphBuilder::LoopBuilder::LoopBuilder(HGraphBuilder* builder) {
Initialize(builder, NULL, kWhileTrue, NULL);
}
HGraphBuilder::LoopBuilder::LoopBuilder(HGraphBuilder* builder, HValue* context,
LoopBuilder::Direction direction) {
Initialize(builder, context, direction, builder->graph()->GetConstant1());
}
HGraphBuilder::LoopBuilder::LoopBuilder(HGraphBuilder* builder, HValue* context,
LoopBuilder::Direction direction,
HValue* increment_amount) {
Initialize(builder, context, direction, increment_amount);
increment_amount_ = increment_amount;
}
void HGraphBuilder::LoopBuilder::Initialize(HGraphBuilder* builder,
HValue* context,
Direction direction,
HValue* increment_amount) {
builder_ = builder;
context_ = context;
direction_ = direction;
increment_amount_ = increment_amount;
finished_ = false;
header_block_ = builder->CreateLoopHeaderBlock();
body_block_ = NULL;
exit_block_ = NULL;
exit_trampoline_block_ = NULL;
}
HValue* HGraphBuilder::LoopBuilder::BeginBody(
HValue* initial,
HValue* terminating,
Token::Value token) {
DCHECK(direction_ != kWhileTrue);
HEnvironment* env = builder_->environment();
phi_ = header_block_->AddNewPhi(env->values()->length());
phi_->AddInput(initial);
env->Push(initial);
builder_->GotoNoSimulate(header_block_);
HEnvironment* body_env = env->Copy();
HEnvironment* exit_env = env->Copy();
// Remove the phi from the expression stack
body_env->Pop();
exit_env->Pop();
body_block_ = builder_->CreateBasicBlock(body_env);
exit_block_ = builder_->CreateBasicBlock(exit_env);
builder_->set_current_block(header_block_);
env->Pop();
builder_->FinishCurrentBlock(builder_->New<HCompareNumericAndBranch>(
phi_, terminating, token, body_block_, exit_block_));
builder_->set_current_block(body_block_);
if (direction_ == kPreIncrement || direction_ == kPreDecrement) {
Isolate* isolate = builder_->isolate();
HValue* one = builder_->graph()->GetConstant1();
if (direction_ == kPreIncrement) {
increment_ = HAdd::New(isolate, zone(), context_, phi_, one);
} else {
increment_ = HSub::New(isolate, zone(), context_, phi_, one);
}
increment_->ClearFlag(HValue::kCanOverflow);
builder_->AddInstruction(increment_);
return increment_;
} else {
return phi_;
}
}
void HGraphBuilder::LoopBuilder::BeginBody(int drop_count) {
DCHECK(direction_ == kWhileTrue);
HEnvironment* env = builder_->environment();
builder_->GotoNoSimulate(header_block_);
builder_->set_current_block(header_block_);
env->Drop(drop_count);
}
void HGraphBuilder::LoopBuilder::Break() {
if (exit_trampoline_block_ == NULL) {
// Its the first time we saw a break.
if (direction_ == kWhileTrue) {
HEnvironment* env = builder_->environment()->Copy();
exit_trampoline_block_ = builder_->CreateBasicBlock(env);
} else {
HEnvironment* env = exit_block_->last_environment()->Copy();
exit_trampoline_block_ = builder_->CreateBasicBlock(env);
builder_->GotoNoSimulate(exit_block_, exit_trampoline_block_);
}
}
builder_->GotoNoSimulate(exit_trampoline_block_);
builder_->set_current_block(NULL);
}
void HGraphBuilder::LoopBuilder::EndBody() {
DCHECK(!finished_);
if (direction_ == kPostIncrement || direction_ == kPostDecrement) {
Isolate* isolate = builder_->isolate();
if (direction_ == kPostIncrement) {
increment_ =
HAdd::New(isolate, zone(), context_, phi_, increment_amount_);
} else {
increment_ =
HSub::New(isolate, zone(), context_, phi_, increment_amount_);
}
increment_->ClearFlag(HValue::kCanOverflow);
builder_->AddInstruction(increment_);
}
if (direction_ != kWhileTrue) {
// Push the new increment value on the expression stack to merge into
// the phi.
builder_->environment()->Push(increment_);
}
HBasicBlock* last_block = builder_->current_block();
builder_->GotoNoSimulate(last_block, header_block_);
header_block_->loop_information()->RegisterBackEdge(last_block);
if (exit_trampoline_block_ != NULL) {
builder_->set_current_block(exit_trampoline_block_);
} else {
builder_->set_current_block(exit_block_);
}
finished_ = true;
}
HGraph* HGraphBuilder::CreateGraph() {
DCHECK(!FLAG_minimal);
graph_ = new (zone()) HGraph(info_, descriptor_);
if (FLAG_hydrogen_stats) isolate()->GetHStatistics()->Initialize(info_);
CompilationPhase phase("H_Block building", info_);
set_current_block(graph()->entry_block());
if (!BuildGraph()) return NULL;
graph()->FinalizeUniqueness();
return graph_;
}
HInstruction* HGraphBuilder::AddInstruction(HInstruction* instr) {
DCHECK(current_block() != NULL);
DCHECK(!FLAG_hydrogen_track_positions || position_.IsKnown() ||
!info_->IsOptimizing());
current_block()->AddInstruction(instr, source_position());
if (graph()->IsInsideNoSideEffectsScope()) {
instr->SetFlag(HValue::kHasNoObservableSideEffects);
}
return instr;
}
void HGraphBuilder::FinishCurrentBlock(HControlInstruction* last) {
DCHECK(!FLAG_hydrogen_track_positions || !info_->IsOptimizing() ||
position_.IsKnown());
current_block()->Finish(last, source_position());
if (last->IsReturn() || last->IsAbnormalExit()) {
set_current_block(NULL);
}
}
void HGraphBuilder::FinishExitCurrentBlock(HControlInstruction* instruction) {
DCHECK(!FLAG_hydrogen_track_positions || !info_->IsOptimizing() ||
position_.IsKnown());
current_block()->FinishExit(instruction, source_position());
if (instruction->IsReturn() || instruction->IsAbnormalExit()) {
set_current_block(NULL);
}
}
void HGraphBuilder::AddIncrementCounter(StatsCounter* counter) {
if (FLAG_native_code_counters && counter->Enabled()) {
HValue* reference = Add<HConstant>(ExternalReference(counter));
HValue* old_value =
Add<HLoadNamedField>(reference, nullptr, HObjectAccess::ForCounter());
HValue* new_value = AddUncasted<HAdd>(old_value, graph()->GetConstant1());
new_value->ClearFlag(HValue::kCanOverflow); // Ignore counter overflow
Add<HStoreNamedField>(reference, HObjectAccess::ForCounter(),
new_value, STORE_TO_INITIALIZED_ENTRY);
}
}
void HGraphBuilder::AddSimulate(BailoutId id,
RemovableSimulate removable) {
DCHECK(current_block() != NULL);
DCHECK(!graph()->IsInsideNoSideEffectsScope());
current_block()->AddNewSimulate(id, source_position(), removable);
}
HBasicBlock* HGraphBuilder::CreateBasicBlock(HEnvironment* env) {
HBasicBlock* b = graph()->CreateBasicBlock();
b->SetInitialEnvironment(env);
return b;
}
HBasicBlock* HGraphBuilder::CreateLoopHeaderBlock() {
HBasicBlock* header = graph()->CreateBasicBlock();
HEnvironment* entry_env = environment()->CopyAsLoopHeader(header);
header->SetInitialEnvironment(entry_env);
header->AttachLoopInformation();
return header;
}
HValue* HGraphBuilder::BuildGetElementsKind(HValue* object) {
HValue* map = Add<HLoadNamedField>(object, nullptr, HObjectAccess::ForMap());
HValue* bit_field2 =
Add<HLoadNamedField>(map, nullptr, HObjectAccess::ForMapBitField2());
return BuildDecodeField<Map::ElementsKindBits>(bit_field2);
}
HValue* HGraphBuilder::BuildEnumLength(HValue* map) {
NoObservableSideEffectsScope scope(this);
HValue* bit_field3 =
Add<HLoadNamedField>(map, nullptr, HObjectAccess::ForMapBitField3());
return BuildDecodeField<Map::EnumLengthBits>(bit_field3);
}
HValue* HGraphBuilder::BuildCheckHeapObject(HValue* obj) {
if (obj->type().IsHeapObject()) return obj;
return Add<HCheckHeapObject>(obj);
}
void HGraphBuilder::FinishExitWithHardDeoptimization(DeoptimizeReason reason) {
Add<HDeoptimize>(reason, Deoptimizer::EAGER);
FinishExitCurrentBlock(New<HAbnormalExit>());
}
HValue* HGraphBuilder::BuildCheckString(HValue* string) {
if (!string->type().IsString()) {
DCHECK(!string->IsConstant() ||
!HConstant::cast(string)->HasStringValue());
BuildCheckHeapObject(string);
return Add<HCheckInstanceType>(string, HCheckInstanceType::IS_STRING);
}
return string;
}
HValue* HGraphBuilder::BuildWrapReceiver(HValue* object, HValue* checked) {
if (object->type().IsJSObject()) return object;
HValue* function = checked->ActualValue();
if (function->IsConstant() &&
HConstant::cast(function)->handle(isolate())->IsJSFunction()) {
Handle<JSFunction> f = Handle<JSFunction>::cast(
HConstant::cast(function)->handle(isolate()));
SharedFunctionInfo* shared = f->shared();
if (is_strict(shared->language_mode()) || shared->native()) return object;
}
return Add<HWrapReceiver>(object, checked);
}
HValue* HGraphBuilder::BuildCheckAndGrowElementsCapacity(
HValue* object, HValue* elements, ElementsKind kind, HValue* length,
HValue* capacity, HValue* key) {
HValue* max_gap = Add<HConstant>(static_cast<int32_t>(JSObject::kMaxGap));
HValue* max_capacity = AddUncasted<HAdd>(capacity, max_gap);
Add<HBoundsCheck>(key, max_capacity);
HValue* new_capacity = BuildNewElementsCapacity(key);
HValue* new_elements = BuildGrowElementsCapacity(object, elements, kind, kind,
length, new_capacity);
return new_elements;
}
HValue* HGraphBuilder::BuildCheckForCapacityGrow(
HValue* object,
HValue* elements,
ElementsKind kind,
HValue* length,
HValue* key,
bool is_js_array,
PropertyAccessType access_type) {
IfBuilder length_checker(this);
Token::Value token = IsHoleyElementsKind(kind) ? Token::GTE : Token::EQ;
length_checker.If<HCompareNumericAndBranch>(key, length, token);
length_checker.Then();
HValue* current_capacity = AddLoadFixedArrayLength(elements);
if (top_info()->IsStub()) {
IfBuilder capacity_checker(this);
capacity_checker.If<HCompareNumericAndBranch>(key, current_capacity,
Token::GTE);
capacity_checker.Then();
HValue* new_elements = BuildCheckAndGrowElementsCapacity(
object, elements, kind, length, current_capacity, key);
environment()->Push(new_elements);
capacity_checker.Else();
environment()->Push(elements);
capacity_checker.End();
} else {
HValue* result = Add<HMaybeGrowElements>(
object, elements, key, current_capacity, is_js_array, kind);
environment()->Push(result);
}
if (is_js_array) {
HValue* new_length = AddUncasted<HAdd>(key, graph_->GetConstant1());
new_length->ClearFlag(HValue::kCanOverflow);
Add<HStoreNamedField>(object, HObjectAccess::ForArrayLength(kind),
new_length);
}
if (access_type == STORE && kind == FAST_SMI_ELEMENTS) {
HValue* checked_elements = environment()->Top();
// Write zero to ensure that the new element is initialized with some smi.
Add<HStoreKeyed>(checked_elements, key, graph()->GetConstant0(), nullptr,
kind);
}
length_checker.Else();
Add<HBoundsCheck>(key, length);
environment()->Push(elements);
length_checker.End();
return environment()->Pop();
}
HValue* HGraphBuilder::BuildCopyElementsOnWrite(HValue* object,
HValue* elements,
ElementsKind kind,
HValue* length) {
Factory* factory = isolate()->factory();
IfBuilder cow_checker(this);
cow_checker.If<HCompareMap>(elements, factory->fixed_cow_array_map());
cow_checker.Then();
HValue* capacity = AddLoadFixedArrayLength(elements);
HValue* new_elements = BuildGrowElementsCapacity(object, elements, kind,
kind, length, capacity);
environment()->Push(new_elements);
cow_checker.Else();
environment()->Push(elements);
cow_checker.End();
return environment()->Pop();
}
HValue* HGraphBuilder::BuildElementIndexHash(HValue* index) {
int32_t seed_value = static_cast<uint32_t>(isolate()->heap()->HashSeed());
HValue* seed = Add<HConstant>(seed_value);
HValue* hash = AddUncasted<HBitwise>(Token::BIT_XOR, index, seed);
// hash = ~hash + (hash << 15);
HValue* shifted_hash = AddUncasted<HShl>(hash, Add<HConstant>(15));
HValue* not_hash = AddUncasted<HBitwise>(Token::BIT_XOR, hash,
graph()->GetConstantMinus1());
hash = AddUncasted<HAdd>(shifted_hash, not_hash);
// hash = hash ^ (hash >> 12);
shifted_hash = AddUncasted<HShr>(hash, Add<HConstant>(12));
hash = AddUncasted<HBitwise>(Token::BIT_XOR, hash, shifted_hash);
// hash = hash + (hash << 2);
shifted_hash = AddUncasted<HShl>(hash, Add<HConstant>(2));
hash = AddUncasted<HAdd>(hash, shifted_hash);
// hash = hash ^ (hash >> 4);
shifted_hash = AddUncasted<HShr>(hash, Add<HConstant>(4));
hash = AddUncasted<HBitwise>(Token::BIT_XOR, hash, shifted_hash);
// hash = hash * 2057;
hash = AddUncasted<HMul>(hash, Add<HConstant>(2057));
hash->ClearFlag(HValue::kCanOverflow);
// hash = hash ^ (hash >> 16);
shifted_hash = AddUncasted<HShr>(hash, Add<HConstant>(16));
return AddUncasted<HBitwise>(Token::BIT_XOR, hash, shifted_hash);
}
HValue* HGraphBuilder::BuildUncheckedDictionaryElementLoad(HValue* receiver,
HValue* elements,
HValue* key,
HValue* hash) {
HValue* capacity =
Add<HLoadKeyed>(elements, Add<HConstant>(NameDictionary::kCapacityIndex),
nullptr, nullptr, FAST_ELEMENTS);
HValue* mask = AddUncasted<HSub>(capacity, graph()->GetConstant1());
mask->ChangeRepresentation(Representation::Integer32());
mask->ClearFlag(HValue::kCanOverflow);
HValue* entry = hash;
HValue* count = graph()->GetConstant1();
Push(entry);
Push(count);
HIfContinuation return_or_loop_continuation(graph()->CreateBasicBlock(),
graph()->CreateBasicBlock());
HIfContinuation found_key_match_continuation(graph()->CreateBasicBlock(),
graph()->CreateBasicBlock());
LoopBuilder probe_loop(this);
probe_loop.BeginBody(2); // Drop entry, count from last environment to
// appease live range building without simulates.
count = Pop();
entry = Pop();
entry = AddUncasted<HBitwise>(Token::BIT_AND, entry, mask);
int entry_size = SeededNumberDictionary::kEntrySize;
HValue* base_index = AddUncasted<HMul>(entry, Add<HConstant>(entry_size));
base_index->ClearFlag(HValue::kCanOverflow);
int start_offset = SeededNumberDictionary::kElementsStartIndex;
HValue* key_index =
AddUncasted<HAdd>(base_index, Add<HConstant>(start_offset));
key_index->ClearFlag(HValue::kCanOverflow);
HValue* candidate_key =
Add<HLoadKeyed>(elements, key_index, nullptr, nullptr, FAST_ELEMENTS);
IfBuilder if_undefined(this);
if_undefined.If<HCompareObjectEqAndBranch>(candidate_key,
graph()->GetConstantUndefined());
if_undefined.Then();
{
// element == undefined means "not found". Call the runtime.
// TODO(jkummerow): walk the prototype chain instead.
Add<HPushArguments>(receiver, key);
Push(Add<HCallRuntime>(Runtime::FunctionForId(Runtime::kKeyedGetProperty),
2));
}
if_undefined.Else();
{
IfBuilder if_match(this);
if_match.If<HCompareObjectEqAndBranch>(candidate_key, key);
if_match.Then();
if_match.Else();
// Update non-internalized string in the dictionary with internalized key?
IfBuilder if_update_with_internalized(this);
HValue* smi_check =
if_update_with_internalized.IfNot<HIsSmiAndBranch>(candidate_key);
if_update_with_internalized.And();
HValue* map = AddLoadMap(candidate_key, smi_check);
HValue* instance_type =
Add<HLoadNamedField>(map, nullptr, HObjectAccess::ForMapInstanceType());
HValue* not_internalized_bit = AddUncasted<HBitwise>(
Token::BIT_AND, instance_type,
Add<HConstant>(static_cast<int>(kIsNotInternalizedMask)));
if_update_with_internalized.If<HCompareNumericAndBranch>(
not_internalized_bit, graph()->GetConstant0(), Token::NE);
if_update_with_internalized.And();
if_update_with_internalized.IfNot<HCompareObjectEqAndBranch>(
candidate_key, graph()->GetConstantHole());
if_update_with_internalized.AndIf<HStringCompareAndBranch>(candidate_key,
key, Token::EQ);
if_update_with_internalized.Then();
// Replace a key that is a non-internalized string by the equivalent
// internalized string for faster further lookups.
Add<HStoreKeyed>(elements, key_index, key, nullptr, FAST_ELEMENTS);
if_update_with_internalized.Else();
if_update_with_internalized.JoinContinuation(&found_key_match_continuation);
if_match.JoinContinuation(&found_key_match_continuation);
IfBuilder found_key_match(this, &found_key_match_continuation);
found_key_match.Then();
// Key at current probe matches. Relevant bits in the |details| field must
// be zero, otherwise the dictionary element requires special handling.
HValue* details_index =
AddUncasted<HAdd>(base_index, Add<HConstant>(start_offset + 2));
details_index->ClearFlag(HValue::kCanOverflow);
HValue* details = Add<HLoadKeyed>(elements, details_index, nullptr, nullptr,
FAST_ELEMENTS);
int details_mask = PropertyDetails::TypeField::kMask;
details = AddUncasted<HBitwise>(Token::BIT_AND, details,
Add<HConstant>(details_mask));
IfBuilder details_compare(this);
details_compare.If<HCompareNumericAndBranch>(
details, graph()->GetConstant0(), Token::EQ);
details_compare.Then();
HValue* result_index =
AddUncasted<HAdd>(base_index, Add<HConstant>(start_offset + 1));
result_index->ClearFlag(HValue::kCanOverflow);
Push(Add<HLoadKeyed>(elements, result_index, nullptr, nullptr,
FAST_ELEMENTS));
details_compare.Else();
Add<HPushArguments>(receiver, key);
Push(Add<HCallRuntime>(Runtime::FunctionForId(Runtime::kKeyedGetProperty),
2));
details_compare.End();
found_key_match.Else();
found_key_match.JoinContinuation(&return_or_loop_continuation);
}
if_undefined.JoinContinuation(&return_or_loop_continuation);
IfBuilder return_or_loop(this, &return_or_loop_continuation);
return_or_loop.Then();
probe_loop.Break();
return_or_loop.Else();
entry = AddUncasted<HAdd>(entry, count);
entry->ClearFlag(HValue::kCanOverflow);
count = AddUncasted<HAdd>(count, graph()->GetConstant1());
count->ClearFlag(HValue::kCanOverflow);
Push(entry);
Push(count);
probe_loop.EndBody();
return_or_loop.End();
return Pop();
}
HValue* HGraphBuilder::BuildCreateIterResultObject(HValue* value,
HValue* done) {
NoObservableSideEffectsScope scope(this);
// Allocate the JSIteratorResult object.
HValue* result =
Add<HAllocate>(Add<HConstant>(JSIteratorResult::kSize), HType::JSObject(),
NOT_TENURED, JS_OBJECT_TYPE, graph()->GetConstant0());
// Initialize the JSIteratorResult object.
HValue* native_context = BuildGetNativeContext();
HValue* map = Add<HLoadNamedField>(
native_context, nullptr,
HObjectAccess::ForContextSlot(Context::ITERATOR_RESULT_MAP_INDEX));
Add<HStoreNamedField>(result, HObjectAccess::ForMap(), map);
HValue* empty_fixed_array = Add<HLoadRoot>(Heap::kEmptyFixedArrayRootIndex);
Add<HStoreNamedField>(result, HObjectAccess::ForPropertiesPointer(),
empty_fixed_array);
Add<HStoreNamedField>(result, HObjectAccess::ForElementsPointer(),
empty_fixed_array);
Add<HStoreNamedField>(result, HObjectAccess::ForObservableJSObjectOffset(
JSIteratorResult::kValueOffset),
value);
Add<HStoreNamedField>(result, HObjectAccess::ForObservableJSObjectOffset(
JSIteratorResult::kDoneOffset),
done);
STATIC_ASSERT(JSIteratorResult::kSize == 5 * kPointerSize);
return result;
}
HValue* HGraphBuilder::BuildNumberToString(HValue* object, AstType* type) {
NoObservableSideEffectsScope scope(this);
// Convert constant numbers at compile time.
if (object->IsConstant() && HConstant::cast(object)->HasNumberValue()) {
Handle<Object> number = HConstant::cast(object)->handle(isolate());
Handle<String> result = isolate()->factory()->NumberToString(number);
return Add<HConstant>(result);
}
// Create a joinable continuation.
HIfContinuation found(graph()->CreateBasicBlock(),
graph()->CreateBasicBlock());
// Load the number string cache.
HValue* number_string_cache =
Add<HLoadRoot>(Heap::kNumberStringCacheRootIndex);
// Make the hash mask from the length of the number string cache. It
// contains two elements (number and string) for each cache entry.
HValue* mask = AddLoadFixedArrayLength(number_string_cache);
mask->set_type(HType::Smi());
mask = AddUncasted<HSar>(mask, graph()->GetConstant1());
mask = AddUncasted<HSub>(mask, graph()->GetConstant1());
// Check whether object is a smi.
IfBuilder if_objectissmi(this);
if_objectissmi.If<HIsSmiAndBranch>(object);
if_objectissmi.Then();
{
// Compute hash for smi similar to smi_get_hash().
HValue* hash = AddUncasted<HBitwise>(Token::BIT_AND, object, mask);
// Load the key.
HValue* key_index = AddUncasted<HShl>(hash, graph()->GetConstant1());
HValue* key = Add<HLoadKeyed>(number_string_cache, key_index, nullptr,
nullptr, FAST_ELEMENTS, ALLOW_RETURN_HOLE);
// Check if object == key.
IfBuilder if_objectiskey(this);
if_objectiskey.If<HCompareObjectEqAndBranch>(object, key);
if_objectiskey.Then();
{
// Make the key_index available.
Push(key_index);
}
if_objectiskey.JoinContinuation(&found);
}
if_objectissmi.Else();
{
if (type->Is(AstType::SignedSmall())) {
if_objectissmi.Deopt(DeoptimizeReason::kExpectedSmi);
} else {
// Check if the object is a heap number.
IfBuilder if_objectisnumber(this);
HValue* objectisnumber = if_objectisnumber.If<HCompareMap>(
object, isolate()->factory()->heap_number_map());
if_objectisnumber.Then();
{
// Compute hash for heap number similar to double_get_hash().
HValue* low = Add<HLoadNamedField>(
object, objectisnumber,
HObjectAccess::ForHeapNumberValueLowestBits());
HValue* high = Add<HLoadNamedField>(
object, objectisnumber,
HObjectAccess::ForHeapNumberValueHighestBits());
HValue* hash = AddUncasted<HBitwise>(Token::BIT_XOR, low, high);
hash = AddUncasted<HBitwise>(Token::BIT_AND, hash, mask);
// Load the key.
HValue* key_index = AddUncasted<HShl>(hash, graph()->GetConstant1());
HValue* key =
Add<HLoadKeyed>(number_string_cache, key_index, nullptr, nullptr,
FAST_ELEMENTS, ALLOW_RETURN_HOLE);
// Check if the key is a heap number and compare it with the object.
IfBuilder if_keyisnotsmi(this);
HValue* keyisnotsmi = if_keyisnotsmi.IfNot<HIsSmiAndBranch>(key);
if_keyisnotsmi.Then();
{
IfBuilder if_keyisheapnumber(this);
if_keyisheapnumber.If<HCompareMap>(
key, isolate()->factory()->heap_number_map());
if_keyisheapnumber.Then();
{
// Check if values of key and object match.
IfBuilder if_keyeqobject(this);
if_keyeqobject.If<HCompareNumericAndBranch>(
Add<HLoadNamedField>(key, keyisnotsmi,
HObjectAccess::ForHeapNumberValue()),
Add<HLoadNamedField>(object, objectisnumber,
HObjectAccess::ForHeapNumberValue()),
Token::EQ);
if_keyeqobject.Then();
{
// Make the key_index available.
Push(key_index);
}
if_keyeqobject.JoinContinuation(&found);
}
if_keyisheapnumber.JoinContinuation(&found);
}
if_keyisnotsmi.JoinContinuation(&found);
}
if_objectisnumber.Else();
{
if (type->Is(AstType::Number())) {
if_objectisnumber.Deopt(DeoptimizeReason::kExpectedHeapNumber);
}
}
if_objectisnumber.JoinContinuation(&found);
}
}
if_objectissmi.JoinContinuation(&found);
// Check for cache hit.
IfBuilder if_found(this, &found);
if_found.Then();
{
// Count number to string operation in native code.
AddIncrementCounter(isolate()->counters()->number_to_string_native());
// Load the value in case of cache hit.
HValue* key_index = Pop();
HValue* value_index = AddUncasted<HAdd>(key_index, graph()->GetConstant1());
Push(Add<HLoadKeyed>(number_string_cache, value_index, nullptr, nullptr,
FAST_ELEMENTS, ALLOW_RETURN_HOLE));
}
if_found.Else();
{
// Cache miss, fallback to runtime.
Add<HPushArguments>(object);
Push(Add<HCallRuntime>(
Runtime::FunctionForId(Runtime::kNumberToStringSkipCache),
1));
}
if_found.End();
return Pop();
}
HValue* HGraphBuilder::BuildToNumber(HValue* input) {
if (input->type().IsTaggedNumber() ||
input->representation().IsSpecialization()) {
return input;
}
Callable callable = CodeFactory::ToNumber(isolate());
HValue* stub = Add<HConstant>(callable.code());
HValue* values[] = {input};
HCallWithDescriptor* instr = Add<HCallWithDescriptor>(
stub, 0, callable.descriptor(), ArrayVector(values));
instr->set_type(HType::TaggedNumber());
return instr;
}
HValue* HGraphBuilder::BuildToObject(HValue* receiver) {
NoObservableSideEffectsScope scope(this);
// Create a joinable continuation.
HIfContinuation wrap(graph()->CreateBasicBlock(),
graph()->CreateBasicBlock());
// Determine the proper global constructor function required to wrap
// {receiver} into a JSValue, unless {receiver} is already a {JSReceiver}, in
// which case we just return it. Deopts to Runtime::kToObject if {receiver}
// is undefined or null.
IfBuilder receiver_is_smi(this);
receiver_is_smi.If<HIsSmiAndBranch>(receiver);
receiver_is_smi.Then();
{
// Use global Number function.
Push(Add<HConstant>(Context::NUMBER_FUNCTION_INDEX));
}
receiver_is_smi.Else();
{
// Determine {receiver} map and instance type.
HValue* receiver_map =
Add<HLoadNamedField>(receiver, nullptr, HObjectAccess::ForMap());
HValue* receiver_instance_type = Add<HLoadNamedField>(
receiver_map, nullptr, HObjectAccess::ForMapInstanceType());
// First check whether {receiver} is already a spec object (fast case).
IfBuilder receiver_is_not_spec_object(this);
receiver_is_not_spec_object.If<HCompareNumericAndBranch>(
receiver_instance_type, Add<HConstant>(FIRST_JS_RECEIVER_TYPE),
Token::LT);
receiver_is_not_spec_object.Then();
{
// Load the constructor function index from the {receiver} map.
HValue* constructor_function_index = Add<HLoadNamedField>(
receiver_map, nullptr,
HObjectAccess::ForMapInObjectPropertiesOrConstructorFunctionIndex());
// Check if {receiver} has a constructor (null and undefined have no
// constructors, so we deoptimize to the runtime to throw an exception).
IfBuilder constructor_function_index_is_invalid(this);
constructor_function_index_is_invalid.If<HCompareNumericAndBranch>(
constructor_function_index,
Add<HConstant>(Map::kNoConstructorFunctionIndex), Token::EQ);
constructor_function_index_is_invalid.ThenDeopt(
DeoptimizeReason::kUndefinedOrNullInToObject);
constructor_function_index_is_invalid.End();
// Use the global constructor function.
Push(constructor_function_index);
}
receiver_is_not_spec_object.JoinContinuation(&wrap);
}
receiver_is_smi.JoinContinuation(&wrap);
// Wrap the receiver if necessary.
IfBuilder if_wrap(this, &wrap);
if_wrap.Then();
{
// Grab the constructor function index.
HValue* constructor_index = Pop();
// Load native context.
HValue* native_context = BuildGetNativeContext();
// Determine the initial map for the global constructor.
HValue* constructor = Add<HLoadKeyed>(native_context, constructor_index,
nullptr, nullptr, FAST_ELEMENTS);
HValue* constructor_initial_map = Add<HLoadNamedField>(
constructor, nullptr, HObjectAccess::ForPrototypeOrInitialMap());
// Allocate and initialize a JSValue wrapper.
HValue* value =
BuildAllocate(Add<HConstant>(JSValue::kSize), HType::JSObject(),
JS_VALUE_TYPE, HAllocationMode());
Add<HStoreNamedField>(value, HObjectAccess::ForMap(),
constructor_initial_map);
HValue* empty_fixed_array = Add<HLoadRoot>(Heap::kEmptyFixedArrayRootIndex);
Add<HStoreNamedField>(value, HObjectAccess::ForPropertiesPointer(),
empty_fixed_array);
Add<HStoreNamedField>(value, HObjectAccess::ForElementsPointer(),
empty_fixed_array);
Add<HStoreNamedField>(value, HObjectAccess::ForObservableJSObjectOffset(
JSValue::kValueOffset),
receiver);
Push(value);
}
if_wrap.Else();
{ Push(receiver); }
if_wrap.End();
return Pop();
}
HAllocate* HGraphBuilder::BuildAllocate(
HValue* object_size,
HType type,
InstanceType instance_type,
HAllocationMode allocation_mode) {
// Compute the effective allocation size.
HValue* size = object_size;
if (allocation_mode.CreateAllocationMementos()) {
size = AddUncasted<HAdd>(size, Add<HConstant>(AllocationMemento::kSize));
size->ClearFlag(HValue::kCanOverflow);
}
// Perform the actual allocation.
HAllocate* object = Add<HAllocate>(
size, type, allocation_mode.GetPretenureMode(), instance_type,
graph()->GetConstant0(), allocation_mode.feedback_site());
// Setup the allocation memento.
if (allocation_mode.CreateAllocationMementos()) {
BuildCreateAllocationMemento(
object, object_size, allocation_mode.current_site());
}
return object;
}
HValue* HGraphBuilder::BuildAddStringLengths(HValue* left_length,
HValue* right_length) {
// Compute the combined string length and check against max string length.
HValue* length = AddUncasted<HAdd>(left_length, right_length);
// Check that length <= kMaxLength <=> length < MaxLength + 1.
HValue* max_length = Add<HConstant>(String::kMaxLength + 1);
if (top_info()->IsStub() || !isolate()->IsStringLengthOverflowIntact()) {
// This is a mitigation for crbug.com/627934; the real fix
// will be to migrate the StringAddStub to TurboFan one day.
IfBuilder if_invalid(this);
if_invalid.If<HCompareNumericAndBranch>(length, max_length, Token::GT);
if_invalid.Then();
{
Add<HCallRuntime>(
Runtime::FunctionForId(Runtime::kThrowInvalidStringLength), 0);
}
if_invalid.End();
} else {
graph()->MarkDependsOnStringLengthOverflow();
Add<HBoundsCheck>(length, max_length);
}
return length;
}
HValue* HGraphBuilder::BuildCreateConsString(
HValue* length,
HValue* left,
HValue* right,
HAllocationMode allocation_mode) {
// Determine the string instance types.
HInstruction* left_instance_type = AddLoadStringInstanceType(left);
HInstruction* right_instance_type = AddLoadStringInstanceType(right);
// Allocate the cons string object. HAllocate does not care whether we
// pass CONS_STRING_TYPE or CONS_ONE_BYTE_STRING_TYPE here, so we just use
// CONS_STRING_TYPE here. Below we decide whether the cons string is
// one-byte or two-byte and set the appropriate map.
DCHECK(HAllocate::CompatibleInstanceTypes(CONS_STRING_TYPE,
CONS_ONE_BYTE_STRING_TYPE));
HAllocate* result = BuildAllocate(Add<HConstant>(ConsString::kSize),
HType::String(), CONS_STRING_TYPE,
allocation_mode);
// Compute intersection and difference of instance types.
HValue* anded_instance_types = AddUncasted<HBitwise>(
Token::BIT_AND, left_instance_type, right_instance_type);
HValue* xored_instance_types = AddUncasted<HBitwise>(
Token::BIT_XOR, left_instance_type, right_instance_type);
// We create a one-byte cons string if
// 1. both strings are one-byte, or
// 2. at least one of the strings is two-byte, but happens to contain only
// one-byte characters.
// To do this, we check
// 1. if both strings are one-byte, or if the one-byte data hint is set in
// both strings, or
// 2. if one of the strings has the one-byte data hint set and the other
// string is one-byte.
IfBuilder if_onebyte(this);
STATIC_ASSERT(kOneByteStringTag != 0);
STATIC_ASSERT(kOneByteDataHintMask != 0);
if_onebyte.If<HCompareNumericAndBranch>(
AddUncasted<HBitwise>(
Token::BIT_AND, anded_instance_types,
Add<HConstant>(static_cast<int32_t>(
kStringEncodingMask | kOneByteDataHintMask))),
graph()->GetConstant0(), Token::NE);
if_onebyte.Or();
STATIC_ASSERT(kOneByteStringTag != 0 &&
kOneByteDataHintTag != 0 &&
kOneByteDataHintTag != kOneByteStringTag);
if_onebyte.If<HCompareNumericAndBranch>(
AddUncasted<HBitwise>(
Token::BIT_AND, xored_instance_types,
Add<HConstant>(static_cast<int32_t>(
kOneByteStringTag | kOneByteDataHintTag))),
Add<HConstant>(static_cast<int32_t>(
kOneByteStringTag | kOneByteDataHintTag)), Token::EQ);
if_onebyte.Then();
{
// We can safely skip the write barrier for storing the map here.
Add<HStoreNamedField>(
result, HObjectAccess::ForMap(),
Add<HConstant>(isolate()->factory()->cons_one_byte_string_map()));
}
if_onebyte.Else();
{
// We can safely skip the write barrier for storing the map here.
Add<HStoreNamedField>(
result, HObjectAccess::ForMap(),
Add<HConstant>(isolate()->factory()->cons_string_map()));
}
if_onebyte.End();
// Initialize the cons string fields.
Add<HStoreNamedField>(result, HObjectAccess::ForStringHashField(),
Add<HConstant>(String::kEmptyHashField));
Add<HStoreNamedField>(result, HObjectAccess::ForStringLength(), length);
Add<HStoreNamedField>(result, HObjectAccess::ForConsStringFirst(), left);
Add<HStoreNamedField>(result, HObjectAccess::ForConsStringSecond(), right);
// Count the native string addition.
AddIncrementCounter(isolate()->counters()->string_add_native());
return result;
}
void HGraphBuilder::BuildCopySeqStringChars(HValue* src,
HValue* src_offset,
String::Encoding src_encoding,
HValue* dst,
HValue* dst_offset,
String::Encoding dst_encoding,
HValue* length) {
DCHECK(dst_encoding != String::ONE_BYTE_ENCODING ||
src_encoding == String::ONE_BYTE_ENCODING);
LoopBuilder loop(this, context(), LoopBuilder::kPostIncrement);
HValue* index = loop.BeginBody(graph()->GetConstant0(), length, Token::LT);
{
HValue* src_index = AddUncasted<HAdd>(src_offset, index);
HValue* value =
AddUncasted<HSeqStringGetChar>(src_encoding, src, src_index);
HValue* dst_index = AddUncasted<HAdd>(dst_offset, index);
Add<HSeqStringSetChar>(dst_encoding, dst, dst_index, value);
}
loop.EndBody();
}
HValue* HGraphBuilder::BuildObjectSizeAlignment(
HValue* unaligned_size, int header_size) {
DCHECK((header_size & kObjectAlignmentMask) == 0);
HValue* size = AddUncasted<HAdd>(
unaligned_size, Add<HConstant>(static_cast<int32_t>(
header_size + kObjectAlignmentMask)));
size->ClearFlag(HValue::kCanOverflow);
return AddUncasted<HBitwise>(
Token::BIT_AND, size, Add<HConstant>(static_cast<int32_t>(
~kObjectAlignmentMask)));
}
HValue* HGraphBuilder::BuildUncheckedStringAdd(
HValue* left,
HValue* right,
HAllocationMode allocation_mode) {
// Determine the string lengths.
HValue* left_length = AddLoadStringLength(left);
HValue* right_length = AddLoadStringLength(right);
// Compute the combined string length.
HValue* length = BuildAddStringLengths(left_length, right_length);
// Do some manual constant folding here.
if (left_length->IsConstant()) {
HConstant* c_left_length = HConstant::cast(left_length);
DCHECK_NE(0, c_left_length->Integer32Value());
if (c_left_length->Integer32Value() + 1 >= ConsString::kMinLength) {
// The right string contains at least one character.
return BuildCreateConsString(length, left, right, allocation_mode);
}
} else if (right_length->IsConstant()) {
HConstant* c_right_length = HConstant::cast(right_length);
DCHECK_NE(0, c_right_length->Integer32Value());
if (c_right_length->Integer32Value() + 1 >= ConsString::kMinLength) {
// The left string contains at least one character.
return BuildCreateConsString(length, left, right, allocation_mode);
}
}
// Check if we should create a cons string.
IfBuilder if_createcons(this);
if_createcons.If<HCompareNumericAndBranch>(
length, Add<HConstant>(ConsString::kMinLength), Token::GTE);
if_createcons.Then();
{
// Create a cons string.
Push(BuildCreateConsString(length, left, right, allocation_mode));
}
if_createcons.Else();
{
// Determine the string instance types.
HValue* left_instance_type = AddLoadStringInstanceType(left);
HValue* right_instance_type = AddLoadStringInstanceType(right);
// Compute union and difference of instance types.
HValue* ored_instance_types = AddUncasted<HBitwise>(
Token::BIT_OR, left_instance_type, right_instance_type);
HValue* xored_instance_types = AddUncasted<HBitwise>(
Token::BIT_XOR, left_instance_type, right_instance_type);
// Check if both strings have the same encoding and both are
// sequential.
IfBuilder if_sameencodingandsequential(this);
if_sameencodingandsequential.If<HCompareNumericAndBranch>(
AddUncasted<HBitwise>(
Token::BIT_AND, xored_instance_types,
Add<HConstant>(static_cast<int32_t>(kStringEncodingMask))),
graph()->GetConstant0(), Token::EQ);
if_sameencodingandsequential.And();
STATIC_ASSERT(kSeqStringTag == 0);
if_sameencodingandsequential.If<HCompareNumericAndBranch>(
AddUncasted<HBitwise>(
Token::BIT_AND, ored_instance_types,
Add<HConstant>(static_cast<int32_t>(kStringRepresentationMask))),
graph()->GetConstant0(), Token::EQ);
if_sameencodingandsequential.Then();
{
HConstant* string_map =
Add<HConstant>(isolate()->factory()->string_map());
HConstant* one_byte_string_map =
Add<HConstant>(isolate()->factory()->one_byte_string_map());
// Determine map and size depending on whether result is one-byte string.
IfBuilder if_onebyte(this);
STATIC_ASSERT(kOneByteStringTag != 0);
if_onebyte.If<HCompareNumericAndBranch>(
AddUncasted<HBitwise>(
Token::BIT_AND, ored_instance_types,
Add<HConstant>(static_cast<int32_t>(kStringEncodingMask))),
graph()->GetConstant0(), Token::NE);
if_onebyte.Then();
{
// Allocate sequential one-byte string object.
Push(length);
Push(one_byte_string_map);
}
if_onebyte.Else();
{
// Allocate sequential two-byte string object.
HValue* size = AddUncasted<HShl>(length, graph()->GetConstant1());
size->ClearFlag(HValue::kCanOverflow);
size->SetFlag(HValue::kUint32);
Push(size);
Push(string_map);
}
if_onebyte.End();
HValue* map = Pop();
// Calculate the number of bytes needed for the characters in the
// string while observing object alignment.
STATIC_ASSERT((SeqString::kHeaderSize & kObjectAlignmentMask) == 0);
HValue* size = BuildObjectSizeAlignment(Pop(), SeqString::kHeaderSize);
IfBuilder if_size(this);
if_size.If<HCompareNumericAndBranch>(
size, Add<HConstant>(kMaxRegularHeapObjectSize), Token::LT);
if_size.Then();
{
// Allocate the string object. HAllocate does not care whether we pass
// STRING_TYPE or ONE_BYTE_STRING_TYPE here, so we just use STRING_TYPE.
HAllocate* result =
BuildAllocate(size, HType::String(), STRING_TYPE, allocation_mode);
Add<HStoreNamedField>(result, HObjectAccess::ForMap(), map);
// Initialize the string fields.
Add<HStoreNamedField>(result, HObjectAccess::ForStringHashField(),
Add<HConstant>(String::kEmptyHashField));
Add<HStoreNamedField>(result, HObjectAccess::ForStringLength(), length);
// Copy characters to the result string.
IfBuilder if_twobyte(this);
if_twobyte.If<HCompareObjectEqAndBranch>(map, string_map);
if_twobyte.Then();
{
// Copy characters from the left string.
BuildCopySeqStringChars(
left, graph()->GetConstant0(), String::TWO_BYTE_ENCODING, result,
graph()->GetConstant0(), String::TWO_BYTE_ENCODING, left_length);
// Copy characters from the right string.
BuildCopySeqStringChars(
right, graph()->GetConstant0(), String::TWO_BYTE_ENCODING, result,
left_length, String::TWO_BYTE_ENCODING, right_length);
}
if_twobyte.Else();
{
// Copy characters from the left string.
BuildCopySeqStringChars(
left, graph()->GetConstant0(), String::ONE_BYTE_ENCODING, result,
graph()->GetConstant0(), String::ONE_BYTE_ENCODING, left_length);
// Copy characters from the right string.
BuildCopySeqStringChars(
right, graph()->GetConstant0(), String::ONE_BYTE_ENCODING, result,
left_length, String::ONE_BYTE_ENCODING, right_length);
}
if_twobyte.End();
// Count the native string addition.
AddIncrementCounter(isolate()->counters()->string_add_native());
// Return the sequential string.
Push(result);
}
if_size.Else();
{
// Fallback to the runtime to add the two strings. The string has to be
// allocated in LO space.
Add<HPushArguments>(left, right);
Push(Add<HCallRuntime>(Runtime::FunctionForId(Runtime::kStringAdd), 2));
}
if_size.End();
}
if_sameencodingandsequential.Else();
{
// Fallback to the runtime to add the two strings.
Add<HPushArguments>(left, right);
Push(Add<HCallRuntime>(Runtime::FunctionForId(Runtime::kStringAdd), 2));
}
if_sameencodingandsequential.End();
}
if_createcons.End();
return Pop();
}
HValue* HGraphBuilder::BuildStringAdd(
HValue* left,
HValue* right,
HAllocationMode allocation_mode) {
NoObservableSideEffectsScope no_effects(this);
// Determine string lengths.
HValue* left_length = AddLoadStringLength(left);
HValue* right_length = AddLoadStringLength(right);
// Check if left string is empty.
IfBuilder if_leftempty(this);
if_leftempty.If<HCompareNumericAndBranch>(
left_length, graph()->GetConstant0(), Token::EQ);
if_leftempty.Then();
{
// Count the native string addition.
AddIncrementCounter(isolate()->counters()->string_add_native());
// Just return the right string.
Push(right);
}
if_leftempty.Else();
{
// Check if right string is empty.
IfBuilder if_rightempty(this);
if_rightempty.If<HCompareNumericAndBranch>(
right_length, graph()->GetConstant0(), Token::EQ);
if_rightempty.Then();
{
// Count the native string addition.
AddIncrementCounter(isolate()->counters()->string_add_native());
// Just return the left string.
Push(left);
}
if_rightempty.Else();
{
// Add the two non-empty strings.
Push(BuildUncheckedStringAdd(left, right, allocation_mode));
}
if_rightempty.End();
}
if_leftempty.End();
return Pop();
}
HInstruction* HGraphBuilder::BuildUncheckedMonomorphicElementAccess(
HValue* checked_object,
HValue* key,
HValue* val,
bool is_js_array,
ElementsKind elements_kind,
PropertyAccessType access_type,
LoadKeyedHoleMode load_mode,
KeyedAccessStoreMode store_mode) {
DCHECK(top_info()->IsStub() || checked_object->IsCompareMap() ||
checked_object->IsCheckMaps());
DCHECK(!IsFixedTypedArrayElementsKind(elements_kind) || !is_js_array);
// 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 ((elements_kind == FAST_HOLEY_ELEMENTS) ||
(elements_kind == FAST_ELEMENTS && access_type == STORE)) {
checked_object->ClearDependsOnFlag(kElementsKind);
}
bool fast_smi_only_elements = IsFastSmiElementsKind(elements_kind);
bool fast_elements = IsFastObjectElementsKind(elements_kind);
HValue* elements = AddLoadElements(checked_object);
if (access_type == STORE && (fast_elements || fast_smi_only_elements) &&
store_mode != STORE_NO_TRANSITION_HANDLE_COW) {
HCheckMaps* check_cow_map = Add<HCheckMaps>(
elements, isolate()->factory()->fixed_array_map());
check_cow_map->ClearDependsOnFlag(kElementsKind);
}
HInstruction* length = NULL;
if (is_js_array) {
length = Add<HLoadNamedField>(
checked_object->ActualValue(), checked_object,
HObjectAccess::ForArrayLength(elements_kind));
} else {
length = AddLoadFixedArrayLength(elements);
}
length->set_type(HType::Smi());
HValue* checked_key = NULL;
if (IsFixedTypedArrayElementsKind(elements_kind)) {
checked_object = Add<HCheckArrayBufferNotNeutered>(checked_object);
HValue* external_pointer = Add<HLoadNamedField>(
elements, nullptr,
HObjectAccess::ForFixedTypedArrayBaseExternalPointer());
HValue* base_pointer = Add<HLoadNamedField>(
elements, nullptr, HObjectAccess::ForFixedTypedArrayBaseBasePointer());
HValue* backing_store = AddUncasted<HAdd>(external_pointer, base_pointer,
AddOfExternalAndTagged);
if (store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS) {
NoObservableSideEffectsScope no_effects(this);
IfBuilder length_checker(this);
length_checker.If<HCompareNumericAndBranch>(key, length, Token::LT);
length_checker.Then();
IfBuilder negative_checker(this);
HValue* bounds_check = negative_checker.If<HCompareNumericAndBranch>(
key, graph()->GetConstant0(), Token::GTE);
negative_checker.Then();
HInstruction* result = AddElementAccess(
backing_store, key, val, bounds_check, checked_object->ActualValue(),
elements_kind, access_type);
negative_checker.ElseDeopt(DeoptimizeReason::kNegativeKeyEncountered);
negative_checker.End();
length_checker.End();
return result;
} else {
DCHECK(store_mode == STANDARD_STORE);
checked_key = Add<HBoundsCheck>(key, length);
return AddElementAccess(backing_store, checked_key, val, checked_object,
checked_object->ActualValue(), elements_kind,
access_type);
}
}
DCHECK(fast_smi_only_elements ||
fast_elements ||
IsFastDoubleElementsKind(elements_kind));
// In case val is stored into a fast smi array, assure that the value is a smi
// before manipulating the backing store. Otherwise the actual store may
// deopt, leaving the backing store in an invalid state.
if (access_type == STORE && IsFastSmiElementsKind(elements_kind) &&
!val->type().IsSmi()) {
val = AddUncasted<HForceRepresentation>(val, Representation::Smi());
}
if (IsGrowStoreMode(store_mode)) {
NoObservableSideEffectsScope no_effects(this);
Representation representation = HStoreKeyed::RequiredValueRepresentation(
elements_kind, STORE_TO_INITIALIZED_ENTRY);
val = AddUncasted<HForceRepresentation>(val, representation);
elements = BuildCheckForCapacityGrow(checked_object, elements,
elements_kind, length, key,
is_js_array, access_type);
checked_key = key;
} else {
checked_key = Add<HBoundsCheck>(key, length);
if (access_type == STORE && (fast_elements || fast_smi_only_elements)) {
if (store_mode == STORE_NO_TRANSITION_HANDLE_COW) {
NoObservableSideEffectsScope no_effects(this);
elements = BuildCopyElementsOnWrite(checked_object, elements,
elements_kind, length);
} else {
HCheckMaps* check_cow_map = Add<HCheckMaps>(
elements, isolate()->factory()->fixed_array_map());
check_cow_map->ClearDependsOnFlag(kElementsKind);
}
}
}
return AddElementAccess(elements, checked_key, val, checked_object, nullptr,
elements_kind, access_type, load_mode);
}
HValue* HGraphBuilder::BuildCalculateElementsSize(ElementsKind kind,
HValue* capacity) {
int elements_size = IsFastDoubleElementsKind(kind)
? kDoubleSize
: kPointerSize;
HConstant* elements_size_value = Add<HConstant>(elements_size);
HInstruction* mul =
HMul::NewImul(isolate(), zone(), context(), capacity->ActualValue(),
elements_size_value);
AddInstruction(mul);
mul->ClearFlag(HValue::kCanOverflow);
STATIC_ASSERT(FixedDoubleArray::kHeaderSize == FixedArray::kHeaderSize);
HConstant* header_size = Add<HConstant>(FixedArray::kHeaderSize);
HValue* total_size = AddUncasted<HAdd>(mul, header_size);
total_size->ClearFlag(HValue::kCanOverflow);
return total_size;
}
HAllocate* HGraphBuilder::AllocateJSArrayObject(AllocationSiteMode mode) {
int base_size = JSArray::kSize;
if (mode == TRACK_ALLOCATION_SITE) {
base_size += AllocationMemento::kSize;
}
HConstant* size_in_bytes = Add<HConstant>(base_size);
return Add<HAllocate>(size_in_bytes, HType::JSArray(), NOT_TENURED,
JS_OBJECT_TYPE, graph()->GetConstant0());
}
HConstant* HGraphBuilder::EstablishElementsAllocationSize(
ElementsKind kind,
int capacity) {
int base_size = IsFastDoubleElementsKind(kind)
? FixedDoubleArray::SizeFor(capacity)
: FixedArray::SizeFor(capacity);
return Add<HConstant>(base_size);
}
HAllocate* HGraphBuilder::BuildAllocateElements(ElementsKind kind,
HValue* size_in_bytes) {
InstanceType instance_type = IsFastDoubleElementsKind(kind)
? FIXED_DOUBLE_ARRAY_TYPE
: FIXED_ARRAY_TYPE;
return Add<HAllocate>(size_in_bytes, HType::HeapObject(), NOT_TENURED,
instance_type, graph()->GetConstant0());
}
void HGraphBuilder::BuildInitializeElementsHeader(HValue* elements,
ElementsKind kind,
HValue* capacity) {
Factory* factory = isolate()->factory();
Handle<Map> map = IsFastDoubleElementsKind(kind)
? factory->fixed_double_array_map()
: factory->fixed_array_map();
Add<HStoreNamedField>(elements, HObjectAccess::ForMap(), Add<HConstant>(map));
Add<HStoreNamedField>(elements, HObjectAccess::ForFixedArrayLength(),
capacity);
}
HValue* HGraphBuilder::BuildAllocateAndInitializeArray(ElementsKind kind,
HValue* capacity) {
// The HForceRepresentation is to prevent possible deopt on int-smi
// conversion after allocation but before the new object fields are set.
capacity = AddUncasted<HForceRepresentation>(capacity, Representation::Smi());
HValue* size_in_bytes = BuildCalculateElementsSize(kind, capacity);
HValue* new_array = BuildAllocateElements(kind, size_in_bytes);
BuildInitializeElementsHeader(new_array, kind, capacity);
return new_array;
}
void HGraphBuilder::BuildJSArrayHeader(HValue* array,
HValue* array_map,
HValue* elements,
AllocationSiteMode mode,
ElementsKind elements_kind,
HValue* allocation_site_payload,
HValue* length_field) {
Add<HStoreNamedField>(array, HObjectAccess::ForMap(), array_map);
HValue* empty_fixed_array = Add<HLoadRoot>(Heap::kEmptyFixedArrayRootIndex);
Add<HStoreNamedField>(
array, HObjectAccess::ForPropertiesPointer(), empty_fixed_array);
Add<HStoreNamedField>(array, HObjectAccess::ForElementsPointer(),
elements != nullptr ? elements : empty_fixed_array);
Add<HStoreNamedField>(
array, HObjectAccess::ForArrayLength(elements_kind), length_field);
if (mode == TRACK_ALLOCATION_SITE) {
BuildCreateAllocationMemento(
array, Add<HConstant>(JSArray::kSize), allocation_site_payload);
}
}
HInstruction* HGraphBuilder::AddElementAccess(
HValue* elements, HValue* checked_key, HValue* val, HValue* dependency,
HValue* backing_store_owner, ElementsKind elements_kind,
PropertyAccessType access_type, LoadKeyedHoleMode load_mode) {
if (access_type == STORE) {
DCHECK(val != NULL);
if (elements_kind == UINT8_CLAMPED_ELEMENTS) {
val = Add<HClampToUint8>(val);
}
return Add<HStoreKeyed>(elements, checked_key, val, backing_store_owner,
elements_kind, STORE_TO_INITIALIZED_ENTRY);
}
DCHECK(access_type == LOAD);
DCHECK(val == NULL);
HLoadKeyed* load =
Add<HLoadKeyed>(elements, checked_key, dependency, backing_store_owner,
elements_kind, load_mode);
if (elements_kind == UINT32_ELEMENTS) {
graph()->RecordUint32Instruction(load);
}
return load;
}
HLoadNamedField* HGraphBuilder::AddLoadMap(HValue* object,
HValue* dependency) {
return Add<HLoadNamedField>(object, dependency, HObjectAccess::ForMap());
}
HLoadNamedField* HGraphBuilder::AddLoadElements(HValue* object,
HValue* dependency) {
return Add<HLoadNamedField>(
object, dependency, HObjectAccess::ForElementsPointer());
}
HLoadNamedField* HGraphBuilder::AddLoadFixedArrayLength(
HValue* array,
HValue* dependency) {
return Add<HLoadNamedField>(
array, dependency, HObjectAccess::ForFixedArrayLength());
}
HLoadNamedField* HGraphBuilder::AddLoadArrayLength(HValue* array,
ElementsKind kind,
HValue* dependency) {
return Add<HLoadNamedField>(
array, dependency, HObjectAccess::ForArrayLength(kind));
}
HValue* HGraphBuilder::BuildNewElementsCapacity(HValue* old_capacity) {
HValue* half_old_capacity = AddUncasted<HShr>(old_capacity,
graph_->GetConstant1());
HValue* new_capacity = AddUncasted<HAdd>(half_old_capacity, old_capacity);
new_capacity->ClearFlag(HValue::kCanOverflow);
HValue* min_growth = Add<HConstant>(16);
new_capacity = AddUncasted<HAdd>(new_capacity, min_growth);
new_capacity->ClearFlag(HValue::kCanOverflow);
return new_capacity;
}
HValue* HGraphBuilder::BuildGrowElementsCapacity(HValue* object,
HValue* elements,
ElementsKind kind,
ElementsKind new_kind,
HValue* length,
HValue* new_capacity) {
Add<HBoundsCheck>(
new_capacity,
Add<HConstant>((kMaxRegularHeapObjectSize - FixedArray::kHeaderSize) >>
ElementsKindToShiftSize(new_kind)));
HValue* new_elements =
BuildAllocateAndInitializeArray(new_kind, new_capacity);
BuildCopyElements(elements, kind, new_elements,
new_kind, length, new_capacity);
Add<HStoreNamedField>(object, HObjectAccess::ForElementsPointer(),
new_elements);
return new_elements;
}
void HGraphBuilder::BuildFillElementsWithValue(HValue* elements,
ElementsKind elements_kind,
HValue* from,
HValue* to,
HValue* value) {
if (to == NULL) {
to = AddLoadFixedArrayLength(elements);
}
// Special loop unfolding case
STATIC_ASSERT(JSArray::kPreallocatedArrayElements <=
kElementLoopUnrollThreshold);
int initial_capacity = -1;
if (from->IsInteger32Constant() && to->IsInteger32Constant()) {
int constant_from = from->GetInteger32Constant();
int constant_to = to->GetInteger32Constant();
if (constant_from == 0 && constant_to <= kElementLoopUnrollThreshold) {
initial_capacity = constant_to;
}
}
if (initial_capacity >= 0) {
for (int i = 0; i < initial_capacity; i++) {
HInstruction* key = Add<HConstant>(i);
Add<HStoreKeyed>(elements, key, value, nullptr, elements_kind);
}
} else {
// Carefully loop backwards so that the "from" remains live through the loop
// rather than the to. This often corresponds to keeping length live rather
// then capacity, which helps register allocation, since length is used more
// other than capacity after filling with holes.
LoopBuilder builder(this, context(), LoopBuilder::kPostDecrement);
HValue* key = builder.BeginBody(to, from, Token::GT);
HValue* adjusted_key = AddUncasted<HSub>(key, graph()->GetConstant1());
adjusted_key->ClearFlag(HValue::kCanOverflow);
Add<HStoreKeyed>(elements, adjusted_key, value, nullptr, elements_kind);
builder.EndBody();
}
}
void HGraphBuilder::BuildFillElementsWithHole(HValue* elements,
ElementsKind elements_kind,
HValue* from,
HValue* to) {
// Fast elements kinds need to be initialized in case statements below cause a
// garbage collection.
HValue* hole = IsFastSmiOrObjectElementsKind(elements_kind)
? graph()->GetConstantHole()
: Add<HConstant>(HConstant::kHoleNaN);
// Since we're about to store a hole value, the store instruction below must
// assume an elements kind that supports heap object values.
if (IsFastSmiOrObjectElementsKind(elements_kind)) {
elements_kind = FAST_HOLEY_ELEMENTS;
}
BuildFillElementsWithValue(elements, elements_kind, from, to, hole);
}
void HGraphBuilder::BuildCopyProperties(HValue* from_properties,
HValue* to_properties, HValue* length,
HValue* capacity) {
ElementsKind kind = FAST_ELEMENTS;
BuildFillElementsWithValue(to_properties, kind, length, capacity,
graph()->GetConstantUndefined());
LoopBuilder builder(this, context(), LoopBuilder::kPostDecrement);
HValue* key = builder.BeginBody(length, graph()->GetConstant0(), Token::GT);
key = AddUncasted<HSub>(key, graph()->GetConstant1());
key->ClearFlag(HValue::kCanOverflow);
HValue* element =
Add<HLoadKeyed>(from_properties, key, nullptr, nullptr, kind);
Add<HStoreKeyed>(to_properties, key, element, nullptr, kind);
builder.EndBody();
}
void HGraphBuilder::BuildCopyElements(HValue* from_elements,
ElementsKind from_elements_kind,
HValue* to_elements,
ElementsKind to_elements_kind,
HValue* length,
HValue* capacity) {
int constant_capacity = -1;
if (capacity != NULL &&
capacity->IsConstant() &&