| Index: src/hydrogen.cc
|
| diff --git a/src/hydrogen.cc b/src/hydrogen.cc
|
| index 7c42a3bcd63c0eb952d133a2c4e64863e5be2056..bd8b3406d44c58bacd8c7aa99f5add11132f4338 100644
|
| --- a/src/hydrogen.cc
|
| +++ b/src/hydrogen.cc
|
| @@ -605,7 +605,8 @@ void HGraph::Verify(bool do_full_verify) const {
|
| block->predecessors()->first()->last_environment()->ast_id();
|
| for (int k = 0; k < block->predecessors()->length(); k++) {
|
| HBasicBlock* predecessor = block->predecessors()->at(k);
|
| - ASSERT(predecessor->end()->IsGoto());
|
| + ASSERT(predecessor->end()->IsGoto() ||
|
| + predecessor->end()->IsDeoptimize());
|
| ASSERT(predecessor->last_environment()->ast_id() == id);
|
| }
|
| }
|
| @@ -745,19 +746,20 @@ bool HGraph::IsStandardConstant(HConstant* constant) {
|
| HGraphBuilder::IfBuilder::IfBuilder(HGraphBuilder* builder)
|
| : builder_(builder),
|
| finished_(false),
|
| - deopt_then_(false),
|
| - deopt_else_(false),
|
| did_then_(false),
|
| did_else_(false),
|
| + did_else_if_(false),
|
| did_and_(false),
|
| did_or_(false),
|
| captured_(false),
|
| needs_compare_(true),
|
| + pending_merge_block_(false),
|
| split_edge_merge_block_(NULL),
|
| - merge_block_(NULL) {
|
| + merge_at_join_blocks_(NULL),
|
| + normal_merge_at_join_block_count_(0),
|
| + deopt_merge_at_join_block_count_(0) {
|
| HEnvironment* env = builder->environment();
|
| first_true_block_ = builder->CreateBasicBlock(env->Copy());
|
| - last_true_block_ = NULL;
|
| first_false_block_ = builder->CreateBasicBlock(env->Copy());
|
| }
|
|
|
| @@ -767,19 +769,20 @@ HGraphBuilder::IfBuilder::IfBuilder(
|
| HIfContinuation* continuation)
|
| : builder_(builder),
|
| finished_(false),
|
| - deopt_then_(false),
|
| - deopt_else_(false),
|
| did_then_(false),
|
| did_else_(false),
|
| + did_else_if_(false),
|
| did_and_(false),
|
| did_or_(false),
|
| captured_(false),
|
| needs_compare_(false),
|
| + pending_merge_block_(false),
|
| first_true_block_(NULL),
|
| - last_true_block_(NULL),
|
| first_false_block_(NULL),
|
| split_edge_merge_block_(NULL),
|
| - merge_block_(NULL) {
|
| + merge_at_join_blocks_(NULL),
|
| + normal_merge_at_join_block_count_(0),
|
| + deopt_merge_at_join_block_count_(0) {
|
| continuation->Continue(&first_true_block_,
|
| &first_false_block_);
|
| }
|
| @@ -787,6 +790,20 @@ HGraphBuilder::IfBuilder::IfBuilder(
|
|
|
| HControlInstruction* HGraphBuilder::IfBuilder::AddCompare(
|
| HControlInstruction* compare) {
|
| + ASSERT(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 =
|
| @@ -842,29 +859,30 @@ void HGraphBuilder::IfBuilder::And() {
|
|
|
| void HGraphBuilder::IfBuilder::CaptureContinuation(
|
| HIfContinuation* continuation) {
|
| + ASSERT(!did_else_if_);
|
| ASSERT(!finished_);
|
| ASSERT(!captured_);
|
| - HBasicBlock* true_block = last_true_block_ == NULL
|
| - ? first_true_block_
|
| - : last_true_block_;
|
| - HBasicBlock* false_block = did_else_ && (first_false_block_ != NULL)
|
| - ? builder_->current_block()
|
| - : first_false_block_;
|
| +
|
| + HBasicBlock* true_block = NULL;
|
| + HBasicBlock* false_block = NULL;
|
| + Finish(&true_block, &false_block);
|
| + ASSERT(true_block != NULL);
|
| + ASSERT(false_block != NULL);
|
| continuation->Capture(true_block, false_block);
|
| captured_ = true;
|
| + builder_->set_current_block(NULL);
|
| End();
|
| }
|
|
|
|
|
| void HGraphBuilder::IfBuilder::JoinContinuation(HIfContinuation* continuation) {
|
| + ASSERT(!did_else_if_);
|
| ASSERT(!finished_);
|
| ASSERT(!captured_);
|
| - ASSERT(did_then_);
|
| - if (!did_else_) Else();
|
| - HBasicBlock* true_block = last_true_block_ == NULL
|
| - ? first_true_block_
|
| - : last_true_block_;
|
| - HBasicBlock* false_block = builder_->current_block();
|
| + 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()) {
|
| ASSERT(continuation->IsTrueReachable());
|
| builder_->GotoNoSimulate(true_block, continuation->true_branch());
|
| @@ -895,6 +913,7 @@ void HGraphBuilder::IfBuilder::Then() {
|
| builder_->FinishCurrentBlock(branch);
|
| }
|
| builder_->set_current_block(first_true_block_);
|
| + pending_merge_block_ = true;
|
| }
|
|
|
|
|
| @@ -902,20 +921,17 @@ void HGraphBuilder::IfBuilder::Else() {
|
| ASSERT(did_then_);
|
| ASSERT(!captured_);
|
| ASSERT(!finished_);
|
| - last_true_block_ = builder_->current_block();
|
| + AddMergeAtJoinBlock(false);
|
| builder_->set_current_block(first_false_block_);
|
| + pending_merge_block_ = true;
|
| did_else_ = true;
|
| }
|
|
|
|
|
| void HGraphBuilder::IfBuilder::Deopt(const char* reason) {
|
| ASSERT(did_then_);
|
| - if (did_else_) {
|
| - deopt_else_ = true;
|
| - } else {
|
| - deopt_then_ = true;
|
| - }
|
| builder_->Add<HDeoptimize>(reason, Deoptimizer::EAGER);
|
| + AddMergeAtJoinBlock(true);
|
| }
|
|
|
|
|
| @@ -923,51 +939,99 @@ void HGraphBuilder::IfBuilder::Return(HValue* value) {
|
| HValue* parameter_count = builder_->graph()->GetConstantMinus1();
|
| builder_->FinishExitCurrentBlock(
|
| builder_->New<HReturn>(value, parameter_count));
|
| - if (did_else_) {
|
| - first_false_block_ = NULL;
|
| - } else {
|
| - first_true_block_ = NULL;
|
| + AddMergeAtJoinBlock(false);
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::IfBuilder::AddMergeAtJoinBlock(bool deopt) {
|
| + if (!pending_merge_block_) return;
|
| + HBasicBlock* block = builder_->current_block();
|
| + ASSERT(block == NULL || !block->IsFinished());
|
| + MergeAtJoinBlock* record =
|
| + new(builder_->zone()) MergeAtJoinBlock(block, deopt,
|
| + merge_at_join_blocks_);
|
| + merge_at_join_blocks_ = record;
|
| + if (block != NULL) {
|
| + ASSERT(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() {
|
| + ASSERT(!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_;
|
| + }
|
| + ASSERT(then_record->next_ == NULL);
|
| }
|
|
|
|
|
| void HGraphBuilder::IfBuilder::End() {
|
| - if (!captured_) {
|
| - ASSERT(did_then_);
|
| - if (!did_else_) {
|
| - last_true_block_ = builder_->current_block();
|
| - }
|
| - if (last_true_block_ == NULL || last_true_block_->IsFinished()) {
|
| - ASSERT(did_else_);
|
| - // Return on true. Nothing to do, just continue the false block.
|
| - } else if (first_false_block_ == NULL ||
|
| - (did_else_ && builder_->current_block()->IsFinished())) {
|
| - // Deopt on false. Nothing to do except switching to the true block.
|
| - builder_->set_current_block(last_true_block_);
|
| - } else {
|
| - merge_block_ = builder_->graph()->CreateBasicBlock();
|
| - ASSERT(!finished_);
|
| - if (!did_else_) Else();
|
| - ASSERT(!last_true_block_->IsFinished());
|
| - HBasicBlock* last_false_block = builder_->current_block();
|
| - ASSERT(!last_false_block->IsFinished());
|
| - if (deopt_then_) {
|
| - builder_->GotoNoSimulate(last_false_block, merge_block_);
|
| - builder_->PadEnvironmentForContinuation(last_true_block_,
|
| - merge_block_);
|
| - builder_->GotoNoSimulate(last_true_block_, merge_block_);
|
| - } else {
|
| - builder_->GotoNoSimulate(last_true_block_, merge_block_);
|
| - if (deopt_else_) {
|
| - builder_->PadEnvironmentForContinuation(last_false_block,
|
| - merge_block_);
|
| - }
|
| - builder_->GotoNoSimulate(last_false_block, merge_block_);
|
| + if (captured_) return;
|
| + Finish();
|
| +
|
| + int total_merged_blocks = normal_merge_at_join_block_count_ +
|
| + deopt_merge_at_join_block_count_;
|
| + ASSERT(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_->set_current_block(merge_block_);
|
| + builder_->GotoNoSimulate(current->block_, merge_block);
|
| }
|
| + current = current->next_;
|
| }
|
| - finished_ = true;
|
| +
|
| + // Merge deopt blocks, padding when necessary.
|
| + current = merge_at_join_blocks_;
|
| + while (current != NULL) {
|
| + if (current->deopt_ && current->block_ != NULL) {
|
| + builder_->PadEnvironmentForContinuation(current->block_,
|
| + merge_block);
|
| + builder_->GotoNoSimulate(current->block_, merge_block);
|
| + }
|
| + current = current->next_;
|
| + }
|
| + builder_->set_current_block(merge_block);
|
| }
|
|
|
|
|
| @@ -1051,6 +1115,7 @@ void HGraphBuilder::LoopBuilder::Break() {
|
| }
|
|
|
| builder_->GotoNoSimulate(exit_trampoline_block_);
|
| + builder_->set_current_block(NULL);
|
| }
|
|
|
|
|
| @@ -1208,6 +1273,17 @@ HValue* HGraphBuilder::BuildCheckMap(HValue* obj, Handle<Map> map) {
|
| }
|
|
|
|
|
| +HValue* HGraphBuilder::BuildCheckString(HValue* string) {
|
| + if (!string->type().IsString()) {
|
| + ASSERT(!string->IsConstant() ||
|
| + !HConstant::cast(string)->HasStringValue());
|
| + BuildCheckHeapObject(string);
|
| + return Add<HCheckInstanceType>(string, HCheckInstanceType::IS_STRING);
|
| + }
|
| + return string;
|
| +}
|
| +
|
| +
|
| HValue* HGraphBuilder::BuildWrapReceiver(HValue* object, HValue* function) {
|
| if (object->type().IsJSObject()) return object;
|
| return Add<HWrapReceiver>(object, function);
|
| @@ -1236,7 +1312,7 @@ HValue* HGraphBuilder::BuildCheckForCapacityGrow(HValue* object,
|
| capacity_checker.Then();
|
|
|
| HValue* max_gap = Add<HConstant>(static_cast<int32_t>(JSObject::kMaxGap));
|
| - HValue* max_capacity = Add<HAdd>(current_capacity, max_gap);
|
| + HValue* max_capacity = AddUncasted<HAdd>(current_capacity, max_gap);
|
| IfBuilder key_checker(this);
|
| key_checker.If<HCompareNumericAndBranch>(key, max_capacity, Token::LT);
|
| key_checker.Then();
|
| @@ -1340,10 +1416,149 @@ void HGraphBuilder::BuildTransitionElementsKind(HValue* object,
|
| }
|
|
|
|
|
| +HValue* HGraphBuilder::BuildUncheckedDictionaryElementLoadHelper(
|
| + HValue* elements,
|
| + HValue* key,
|
| + HValue* hash,
|
| + HValue* mask,
|
| + int current_probe) {
|
| + if (current_probe == kNumberDictionaryProbes) {
|
| + return NULL;
|
| + }
|
| +
|
| + int32_t offset = SeededNumberDictionary::GetProbeOffset(current_probe);
|
| + HValue* raw_index = (current_probe == 0)
|
| + ? hash
|
| + : AddUncasted<HAdd>(hash, Add<HConstant>(offset));
|
| + raw_index = AddUncasted<HBitwise>(Token::BIT_AND, raw_index, mask);
|
| + int32_t entry_size = SeededNumberDictionary::kEntrySize;
|
| + raw_index = AddUncasted<HMul>(raw_index, Add<HConstant>(entry_size));
|
| + raw_index->ClearFlag(HValue::kCanOverflow);
|
| +
|
| + int32_t base_offset = SeededNumberDictionary::kElementsStartIndex;
|
| + HValue* key_index = AddUncasted<HAdd>(raw_index, Add<HConstant>(base_offset));
|
| + key_index->ClearFlag(HValue::kCanOverflow);
|
| +
|
| + HValue* candidate_key = Add<HLoadKeyed>(elements, key_index,
|
| + static_cast<HValue*>(NULL),
|
| + FAST_SMI_ELEMENTS);
|
| +
|
| + IfBuilder key_compare(this);
|
| + key_compare.IfNot<HCompareObjectEqAndBranch>(key, candidate_key);
|
| + key_compare.Then();
|
| + {
|
| + // Key at the current probe doesn't match, try at the next probe.
|
| + HValue* result = BuildUncheckedDictionaryElementLoadHelper(
|
| + elements, key, hash, mask, current_probe + 1);
|
| + if (result == NULL) {
|
| + key_compare.Deopt("probes exhausted in keyed load dictionary lookup");
|
| + result = graph()->GetConstantUndefined();
|
| + } else {
|
| + Push(result);
|
| + }
|
| + }
|
| + key_compare.Else();
|
| + {
|
| + // Key at current probe matches. Details must be zero, otherwise the
|
| + // dictionary element requires special handling.
|
| + HValue* details_index = AddUncasted<HAdd>(
|
| + raw_index, Add<HConstant>(base_offset + 2));
|
| + details_index->ClearFlag(HValue::kCanOverflow);
|
| +
|
| + HValue* details = Add<HLoadKeyed>(elements, details_index,
|
| + static_cast<HValue*>(NULL),
|
| + FAST_SMI_ELEMENTS);
|
| + IfBuilder details_compare(this);
|
| + details_compare.If<HCompareNumericAndBranch>(details,
|
| + graph()->GetConstant0(),
|
| + Token::NE);
|
| + details_compare.ThenDeopt("keyed load dictionary element not fast case");
|
| +
|
| + details_compare.Else();
|
| + {
|
| + // Key matches and details are zero --> fast case. Load and return the
|
| + // value.
|
| + HValue* result_index = AddUncasted<HAdd>(
|
| + raw_index, Add<HConstant>(base_offset + 1));
|
| + result_index->ClearFlag(HValue::kCanOverflow);
|
| +
|
| + Push(Add<HLoadKeyed>(elements, result_index,
|
| + static_cast<HValue*>(NULL),
|
| + FAST_ELEMENTS));
|
| + }
|
| + details_compare.End();
|
| + }
|
| + key_compare.End();
|
| +
|
| + return 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* key) {
|
| + HValue* elements = AddLoadElements(receiver);
|
| +
|
| + HValue* hash = BuildElementIndexHash(key);
|
| +
|
| + HValue* capacity = Add<HLoadKeyed>(
|
| + elements,
|
| + Add<HConstant>(NameDictionary::kCapacityIndex),
|
| + static_cast<HValue*>(NULL),
|
| + FAST_SMI_ELEMENTS);
|
| +
|
| + HValue* mask = Add<HSub>(capacity, graph()->GetConstant1());
|
| + mask->ChangeRepresentation(Representation::Integer32());
|
| + mask->ClearFlag(HValue::kCanOverflow);
|
| +
|
| + return BuildUncheckedDictionaryElementLoadHelper(elements, key,
|
| + hash, mask, 0);
|
| +}
|
| +
|
| +
|
| HValue* HGraphBuilder::BuildNumberToString(HValue* object,
|
| Handle<Type> 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());
|
| @@ -1365,10 +1580,10 @@ HValue* HGraphBuilder::BuildNumberToString(HValue* object,
|
| if_objectissmi.Then();
|
| {
|
| // Compute hash for smi similar to smi_get_hash().
|
| - HValue* hash = Add<HBitwise>(Token::BIT_AND, object, mask);
|
| + HValue* hash = AddUncasted<HBitwise>(Token::BIT_AND, object, mask);
|
|
|
| // Load the key.
|
| - HValue* key_index = Add<HShl>(hash, graph()->GetConstant1());
|
| + HValue* key_index = AddUncasted<HShl>(hash, graph()->GetConstant1());
|
| HValue* key = Add<HLoadKeyed>(number_string_cache, key_index,
|
| static_cast<HValue*>(NULL),
|
| FAST_ELEMENTS, ALLOW_RETURN_HOLE);
|
| @@ -1399,11 +1614,11 @@ HValue* HGraphBuilder::BuildNumberToString(HValue* object,
|
| object, HObjectAccess::ForHeapNumberValueLowestBits());
|
| HValue* high = Add<HLoadNamedField>(
|
| object, HObjectAccess::ForHeapNumberValueHighestBits());
|
| - HValue* hash = Add<HBitwise>(Token::BIT_XOR, low, high);
|
| - hash = Add<HBitwise>(Token::BIT_AND, hash, mask);
|
| + HValue* hash = AddUncasted<HBitwise>(Token::BIT_XOR, low, high);
|
| + hash = AddUncasted<HBitwise>(Token::BIT_AND, hash, mask);
|
|
|
| // Load the key.
|
| - HValue* key_index = Add<HShl>(hash, graph()->GetConstant1());
|
| + HValue* key_index = AddUncasted<HShl>(hash, graph()->GetConstant1());
|
| HValue* key = Add<HLoadKeyed>(number_string_cache, key_index,
|
| static_cast<HValue*>(NULL),
|
| FAST_ELEMENTS, ALLOW_RETURN_HOLE);
|
| @@ -1449,7 +1664,7 @@ HValue* HGraphBuilder::BuildNumberToString(HValue* object,
|
|
|
| // Load the value in case of cache hit.
|
| HValue* key_index = Pop();
|
| - HValue* value_index = Add<HAdd>(key_index, graph()->GetConstant1());
|
| + HValue* value_index = AddUncasted<HAdd>(key_index, graph()->GetConstant1());
|
| Push(Add<HLoadKeyed>(number_string_cache, value_index,
|
| static_cast<HValue*>(NULL),
|
| FAST_ELEMENTS, ALLOW_RETURN_HOLE));
|
| @@ -1474,14 +1689,14 @@ HValue* HGraphBuilder::BuildSeqStringSizeFor(HValue* length,
|
| STATIC_ASSERT((SeqString::kHeaderSize & kObjectAlignmentMask) == 0);
|
| HValue* size = length;
|
| if (encoding == String::TWO_BYTE_ENCODING) {
|
| - size = Add<HShl>(length, graph()->GetConstant1());
|
| + size = AddUncasted<HShl>(length, graph()->GetConstant1());
|
| size->ClearFlag(HValue::kCanOverflow);
|
| size->SetFlag(HValue::kUint32);
|
| }
|
| - size = Add<HAdd>(size, Add<HConstant>(static_cast<int32_t>(
|
| + size = AddUncasted<HAdd>(size, Add<HConstant>(static_cast<int32_t>(
|
| SeqString::kHeaderSize + kObjectAlignmentMask)));
|
| size->ClearFlag(HValue::kCanOverflow);
|
| - size = Add<HBitwise>(
|
| + size = AddUncasted<HBitwise>(
|
| Token::BIT_AND, size, Add<HConstant>(static_cast<int32_t>(
|
| ~kObjectAlignmentMask)));
|
| return size;
|
| @@ -1500,9 +1715,9 @@ void HGraphBuilder::BuildCopySeqStringChars(HValue* src,
|
| LoopBuilder loop(this, context(), LoopBuilder::kPostIncrement);
|
| HValue* index = loop.BeginBody(graph()->GetConstant0(), length, Token::LT);
|
| {
|
| - HValue* src_index = Add<HAdd>(src_offset, index);
|
| + HValue* src_index = AddUncasted<HAdd>(src_offset, index);
|
| HValue* value = Add<HSeqStringGetChar>(src_encoding, src, src_index);
|
| - HValue* dst_index = Add<HAdd>(dst_offset, index);
|
| + HValue* dst_index = AddUncasted<HAdd>(dst_offset, index);
|
| Add<HSeqStringSetChar>(dst_encoding, dst, dst_index, value);
|
| }
|
| loop.EndBody();
|
| @@ -1518,240 +1733,237 @@ HValue* HGraphBuilder::BuildUncheckedStringAdd(HValue* left,
|
| HValue* right_length = Add<HLoadNamedField>(
|
| right, HObjectAccess::ForStringLength());
|
|
|
| - // Check if we concatenated the strings here, or if we have to resort to the
|
| - // runtime function.
|
| - HIfContinuation handled(graph()->CreateBasicBlock(),
|
| - graph()->CreateBasicBlock());
|
| -
|
| - // Check if both parameters do not exceed half the max string length, because
|
| - // exceptionally long strings should be handled in the runtime. Unfortunately
|
| - // we cannot actually check whether the combined length of both strings
|
| - // exceeds String::kMaxLength (because of unclear results from the
|
| - // representation inference phase), so we use a pessimistic approach here
|
| - // instead, checking that the length of either substring does not exceed half
|
| - // of String::kMaxLength.
|
| - HConstant* max_length = Add<HConstant>(String::kMaxLength / 2);
|
| - IfBuilder if_nooverflow(this);
|
| - if_nooverflow.If<HCompareNumericAndBranch>(
|
| - left_length, max_length, Token::LTE);
|
| - if_nooverflow.AndIf<HCompareNumericAndBranch>(
|
| - right_length, max_length, Token::LTE);
|
| - if_nooverflow.Then();
|
| + // Compute the combined string length. If the result is larger than the max
|
| + // supported string length, we bailout to the runtime. This is done implicitly
|
| + // when converting the result back to a smi in case the max string length
|
| + // equals the max smi valie. Otherwise, for platforms with 32-bit smis, we do
|
| + HValue* length = AddUncasted<HAdd>(left_length, right_length);
|
| + STATIC_ASSERT(String::kMaxLength <= Smi::kMaxValue);
|
| + if (String::kMaxLength != Smi::kMaxValue) {
|
| + IfBuilder if_nooverflow(this);
|
| + if_nooverflow.If<HCompareNumericAndBranch>(
|
| + length, Add<HConstant>(String::kMaxLength), Token::LTE);
|
| + if_nooverflow.Then();
|
| + if_nooverflow.ElseDeopt("String length exceeds limit");
|
| + }
|
| +
|
| + // Determine the string instance types.
|
| + HLoadNamedField* left_instance_type = Add<HLoadNamedField>(
|
| + Add<HLoadNamedField>(left, HObjectAccess::ForMap()),
|
| + HObjectAccess::ForMapInstanceType());
|
| + HLoadNamedField* right_instance_type = Add<HLoadNamedField>(
|
| + Add<HLoadNamedField>(right, HObjectAccess::ForMap()),
|
| + HObjectAccess::ForMapInstanceType());
|
| +
|
| + // Compute difference of instance types.
|
| + HValue* xored_instance_types = AddUncasted<HBitwise>(
|
| + Token::BIT_XOR, left_instance_type, right_instance_type);
|
| +
|
| + // 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();
|
| + {
|
| + // Allocate the cons string object. HAllocate does not care whether we
|
| + // pass CONS_STRING_TYPE or CONS_ASCII_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.
|
| + HAllocate* string = Add<HAllocate>(Add<HConstant>(ConsString::kSize),
|
| + HType::String(), pretenure_flag,
|
| + CONS_STRING_TYPE);
|
| +
|
| + // Compute the intersection of instance types.
|
| + HValue* anded_instance_types = AddUncasted<HBitwise>(
|
| + Token::BIT_AND, 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.
|
| + Handle<Map> map = isolate()->factory()->cons_ascii_string_map();
|
| + AddStoreMapConstantNoWriteBarrier(string, map);
|
| + }
|
| + if_onebyte.Else();
|
| + {
|
| + // We can safely skip the write barrier for storing the map here.
|
| + Handle<Map> map = isolate()->factory()->cons_string_map();
|
| + AddStoreMapConstantNoWriteBarrier(string, map);
|
| + }
|
| + if_onebyte.End();
|
| +
|
| + // Initialize the cons string fields.
|
| + Add<HStoreNamedField>(string, HObjectAccess::ForStringHashField(),
|
| + Add<HConstant>(String::kEmptyHashField));
|
| + Add<HStoreNamedField>(string, HObjectAccess::ForStringLength(), length);
|
| + Add<HStoreNamedField>(string, HObjectAccess::ForConsStringFirst(), left);
|
| + Add<HStoreNamedField>(string, HObjectAccess::ForConsStringSecond(),
|
| + right);
|
| +
|
| + // Count the native string addition.
|
| + AddIncrementCounter(isolate()->counters()->string_add_native());
|
| +
|
| + // Cons string is result.
|
| + Push(string);
|
| + }
|
| + if_createcons.Else();
|
| {
|
| - // Determine the string instance types.
|
| - HLoadNamedField* left_instance_type = Add<HLoadNamedField>(
|
| - Add<HLoadNamedField>(left, HObjectAccess::ForMap()),
|
| - HObjectAccess::ForMapInstanceType());
|
| - HLoadNamedField* right_instance_type = Add<HLoadNamedField>(
|
| - Add<HLoadNamedField>(right, HObjectAccess::ForMap()),
|
| - HObjectAccess::ForMapInstanceType());
|
| -
|
| - // Compute difference of instance types.
|
| - HValue* xored_instance_types = Add<HBitwise>(
|
| - Token::BIT_XOR, left_instance_type, right_instance_type);
|
| -
|
| - // Compute the length of the resulting string.
|
| - HValue* length = Add<HAdd>(left_length, right_length);
|
| -
|
| - // 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();
|
| + // Compute union of instance types.
|
| + HValue* ored_instance_types = AddUncasted<HBitwise>(
|
| + Token::BIT_OR, 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();
|
| {
|
| - // Allocate the cons string object. HAllocate does not care whether we
|
| - // pass CONS_STRING_TYPE or CONS_ASCII_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.
|
| - HAllocate* string = Add<HAllocate>(Add<HConstant>(ConsString::kSize),
|
| - HType::String(), pretenure_flag,
|
| - CONS_STRING_TYPE);
|
| -
|
| - // Compute the intersection of instance types.
|
| - HValue* anded_instance_types = Add<HBitwise>(
|
| - Token::BIT_AND, 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.
|
| + // Check if the result is a one-byte string.
|
| IfBuilder if_onebyte(this);
|
| STATIC_ASSERT(kOneByteStringTag != 0);
|
| - STATIC_ASSERT(kOneByteDataHintMask != 0);
|
| if_onebyte.If<HCompareNumericAndBranch>(
|
| - Add<HBitwise>(
|
| - Token::BIT_AND, anded_instance_types,
|
| - Add<HConstant>(static_cast<int32_t>(
|
| - kStringEncodingMask | kOneByteDataHintMask))),
|
| + AddUncasted<HBitwise>(
|
| + Token::BIT_AND, ored_instance_types,
|
| + Add<HConstant>(static_cast<int32_t>(kStringEncodingMask))),
|
| graph()->GetConstant0(), Token::NE);
|
| - if_onebyte.Or();
|
| - STATIC_ASSERT(kOneByteStringTag != 0 &&
|
| - kOneByteDataHintTag != 0 &&
|
| - kOneByteDataHintTag != kOneByteStringTag);
|
| - if_onebyte.If<HCompareNumericAndBranch>(
|
| - Add<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.
|
| - Handle<Map> map = isolate()->factory()->cons_ascii_string_map();
|
| + // Calculate the number of bytes needed for the characters in the
|
| + // string while observing object alignment.
|
| + HValue* size = BuildSeqStringSizeFor(
|
| + length, String::ONE_BYTE_ENCODING);
|
| +
|
| + // Allocate the ASCII string object.
|
| + Handle<Map> map = isolate()->factory()->ascii_string_map();
|
| + HAllocate* string = Add<HAllocate>(size, HType::String(),
|
| + pretenure_flag, ASCII_STRING_TYPE);
|
| + string->set_known_initial_map(map);
|
| +
|
| + // We can safely skip the write barrier for storing map here.
|
| AddStoreMapConstantNoWriteBarrier(string, map);
|
| +
|
| + // Length must be stored into the string before we copy characters to
|
| + // make debug verification code happy.
|
| + Add<HStoreNamedField>(string, HObjectAccess::ForStringLength(),
|
| + length);
|
| +
|
| + // Copy bytes from the left string.
|
| + BuildCopySeqStringChars(
|
| + left, graph()->GetConstant0(), String::ONE_BYTE_ENCODING,
|
| + string, graph()->GetConstant0(), String::ONE_BYTE_ENCODING,
|
| + left_length);
|
| +
|
| + // Copy bytes from the right string.
|
| + BuildCopySeqStringChars(
|
| + right, graph()->GetConstant0(), String::ONE_BYTE_ENCODING,
|
| + string, left_length, String::ONE_BYTE_ENCODING,
|
| + right_length);
|
| +
|
| + // Count the native string addition.
|
| + AddIncrementCounter(isolate()->counters()->string_add_native());
|
| +
|
| + // Return the string.
|
| + Push(string);
|
| }
|
| if_onebyte.Else();
|
| {
|
| - // We can safely skip the write barrier for storing the map here.
|
| - Handle<Map> map = isolate()->factory()->cons_string_map();
|
| + // Calculate the number of bytes needed for the characters in the
|
| + // string while observing object alignment.
|
| + HValue* size = BuildSeqStringSizeFor(
|
| + length, String::TWO_BYTE_ENCODING);
|
| +
|
| + // Allocate the two-byte string object.
|
| + Handle<Map> map = isolate()->factory()->string_map();
|
| + HAllocate* string = Add<HAllocate>(size, HType::String(),
|
| + pretenure_flag, STRING_TYPE);
|
| + string->set_known_initial_map(map);
|
| +
|
| + // We can safely skip the write barrier for storing map here.
|
| AddStoreMapConstantNoWriteBarrier(string, map);
|
| +
|
| + // Length must be stored into the string before we copy characters to
|
| + // make debug verification code happy.
|
| + Add<HStoreNamedField>(string, HObjectAccess::ForStringLength(),
|
| + length);
|
| +
|
| + // Copy bytes from the left string.
|
| + BuildCopySeqStringChars(
|
| + left, graph()->GetConstant0(), String::TWO_BYTE_ENCODING,
|
| + string, graph()->GetConstant0(), String::TWO_BYTE_ENCODING,
|
| + left_length);
|
| +
|
| + // Copy bytes from the right string.
|
| + BuildCopySeqStringChars(
|
| + right, graph()->GetConstant0(), String::TWO_BYTE_ENCODING,
|
| + string, left_length, String::TWO_BYTE_ENCODING,
|
| + right_length);
|
| +
|
| + // Return the string.
|
| + Push(string);
|
| }
|
| if_onebyte.End();
|
|
|
| - // Initialize the cons string fields.
|
| + // Initialize the (common) string fields.
|
| + HValue* string = Pop();
|
| Add<HStoreNamedField>(string, HObjectAccess::ForStringHashField(),
|
| Add<HConstant>(String::kEmptyHashField));
|
| - Add<HStoreNamedField>(string, HObjectAccess::ForStringLength(), length);
|
| - Add<HStoreNamedField>(string, HObjectAccess::ForConsStringFirst(), left);
|
| - Add<HStoreNamedField>(string, HObjectAccess::ForConsStringSecond(),
|
| - right);
|
|
|
| - // Cons string is result.
|
| + // Count the native string addition.
|
| + AddIncrementCounter(isolate()->counters()->string_add_native());
|
| +
|
| Push(string);
|
| }
|
| - if_createcons.Else();
|
| + if_sameencodingandsequential.Else();
|
| {
|
| - // Compute union of instance types.
|
| - HValue* ored_instance_types = Add<HBitwise>(
|
| - Token::BIT_OR, 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>(
|
| - Add<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>(
|
| - Add<HBitwise>(
|
| - Token::BIT_AND, ored_instance_types,
|
| - Add<HConstant>(static_cast<int32_t>(kStringRepresentationMask))),
|
| - graph()->GetConstant0(), Token::EQ);
|
| - if_sameencodingandsequential.Then();
|
| - {
|
| - // Check if the result is a one-byte string.
|
| - IfBuilder if_onebyte(this);
|
| - STATIC_ASSERT(kOneByteStringTag != 0);
|
| - if_onebyte.If<HCompareNumericAndBranch>(
|
| - Add<HBitwise>(
|
| - Token::BIT_AND, ored_instance_types,
|
| - Add<HConstant>(static_cast<int32_t>(kStringEncodingMask))),
|
| - graph()->GetConstant0(), Token::NE);
|
| - if_onebyte.Then();
|
| - {
|
| - // Calculate the number of bytes needed for the characters in the
|
| - // string while observing object alignment.
|
| - HValue* size = BuildSeqStringSizeFor(
|
| - length, String::ONE_BYTE_ENCODING);
|
| -
|
| - // Allocate the ASCII string object.
|
| - Handle<Map> map = isolate()->factory()->ascii_string_map();
|
| - HAllocate* string = Add<HAllocate>(size, HType::String(),
|
| - pretenure_flag, ASCII_STRING_TYPE);
|
| - string->set_known_initial_map(map);
|
| -
|
| - // We can safely skip the write barrier for storing map here.
|
| - AddStoreMapConstantNoWriteBarrier(string, map);
|
| -
|
| - // Copy bytes from the left string.
|
| - BuildCopySeqStringChars(
|
| - left, graph()->GetConstant0(), String::ONE_BYTE_ENCODING,
|
| - string, graph()->GetConstant0(), String::ONE_BYTE_ENCODING,
|
| - left_length);
|
| -
|
| - // Copy bytes from the right string.
|
| - BuildCopySeqStringChars(
|
| - right, graph()->GetConstant0(), String::ONE_BYTE_ENCODING,
|
| - string, left_length, String::ONE_BYTE_ENCODING,
|
| - right_length);
|
| -
|
| - // Return the string.
|
| - Push(string);
|
| - }
|
| - if_onebyte.Else();
|
| - {
|
| - // Calculate the number of bytes needed for the characters in the
|
| - // string while observing object alignment.
|
| - HValue* size = BuildSeqStringSizeFor(
|
| - length, String::TWO_BYTE_ENCODING);
|
| -
|
| - // Allocate the two-byte string object.
|
| - Handle<Map> map = isolate()->factory()->string_map();
|
| - HAllocate* string = Add<HAllocate>(size, HType::String(),
|
| - pretenure_flag, STRING_TYPE);
|
| - string->set_known_initial_map(map);
|
| -
|
| - // We can safely skip the write barrier for storing map here.
|
| - AddStoreMapConstantNoWriteBarrier(string, map);
|
| -
|
| - // Copy bytes from the left string.
|
| - BuildCopySeqStringChars(
|
| - left, graph()->GetConstant0(), String::TWO_BYTE_ENCODING,
|
| - string, graph()->GetConstant0(), String::TWO_BYTE_ENCODING,
|
| - left_length);
|
| -
|
| - // Copy bytes from the right string.
|
| - BuildCopySeqStringChars(
|
| - right, graph()->GetConstant0(), String::TWO_BYTE_ENCODING,
|
| - string, left_length, String::TWO_BYTE_ENCODING,
|
| - right_length);
|
| -
|
| - // Return the string.
|
| - Push(string);
|
| - }
|
| - if_onebyte.End();
|
| -
|
| - // Initialize the (common) string fields.
|
| - HValue* string = Pop();
|
| - Add<HStoreNamedField>(string, HObjectAccess::ForStringHashField(),
|
| - Add<HConstant>(String::kEmptyHashField));
|
| - Add<HStoreNamedField>(string, HObjectAccess::ForStringLength(),
|
| - length);
|
| - Push(string);
|
| - }
|
| - if_sameencodingandsequential.JoinContinuation(&handled);
|
| + // Fallback to the runtime to add the two strings.
|
| + Add<HPushArgument>(left);
|
| + Add<HPushArgument>(right);
|
| + Push(Add<HCallRuntime>(isolate()->factory()->empty_string(),
|
| + Runtime::FunctionForId(Runtime::kStringAdd),
|
| + 2));
|
| }
|
| - if_createcons.JoinContinuation(&handled);
|
| - }
|
| - if_nooverflow.JoinContinuation(&handled);
|
| -
|
| - // Check if the strings were concatenated successfully, otherwise fallback to
|
| - // add the strings in the runtime.
|
| - IfBuilder if_handled(this, &handled);
|
| - if_handled.Then();
|
| - {
|
| - // Count the native string addition.
|
| - AddIncrementCounter(isolate()->counters()->string_add_native());
|
| - }
|
| - if_handled.Else();
|
| - {
|
| - // Fallback to the runtime to add the two strings.
|
| - Add<HPushArgument>(left);
|
| - Add<HPushArgument>(right);
|
| - Push(Add<HCallRuntime>(isolate()->factory()->empty_string(),
|
| - Runtime::FunctionForId(Runtime::kStringAdd),
|
| - 2));
|
| + if_sameencodingandsequential.End();
|
| }
|
| - if_handled.End();
|
| + if_createcons.End();
|
|
|
| return Pop();
|
| }
|
| @@ -1859,6 +2071,7 @@ HInstruction* HGraphBuilder::BuildUncheckedMonomorphicElementAccess(
|
| HInstruction* result = AddElementAccess(
|
| external_elements, key, val, bounds_check, elements_kind, is_store);
|
| negative_checker.ElseDeopt("Negative key encountered");
|
| + negative_checker.End();
|
| length_checker.End();
|
| return result;
|
| } else {
|
| @@ -1880,7 +2093,7 @@ HInstruction* HGraphBuilder::BuildUncheckedMonomorphicElementAccess(
|
| // deopt, leaving the backing store in an invalid state.
|
| if (is_store && IsFastSmiElementsKind(elements_kind) &&
|
| !val->type().IsSmi()) {
|
| - val = Add<HForceRepresentation>(val, Representation::Smi());
|
| + val = AddUncasted<HForceRepresentation>(val, Representation::Smi());
|
| }
|
|
|
| if (IsGrowStoreMode(store_mode)) {
|
| @@ -1909,6 +2122,49 @@ HInstruction* HGraphBuilder::BuildUncheckedMonomorphicElementAccess(
|
| }
|
|
|
|
|
| +
|
| +HValue* HGraphBuilder::BuildAllocateArrayFromLength(
|
| + JSArrayBuilder* array_builder,
|
| + HValue* length_argument) {
|
| + if (length_argument->IsConstant() &&
|
| + HConstant::cast(length_argument)->HasSmiValue()) {
|
| + int array_length = HConstant::cast(length_argument)->Integer32Value();
|
| + HValue* new_object = array_length == 0
|
| + ? array_builder->AllocateEmptyArray()
|
| + : array_builder->AllocateArray(length_argument, length_argument);
|
| + return new_object;
|
| + }
|
| +
|
| + HValue* constant_zero = graph()->GetConstant0();
|
| + HConstant* max_alloc_length =
|
| + Add<HConstant>(JSObject::kInitialMaxFastElementArray);
|
| + HInstruction* checked_length = Add<HBoundsCheck>(length_argument,
|
| + max_alloc_length);
|
| + IfBuilder if_builder(this);
|
| + if_builder.If<HCompareNumericAndBranch>(checked_length, constant_zero,
|
| + Token::EQ);
|
| + if_builder.Then();
|
| + const int initial_capacity = JSArray::kPreallocatedArrayElements;
|
| + HConstant* initial_capacity_node = Add<HConstant>(initial_capacity);
|
| + Push(initial_capacity_node); // capacity
|
| + Push(constant_zero); // length
|
| + if_builder.Else();
|
| + if (!(top_info()->IsStub()) &&
|
| + IsFastPackedElementsKind(array_builder->kind())) {
|
| + // We'll come back later with better (holey) feedback.
|
| + if_builder.Deopt("Holey array despite packed elements_kind feedback");
|
| + } else {
|
| + Push(checked_length); // capacity
|
| + Push(checked_length); // length
|
| + }
|
| + if_builder.End();
|
| +
|
| + // Figure out total size
|
| + HValue* length = Pop();
|
| + HValue* capacity = Pop();
|
| + return array_builder->AllocateArray(capacity, length);
|
| +}
|
| +
|
| HValue* HGraphBuilder::BuildAllocateElements(ElementsKind kind,
|
| HValue* capacity) {
|
| int elements_size;
|
| @@ -1923,11 +2179,11 @@ HValue* HGraphBuilder::BuildAllocateElements(ElementsKind kind,
|
| }
|
|
|
| HConstant* elements_size_value = Add<HConstant>(elements_size);
|
| - HValue* mul = Add<HMul>(capacity, elements_size_value);
|
| + HValue* mul = AddUncasted<HMul>(capacity, elements_size_value);
|
| mul->ClearFlag(HValue::kCanOverflow);
|
|
|
| HConstant* header_size = Add<HConstant>(FixedArray::kHeaderSize);
|
| - HValue* total_size = Add<HAdd>(mul, header_size);
|
| + HValue* total_size = AddUncasted<HAdd>(mul, header_size);
|
| total_size->ClearFlag(HValue::kCanOverflow);
|
|
|
| return Add<HAllocate>(total_size, HType::JSArray(),
|
| @@ -1954,7 +2210,7 @@ HValue* HGraphBuilder::BuildAllocateElementsAndInitializeElementsHeader(
|
| HValue* capacity) {
|
| // The HForceRepresentation is to prevent possible deopt on int-smi
|
| // conversion after allocation but before the new object fields are set.
|
| - capacity = Add<HForceRepresentation>(capacity, Representation::Smi());
|
| + capacity = AddUncasted<HForceRepresentation>(capacity, Representation::Smi());
|
| HValue* new_elements = BuildAllocateElements(kind, capacity);
|
| BuildInitializeElementsHeader(new_elements, kind, capacity);
|
| return new_elements;
|
| @@ -2097,19 +2353,15 @@ void HGraphBuilder::BuildFillElementsWithHole(HValue* elements,
|
| : Add<HConstant>(nan_double);
|
|
|
| // Special loop unfolding case
|
| - static const int kLoopUnfoldLimit = 4;
|
| - bool unfold_loop = false;
|
| - int initial_capacity = JSArray::kPreallocatedArrayElements;
|
| - if (from->IsConstant() && to->IsConstant() &&
|
| - initial_capacity <= kLoopUnfoldLimit) {
|
| - HConstant* constant_from = HConstant::cast(from);
|
| - HConstant* constant_to = HConstant::cast(to);
|
| + static const int kLoopUnfoldLimit = 8;
|
| + STATIC_ASSERT(JSArray::kPreallocatedArrayElements <= kLoopUnfoldLimit);
|
| + int initial_capacity = -1;
|
| + if (from->IsInteger32Constant() && to->IsInteger32Constant()) {
|
| + int constant_from = from->GetInteger32Constant();
|
| + int constant_to = to->GetInteger32Constant();
|
|
|
| - if (constant_from->HasInteger32Value() &&
|
| - constant_from->Integer32Value() == 0 &&
|
| - constant_to->HasInteger32Value() &&
|
| - constant_to->Integer32Value() == initial_capacity) {
|
| - unfold_loop = true;
|
| + if (constant_from == 0 && constant_to <= kLoopUnfoldLimit) {
|
| + initial_capacity = constant_to;
|
| }
|
| }
|
|
|
| @@ -2119,7 +2371,7 @@ void HGraphBuilder::BuildFillElementsWithHole(HValue* elements,
|
| elements_kind = FAST_HOLEY_ELEMENTS;
|
| }
|
|
|
| - if (unfold_loop) {
|
| + if (initial_capacity >= 0) {
|
| for (int i = 0; i < initial_capacity; i++) {
|
| HInstruction* key = Add<HConstant>(i);
|
| Add<HStoreKeyed>(elements, key, hole, elements_kind);
|
| @@ -2378,7 +2630,14 @@ HGraphBuilder::JSArrayBuilder::JSArrayBuilder(HGraphBuilder* builder,
|
|
|
|
|
| HValue* HGraphBuilder::JSArrayBuilder::EmitMapCode() {
|
| - if (kind_ == GetInitialFastElementsKind()) {
|
| + if (!builder()->top_info()->IsStub()) {
|
| + // A constant map is fine.
|
| + Handle<Map> map(builder()->isolate()->get_initial_js_array_map(kind_),
|
| + builder()->isolate());
|
| + return builder()->Add<HConstant>(map);
|
| + }
|
| +
|
| + if (constructor_function_ != NULL && kind_ == GetInitialFastElementsKind()) {
|
| // No need for a context lookup if the kind_ matches the initial
|
| // map, because we can just load the map in that case.
|
| HObjectAccess access = HObjectAccess::ForPrototypeOrInitialMap();
|
| @@ -2420,12 +2679,14 @@ HValue* HGraphBuilder::JSArrayBuilder::EstablishAllocationSize(
|
|
|
| HInstruction* elements_size_value =
|
| builder()->Add<HConstant>(elements_size());
|
| - HInstruction* mul = builder()->Add<HMul>(length_node, elements_size_value);
|
| - mul->ClearFlag(HValue::kCanOverflow);
|
| -
|
| + HInstruction* mul = HMul::NewImul(builder()->zone(), builder()->context(),
|
| + length_node, elements_size_value);
|
| + builder()->AddInstruction(mul);
|
| HInstruction* base = builder()->Add<HConstant>(base_size);
|
| - HInstruction* total_size = builder()->Add<HAdd>(base, mul);
|
| + HInstruction* total_size = HAdd::New(builder()->zone(), builder()->context(),
|
| + base, mul);
|
| total_size->ClearFlag(HValue::kCanOverflow);
|
| + builder()->AddInstruction(total_size);
|
| return total_size;
|
| }
|
|
|
| @@ -2449,30 +2710,31 @@ HValue* HGraphBuilder::JSArrayBuilder::AllocateEmptyArray() {
|
| HConstant* capacity = builder()->Add<HConstant>(initial_capacity());
|
| return AllocateArray(size_in_bytes,
|
| capacity,
|
| - builder()->graph()->GetConstant0(),
|
| - true);
|
| + builder()->graph()->GetConstant0());
|
| }
|
|
|
|
|
| HValue* HGraphBuilder::JSArrayBuilder::AllocateArray(HValue* capacity,
|
| HValue* length_field,
|
| - bool fill_with_hole) {
|
| + FillMode fill_mode) {
|
| HValue* size_in_bytes = EstablishAllocationSize(capacity);
|
| - return AllocateArray(size_in_bytes, capacity, length_field, fill_with_hole);
|
| + return AllocateArray(size_in_bytes, capacity, length_field, fill_mode);
|
| }
|
|
|
|
|
| HValue* HGraphBuilder::JSArrayBuilder::AllocateArray(HValue* size_in_bytes,
|
| HValue* capacity,
|
| HValue* length_field,
|
| - bool fill_with_hole) {
|
| + FillMode fill_mode) {
|
| // These HForceRepresentations are because we store these as fields in the
|
| // objects we construct, and an int32-to-smi HChange could deopt. Accept
|
| // the deopt possibility now, before allocation occurs.
|
| - capacity = builder()->Add<HForceRepresentation>(capacity,
|
| - Representation::Smi());
|
| - length_field = builder()->Add<HForceRepresentation>(length_field,
|
| - Representation::Smi());
|
| + capacity =
|
| + builder()->AddUncasted<HForceRepresentation>(capacity,
|
| + Representation::Smi());
|
| + length_field =
|
| + builder()->AddUncasted<HForceRepresentation>(length_field,
|
| + Representation::Smi());
|
| // Allocate (dealing with failure appropriately)
|
| HAllocate* new_object = builder()->Add<HAllocate>(size_in_bytes,
|
| HType::JSArray(), NOT_TENURED, JS_ARRAY_TYPE);
|
| @@ -2499,7 +2761,7 @@ HValue* HGraphBuilder::JSArrayBuilder::AllocateArray(HValue* size_in_bytes,
|
| // Initialize the elements
|
| builder()->BuildInitializeElementsHeader(elements_location_, kind_, capacity);
|
|
|
| - if (fill_with_hole) {
|
| + if (fill_mode == FILL_WITH_HOLE) {
|
| builder()->BuildFillElementsWithHole(elements_location_, kind_,
|
| graph()->GetConstant0(), capacity);
|
| }
|
| @@ -2659,7 +2921,7 @@ HBasicBlock* HGraph::CreateBasicBlock() {
|
|
|
| void HGraph::FinalizeUniqueness() {
|
| DisallowHeapAllocation no_gc;
|
| - ASSERT(!isolate()->optimizing_compiler_thread()->IsOptimizerThread());
|
| + ASSERT(!OptimizingCompilerThread::IsOptimizerThread(isolate()));
|
| for (int i = 0; i < blocks()->length(); ++i) {
|
| for (HInstructionIterator it(blocks()->at(i)); !it.Done(); it.Advance()) {
|
| it.Current()->FinalizeUniqueness();
|
| @@ -4249,7 +4511,7 @@ void HOptimizedGraphBuilder::VisitForInStatement(ForInStatement* stmt) {
|
| set_current_block(body_exit);
|
|
|
| HValue* current_index = Pop();
|
| - Push(Add<HAdd>(current_index, graph()->GetConstant1()));
|
| + Push(AddUncasted<HAdd>(current_index, graph()->GetConstant1()));
|
| body_exit = current_block();
|
| }
|
|
|
| @@ -5679,12 +5941,7 @@ void HOptimizedGraphBuilder::HandleCompoundAssignment(Assignment* expr) {
|
| HValue* right = Pop();
|
| HValue* left = Pop();
|
|
|
| - HInstruction* instr = BuildBinaryOperation(operation, left, right);
|
| - AddInstruction(instr);
|
| - Push(instr);
|
| - if (instr->HasObservableSideEffects()) {
|
| - Add<HSimulate>(operation->id(), REMOVABLE_SIMULATE);
|
| - }
|
| + Push(BuildBinaryOperation(operation, left, right));
|
| BuildStore(expr, prop, expr->id(),
|
| expr->AssignmentId(), expr->IsUninitialized());
|
| } else {
|
| @@ -7218,16 +7475,6 @@ bool HOptimizedGraphBuilder::TryInlineBuiltinMethodCall(
|
| return true;
|
| }
|
| break;
|
| - case kMathRandom:
|
| - if (argument_count == 1 && check_type == RECEIVER_MAP_CHECK) {
|
| - AddCheckConstantFunction(expr->holder(), receiver, receiver_map);
|
| - Drop(1); // Receiver.
|
| - HGlobalObject* global_object = Add<HGlobalObject>();
|
| - HRandom* result = New<HRandom>(global_object);
|
| - ast_context()->ReturnInstruction(result, expr->id());
|
| - return true;
|
| - }
|
| - break;
|
| case kMathMax:
|
| case kMathMin:
|
| if (argument_count == 3 && check_type == RECEIVER_MAP_CHECK) {
|
| @@ -7355,18 +7602,30 @@ void HOptimizedGraphBuilder::VisitCall(Call* expr) {
|
| if (prop != NULL) {
|
| if (!prop->key()->IsPropertyName()) {
|
| // Keyed function call.
|
| - CHECK_ALIVE(VisitArgument(prop->obj()));
|
| -
|
| + CHECK_ALIVE(VisitForValue(prop->obj()));
|
| CHECK_ALIVE(VisitForValue(prop->key()));
|
| +
|
| // Push receiver and key like the non-optimized code generator expects it.
|
| HValue* key = Pop();
|
| HValue* receiver = Pop();
|
| Push(key);
|
| - Push(receiver);
|
| -
|
| + Push(Add<HPushArgument>(receiver));
|
| CHECK_ALIVE(VisitArgumentList(expr->arguments()));
|
|
|
| - call = New<HCallKeyed>(key, argument_count);
|
| + if (expr->IsMonomorphic()) {
|
| + BuildCheckHeapObject(receiver);
|
| + ElementsKind kind = expr->KeyedArrayCallIsHoley()
|
| + ? FAST_HOLEY_ELEMENTS : FAST_ELEMENTS;
|
| +
|
| + Handle<Map> map(isolate()->get_initial_js_array_map(kind));
|
| +
|
| + HValue* function = BuildMonomorphicElementAccess(
|
| + receiver, key, NULL, NULL, map, false, STANDARD_STORE);
|
| +
|
| + call = New<HCallFunction>(function, argument_count);
|
| + } else {
|
| + call = New<HCallKeyed>(key, argument_count);
|
| + }
|
| Drop(argument_count + 1); // 1 is the key.
|
| return ast_context()->ReturnInstruction(call, expr->id());
|
| }
|
| @@ -7535,6 +7794,71 @@ void HOptimizedGraphBuilder::VisitCall(Call* expr) {
|
| }
|
|
|
|
|
| +void HOptimizedGraphBuilder::BuildInlinedCallNewArray(CallNew* expr) {
|
| + NoObservableSideEffectsScope no_effects(this);
|
| +
|
| + int argument_count = expr->arguments()->length();
|
| + // We should at least have the constructor on the expression stack.
|
| + HValue* constructor = environment()->ExpressionStackAt(argument_count);
|
| +
|
| + ElementsKind kind = expr->elements_kind();
|
| + Handle<Cell> cell = expr->allocation_info_cell();
|
| + AllocationSite* site = AllocationSite::cast(cell->value());
|
| +
|
| + // Register on the site for deoptimization if the cell value changes.
|
| + site->AddDependentCompilationInfo(AllocationSite::TRANSITIONS, top_info());
|
| + HInstruction* cell_instruction = Add<HConstant>(cell);
|
| +
|
| + // In the single constant argument case, we may have to adjust elements kind
|
| + // to avoid creating a packed non-empty array.
|
| + if (argument_count == 1 && !IsHoleyElementsKind(kind)) {
|
| + HValue* argument = environment()->Top();
|
| + if (argument->IsConstant()) {
|
| + HConstant* constant_argument = HConstant::cast(argument);
|
| + ASSERT(constant_argument->HasSmiValue());
|
| + int constant_array_size = constant_argument->Integer32Value();
|
| + if (constant_array_size != 0) {
|
| + kind = GetHoleyElementsKind(kind);
|
| + }
|
| + }
|
| + }
|
| +
|
| + // Build the array.
|
| + JSArrayBuilder array_builder(this,
|
| + kind,
|
| + cell_instruction,
|
| + constructor,
|
| + DISABLE_ALLOCATION_SITES);
|
| + HValue* new_object;
|
| + if (argument_count == 0) {
|
| + new_object = array_builder.AllocateEmptyArray();
|
| + } else if (argument_count == 1) {
|
| + HValue* argument = environment()->Top();
|
| + new_object = BuildAllocateArrayFromLength(&array_builder, argument);
|
| + } else {
|
| + HValue* length = Add<HConstant>(argument_count);
|
| + // Smi arrays need to initialize array elements with the hole because
|
| + // bailout could occur if the arguments don't fit in a smi.
|
| + //
|
| + // TODO(mvstanton): If all the arguments are constants in smi range, then
|
| + // we could set fill_with_hole to false and save a few instructions.
|
| + JSArrayBuilder::FillMode fill_mode = IsFastSmiElementsKind(kind)
|
| + ? JSArrayBuilder::FILL_WITH_HOLE
|
| + : JSArrayBuilder::DONT_FILL_WITH_HOLE;
|
| + new_object = array_builder.AllocateArray(length, length, fill_mode);
|
| + HValue* elements = array_builder.GetElementsLocation();
|
| + for (int i = 0; i < argument_count; i++) {
|
| + HValue* value = environment()->ExpressionStackAt(argument_count - i - 1);
|
| + HValue* constant_i = Add<HConstant>(i);
|
| + Add<HStoreKeyed>(elements, constant_i, value, kind);
|
| + }
|
| + }
|
| +
|
| + Drop(argument_count + 1); // drop constructor and args.
|
| + ast_context()->ReturnValue(new_object);
|
| +}
|
| +
|
| +
|
| // Checks whether allocation using the given constructor can be inlined.
|
| static bool IsAllocationInlineable(Handle<JSFunction> constructor) {
|
| return constructor->has_initial_map() &&
|
| @@ -7544,6 +7868,50 @@ static bool IsAllocationInlineable(Handle<JSFunction> constructor) {
|
| }
|
|
|
|
|
| +bool HOptimizedGraphBuilder::IsCallNewArrayInlineable(CallNew* expr) {
|
| + bool inline_ok = false;
|
| + Handle<JSFunction> caller = current_info()->closure();
|
| + Handle<JSFunction> target(isolate()->global_context()->array_function(),
|
| + isolate());
|
| + int argument_count = expr->arguments()->length();
|
| + // We should have the function plus array arguments on the environment stack.
|
| + ASSERT(environment()->length() >= (argument_count + 1));
|
| + Handle<Cell> cell = expr->allocation_info_cell();
|
| + AllocationSite* site = AllocationSite::cast(cell->value());
|
| + if (site->CanInlineCall()) {
|
| + // We also want to avoid inlining in certain 1 argument scenarios.
|
| + if (argument_count == 1) {
|
| + HValue* argument = Top();
|
| + if (argument->IsConstant()) {
|
| + // Do not inline if the constant length argument is not a smi or
|
| + // outside the valid range for a fast array.
|
| + HConstant* constant_argument = HConstant::cast(argument);
|
| + if (constant_argument->HasSmiValue()) {
|
| + int value = constant_argument->Integer32Value();
|
| + inline_ok = value >= 0 &&
|
| + value < JSObject::kInitialMaxFastElementArray;
|
| + if (!inline_ok) {
|
| + TraceInline(target, caller,
|
| + "Length outside of valid array range");
|
| + }
|
| + }
|
| + } else {
|
| + inline_ok = true;
|
| + }
|
| + } else {
|
| + inline_ok = true;
|
| + }
|
| + } else {
|
| + TraceInline(target, caller, "AllocationSite requested no inlining.");
|
| + }
|
| +
|
| + if (inline_ok) {
|
| + TraceInline(target, caller, NULL);
|
| + }
|
| + return inline_ok;
|
| +}
|
| +
|
| +
|
| void HOptimizedGraphBuilder::VisitCallNew(CallNew* expr) {
|
| ASSERT(!HasStackOverflow());
|
| ASSERT(current_block() != NULL);
|
| @@ -7552,14 +7920,15 @@ void HOptimizedGraphBuilder::VisitCallNew(CallNew* expr) {
|
| int argument_count = expr->arguments()->length() + 1; // Plus constructor.
|
| Factory* factory = isolate()->factory();
|
|
|
| + // The constructor function is on the stack in the unoptimized code
|
| + // during evaluation of the arguments.
|
| + CHECK_ALIVE(VisitForValue(expr->expression()));
|
| + HValue* function = Top();
|
| + CHECK_ALIVE(VisitExpressions(expr->arguments()));
|
| +
|
| if (FLAG_inline_construct &&
|
| expr->IsMonomorphic() &&
|
| IsAllocationInlineable(expr->target())) {
|
| - // The constructor function is on the stack in the unoptimized code
|
| - // during evaluation of the arguments.
|
| - CHECK_ALIVE(VisitForValue(expr->expression()));
|
| - HValue* function = Top();
|
| - CHECK_ALIVE(VisitExpressions(expr->arguments()));
|
| Handle<JSFunction> constructor = expr->target();
|
| HValue* check = Add<HCheckValue>(function, constructor);
|
|
|
| @@ -7646,19 +8015,24 @@ void HOptimizedGraphBuilder::VisitCallNew(CallNew* expr) {
|
| // argument to the construct call.
|
| Handle<JSFunction> array_function(
|
| isolate()->global_context()->array_function(), isolate());
|
| - CHECK_ALIVE(VisitArgument(expr->expression()));
|
| - HValue* constructor = HPushArgument::cast(Top())->argument();
|
| - CHECK_ALIVE(VisitArgumentList(expr->arguments()));
|
| + bool use_call_new_array = expr->target().is_identical_to(array_function);
|
| + Handle<Cell> cell = expr->allocation_info_cell();
|
| + if (use_call_new_array && IsCallNewArrayInlineable(expr)) {
|
| + // Verify we are still calling the array function for our native context.
|
| + Add<HCheckValue>(function, array_function);
|
| + BuildInlinedCallNewArray(expr);
|
| + return;
|
| + }
|
| +
|
| HBinaryCall* call;
|
| - if (expr->target().is_identical_to(array_function)) {
|
| - Handle<Cell> cell = expr->allocation_info_cell();
|
| - Add<HCheckValue>(constructor, array_function);
|
| - call = New<HCallNewArray>(constructor, argument_count,
|
| - cell, expr->elements_kind());
|
| + if (use_call_new_array) {
|
| + Add<HCheckValue>(function, array_function);
|
| + call = New<HCallNewArray>(function, argument_count, cell,
|
| + expr->elements_kind());
|
| } else {
|
| - call = New<HCallNew>(constructor, argument_count);
|
| + call = New<HCallNew>(function, argument_count);
|
| }
|
| - Drop(argument_count);
|
| + PreProcessCall(call);
|
| return ast_context()->ReturnInstruction(call, expr->id());
|
| }
|
| }
|
| @@ -7680,6 +8054,52 @@ const HOptimizedGraphBuilder::InlineFunctionGenerator
|
| #undef INLINE_FUNCTION_GENERATOR_ADDRESS
|
|
|
|
|
| +void HOptimizedGraphBuilder::VisitDataViewInitialize(
|
| + CallRuntime* expr) {
|
| + ZoneList<Expression*>* arguments = expr->arguments();
|
| +
|
| + NoObservableSideEffectsScope scope(this);
|
| + ASSERT(arguments->length()== 4);
|
| + CHECK_ALIVE(VisitForValue(arguments->at(0)));
|
| + HValue* obj = Pop();
|
| +
|
| + CHECK_ALIVE(VisitForValue(arguments->at(1)));
|
| + HValue* buffer = Pop();
|
| +
|
| + CHECK_ALIVE(VisitForValue(arguments->at(2)));
|
| + HValue* byte_offset = Pop();
|
| +
|
| + CHECK_ALIVE(VisitForValue(arguments->at(3)));
|
| + HValue* byte_length = Pop();
|
| +
|
| + for (int offset = JSDataView::kSize;
|
| + offset < JSDataView::kSizeWithInternalFields;
|
| + offset += kPointerSize) {
|
| + Add<HStoreNamedField>(obj,
|
| + HObjectAccess::ForJSObjectOffset(offset),
|
| + Add<HConstant>(static_cast<int32_t>(0)));
|
| + }
|
| +
|
| + Add<HStoreNamedField>(obj,
|
| + HObjectAccess::ForJSObjectOffset(JSDataView::kBufferOffset), buffer);
|
| + Add<HStoreNamedField>(obj,
|
| + HObjectAccess::ForJSObjectOffset(JSDataView::kByteOffsetOffset),
|
| + byte_offset);
|
| + Add<HStoreNamedField>(obj,
|
| + HObjectAccess::ForJSObjectOffset(JSDataView::kByteLengthOffset),
|
| + byte_length);
|
| +
|
| + Add<HStoreNamedField>(obj,
|
| + HObjectAccess::ForJSObjectOffset(JSDataView::kWeakNextOffset),
|
| + Add<HLoadNamedField>(buffer,
|
| + HObjectAccess::ForJSObjectOffset(
|
| + JSArrayBuffer::kWeakFirstViewOffset)));
|
| + Add<HStoreNamedField>(buffer,
|
| + HObjectAccess::ForJSObjectOffset(JSArrayBuffer::kWeakFirstViewOffset),
|
| + obj);
|
| +}
|
| +
|
| +
|
| void HOptimizedGraphBuilder::VisitCallRuntime(CallRuntime* expr) {
|
| ASSERT(!HasStackOverflow());
|
| ASSERT(current_block() != NULL);
|
| @@ -7690,6 +8110,11 @@ void HOptimizedGraphBuilder::VisitCallRuntime(CallRuntime* expr) {
|
|
|
| const Runtime::Function* function = expr->function();
|
| ASSERT(function != NULL);
|
| +
|
| + if (function->function_id == Runtime::kDataViewInitialize) {
|
| + return VisitDataViewInitialize(expr);
|
| + }
|
| +
|
| if (function->intrinsic_type == Runtime::INLINE) {
|
| ASSERT(expr->name()->length() > 0);
|
| ASSERT(expr->name()->Get(0) == '_');
|
| @@ -7844,7 +8269,7 @@ HInstruction* HOptimizedGraphBuilder::BuildIncrement(
|
| // actual HChange instruction we need is (sometimes) added in a later
|
| // phase, so it is not available now to be used as an input to HAdd and
|
| // as the return value.
|
| - HInstruction* number_input = Add<HForceRepresentation>(Pop(), rep);
|
| + HInstruction* number_input = AddUncasted<HForceRepresentation>(Pop(), rep);
|
| if (!rep.IsDouble()) {
|
| number_input->SetFlag(HInstruction::kFlexibleRepresentation);
|
| number_input->SetFlag(HInstruction::kCannotBeTagged);
|
| @@ -8091,10 +8516,11 @@ bool CanBeZero(HValue* right) {
|
| HValue* HGraphBuilder::EnforceNumberType(HValue* number,
|
| Handle<Type> expected) {
|
| if (expected->Is(Type::Smi())) {
|
| - return Add<HForceRepresentation>(number, Representation::Smi());
|
| + return AddUncasted<HForceRepresentation>(number, Representation::Smi());
|
| }
|
| if (expected->Is(Type::Signed32())) {
|
| - return Add<HForceRepresentation>(number, Representation::Integer32());
|
| + return AddUncasted<HForceRepresentation>(number,
|
| + Representation::Integer32());
|
| }
|
| return number;
|
| }
|
| @@ -8141,7 +8567,7 @@ HValue* HGraphBuilder::TruncateToNumber(HValue* value, Handle<Type>* expected) {
|
| }
|
|
|
|
|
| -HInstruction* HOptimizedGraphBuilder::BuildBinaryOperation(
|
| +HValue* HOptimizedGraphBuilder::BuildBinaryOperation(
|
| BinaryOperation* expr,
|
| HValue* left,
|
| HValue* right) {
|
| @@ -8150,20 +8576,29 @@ HInstruction* HOptimizedGraphBuilder::BuildBinaryOperation(
|
| Handle<Type> result_type = expr->bounds().lower;
|
| Maybe<int> fixed_right_arg = expr->fixed_right_arg();
|
|
|
| - return HGraphBuilder::BuildBinaryOperation(expr->op(), left, right,
|
| - left_type, right_type, result_type, fixed_right_arg);
|
| + HValue* result = HGraphBuilder::BuildBinaryOperation(
|
| + expr->op(), left, right, left_type, right_type,
|
| + result_type, fixed_right_arg);
|
| + // Add a simulate after instructions with observable side effects, and
|
| + // after phis, which are the result of BuildBinaryOperation when we
|
| + // inlined some complex subgraph.
|
| + if (result->HasObservableSideEffects() || result->IsPhi()) {
|
| + Push(result);
|
| + Add<HSimulate>(expr->id(), REMOVABLE_SIMULATE);
|
| + Drop(1);
|
| + }
|
| + return result;
|
| }
|
|
|
|
|
| -HInstruction* HGraphBuilder::BuildBinaryOperation(
|
| +HValue* HGraphBuilder::BuildBinaryOperation(
|
| Token::Value op,
|
| HValue* left,
|
| HValue* right,
|
| Handle<Type> left_type,
|
| Handle<Type> right_type,
|
| Handle<Type> result_type,
|
| - Maybe<int> fixed_right_arg,
|
| - bool binop_stub) {
|
| + Maybe<int> fixed_right_arg) {
|
|
|
| Representation left_rep = Representation::FromType(left_type);
|
| Representation right_rep = Representation::FromType(right_type);
|
| @@ -8195,40 +8630,44 @@ HInstruction* HGraphBuilder::BuildBinaryOperation(
|
| // Special case for string addition here.
|
| if (op == Token::ADD &&
|
| (left_type->Is(Type::String()) || right_type->Is(Type::String()))) {
|
| + // Validate type feedback for left argument.
|
| if (left_type->Is(Type::String())) {
|
| - IfBuilder if_isstring(this);
|
| - if_isstring.If<HIsStringAndBranch>(left);
|
| - if_isstring.Then();
|
| - if_isstring.ElseDeopt("Expected string for LHS of binary operation");
|
| - } else if (left_type->Is(Type::Number())) {
|
| + left = BuildCheckString(left);
|
| + }
|
| +
|
| + // Validate type feedback for right argument.
|
| + if (right_type->Is(Type::String())) {
|
| + right = BuildCheckString(right);
|
| + }
|
| +
|
| + // Convert left argument as necessary.
|
| + if (left_type->Is(Type::Number())) {
|
| + ASSERT(right_type->Is(Type::String()));
|
| left = BuildNumberToString(left, left_type);
|
| - } else {
|
| + } else if (!left_type->Is(Type::String())) {
|
| ASSERT(right_type->Is(Type::String()));
|
| HValue* function = AddLoadJSBuiltin(Builtins::STRING_ADD_RIGHT);
|
| Add<HPushArgument>(left);
|
| Add<HPushArgument>(right);
|
| - return NewUncasted<HInvokeFunction>(function, 2);
|
| + return AddUncasted<HInvokeFunction>(function, 2);
|
| }
|
|
|
| - if (right_type->Is(Type::String())) {
|
| - IfBuilder if_isstring(this);
|
| - if_isstring.If<HIsStringAndBranch>(right);
|
| - if_isstring.Then();
|
| - if_isstring.ElseDeopt("Expected string for RHS of binary operation");
|
| - } else if (right_type->Is(Type::Number())) {
|
| + // Convert right argument as necessary.
|
| + if (right_type->Is(Type::Number())) {
|
| + ASSERT(left_type->Is(Type::String()));
|
| right = BuildNumberToString(right, right_type);
|
| - } else {
|
| + } else if (!right_type->Is(Type::String())) {
|
| ASSERT(left_type->Is(Type::String()));
|
| HValue* function = AddLoadJSBuiltin(Builtins::STRING_ADD_LEFT);
|
| Add<HPushArgument>(left);
|
| Add<HPushArgument>(right);
|
| - return NewUncasted<HInvokeFunction>(function, 2);
|
| + return AddUncasted<HInvokeFunction>(function, 2);
|
| }
|
|
|
| - return NewUncasted<HStringAdd>(left, right, STRING_ADD_CHECK_NONE);
|
| + return AddUncasted<HStringAdd>(left, right, STRING_ADD_CHECK_NONE);
|
| }
|
|
|
| - if (binop_stub) {
|
| + if (graph()->info()->IsStub()) {
|
| left = EnforceNumberType(left, left_type);
|
| right = EnforceNumberType(right, right_type);
|
| }
|
| @@ -8242,55 +8681,70 @@ HInstruction* HGraphBuilder::BuildBinaryOperation(
|
| // Only the stub is allowed to call into the runtime, since otherwise we would
|
| // inline several instructions (including the two pushes) for every tagged
|
| // operation in optimized code, which is more expensive, than a stub call.
|
| - if (binop_stub && is_non_primitive) {
|
| + if (graph()->info()->IsStub() && is_non_primitive) {
|
| HValue* function = AddLoadJSBuiltin(BinaryOpIC::TokenToJSBuiltin(op));
|
| Add<HPushArgument>(left);
|
| Add<HPushArgument>(right);
|
| - instr = NewUncasted<HInvokeFunction>(function, 2);
|
| + instr = AddUncasted<HInvokeFunction>(function, 2);
|
| } else {
|
| switch (op) {
|
| case Token::ADD:
|
| - instr = NewUncasted<HAdd>(left, right);
|
| + instr = AddUncasted<HAdd>(left, right);
|
| break;
|
| case Token::SUB:
|
| - instr = NewUncasted<HSub>(left, right);
|
| + instr = AddUncasted<HSub>(left, right);
|
| break;
|
| case Token::MUL:
|
| - instr = NewUncasted<HMul>(left, right);
|
| + instr = AddUncasted<HMul>(left, right);
|
| break;
|
| - case Token::MOD:
|
| - instr = NewUncasted<HMod>(left, right, fixed_right_arg);
|
| + case Token::MOD: {
|
| + if (fixed_right_arg.has_value) {
|
| + if (right->IsConstant()) {
|
| + ASSERT_EQ(fixed_right_arg.value,
|
| + HConstant::cast(right)->Integer32Value());
|
| + } else {
|
| + HConstant* fixed_right = Add<HConstant>(
|
| + static_cast<int>(fixed_right_arg.value));
|
| + IfBuilder if_same(this);
|
| + if_same.If<HCompareNumericAndBranch>(right, fixed_right, Token::EQ);
|
| + if_same.Then();
|
| + if_same.ElseDeopt("Unexpected RHS of binary operation");
|
| + right = fixed_right;
|
| + }
|
| + }
|
| + instr = AddUncasted<HMod>(left, right);
|
| break;
|
| + }
|
| case Token::DIV:
|
| - instr = NewUncasted<HDiv>(left, right);
|
| + instr = AddUncasted<HDiv>(left, right);
|
| break;
|
| case Token::BIT_XOR:
|
| case Token::BIT_AND:
|
| - instr = NewUncasted<HBitwise>(op, left, right);
|
| + instr = AddUncasted<HBitwise>(op, left, right);
|
| break;
|
| case Token::BIT_OR: {
|
| HValue* operand, *shift_amount;
|
| if (left_type->Is(Type::Signed32()) &&
|
| right_type->Is(Type::Signed32()) &&
|
| MatchRotateRight(left, right, &operand, &shift_amount)) {
|
| - instr = NewUncasted<HRor>(operand, shift_amount);
|
| + instr = AddUncasted<HRor>(operand, shift_amount);
|
| } else {
|
| - instr = NewUncasted<HBitwise>(op, left, right);
|
| + instr = AddUncasted<HBitwise>(op, left, right);
|
| }
|
| break;
|
| }
|
| case Token::SAR:
|
| - instr = NewUncasted<HSar>(left, right);
|
| + instr = AddUncasted<HSar>(left, right);
|
| break;
|
| case Token::SHR:
|
| - instr = NewUncasted<HShr>(left, right);
|
| + instr = AddUncasted<HShr>(left, right);
|
| if (FLAG_opt_safe_uint32_operations && instr->IsShr() &&
|
| CanBeZero(right)) {
|
| graph()->RecordUint32Instruction(instr);
|
| }
|
| break;
|
| case Token::SHL:
|
| - instr = NewUncasted<HShl>(left, right);
|
| + instr = AddUncasted<HShl>(left, right);
|
| break;
|
| default:
|
| UNREACHABLE();
|
| @@ -8302,7 +8756,7 @@ HInstruction* HGraphBuilder::BuildBinaryOperation(
|
| binop->set_observed_input_representation(1, left_rep);
|
| binop->set_observed_input_representation(2, right_rep);
|
| binop->initialize_output_representation(result_rep);
|
| - if (binop_stub) {
|
| + if (graph()->info()->IsStub()) {
|
| // Stub should not call into stub.
|
| instr->SetFlag(HValue::kCannotBeTagged);
|
| // And should truncate on HForceRepresentation already.
|
| @@ -8463,11 +8917,15 @@ void HOptimizedGraphBuilder::VisitLogicalExpression(BinaryOperation* expr) {
|
| void HOptimizedGraphBuilder::VisitArithmeticExpression(BinaryOperation* expr) {
|
| CHECK_ALIVE(VisitForValue(expr->left()));
|
| CHECK_ALIVE(VisitForValue(expr->right()));
|
| - if (!FLAG_emit_opt_code_positions) SetSourcePosition(expr->position());
|
| + SetSourcePosition(expr->position());
|
| HValue* right = Pop();
|
| HValue* left = Pop();
|
| - HInstruction* instr = BuildBinaryOperation(expr, left, right);
|
| - return ast_context()->ReturnInstruction(instr, expr->id());
|
| + HValue* result = BuildBinaryOperation(expr, left, right);
|
| + if (FLAG_emit_opt_code_positions && result->IsBinaryOperation()) {
|
| + HBinaryOperation::cast(result)->SetOperandPositions(
|
| + zone(), expr->left()->position(), expr->right()->position());
|
| + }
|
| + return ast_context()->ReturnValue(result);
|
| }
|
|
|
|
|
| @@ -8475,7 +8933,7 @@ void HOptimizedGraphBuilder::HandleLiteralCompareTypeof(CompareOperation* expr,
|
| Expression* sub_expr,
|
| Handle<String> check) {
|
| CHECK_ALIVE(VisitForTypeOf(sub_expr));
|
| - if (!FLAG_emit_opt_code_positions) SetSourcePosition(expr->position());
|
| + SetSourcePosition(expr->position());
|
| HValue* value = Pop();
|
| HTypeofIsAndBranch* instr = New<HTypeofIsAndBranch>(value, check);
|
| return ast_context()->ReturnControl(instr, expr->id());
|
| @@ -8537,6 +8995,8 @@ void HOptimizedGraphBuilder::VisitCompareOperation(CompareOperation* expr) {
|
| CHECK_ALIVE(VisitForValue(expr->left()));
|
| CHECK_ALIVE(VisitForValue(expr->right()));
|
|
|
| + if (FLAG_emit_opt_code_positions) SetSourcePosition(expr->position());
|
| +
|
| HValue* right = Pop();
|
| HValue* left = Pop();
|
| Token::Value op = expr->op();
|
| @@ -8615,6 +9075,10 @@ void HOptimizedGraphBuilder::VisitCompareOperation(CompareOperation* expr) {
|
| AddCheckMap(right, map);
|
| HCompareObjectEqAndBranch* result =
|
| New<HCompareObjectEqAndBranch>(left, right);
|
| + if (FLAG_emit_opt_code_positions) {
|
| + result->set_operand_position(zone(), 0, expr->left()->position());
|
| + result->set_operand_position(zone(), 1, expr->right()->position());
|
| + }
|
| return ast_context()->ReturnControl(result, expr->id());
|
| } else {
|
| BuildCheckHeapObject(left);
|
| @@ -8656,6 +9120,11 @@ void HOptimizedGraphBuilder::VisitCompareOperation(CompareOperation* expr) {
|
| HCompareNumericAndBranch* result =
|
| New<HCompareNumericAndBranch>(left, right, op);
|
| result->set_observed_input_representation(left_rep, right_rep);
|
| + if (FLAG_emit_opt_code_positions) {
|
| + result->SetOperandPositions(zone(),
|
| + expr->left()->position(),
|
| + expr->right()->position());
|
| + }
|
| return ast_context()->ReturnControl(result, expr->id());
|
| }
|
| }
|
| @@ -9264,9 +9733,10 @@ void HOptimizedGraphBuilder::GenerateOneByteSeqStringSetChar(
|
| HValue* value = Pop();
|
| HValue* index = Pop();
|
| HValue* string = Pop();
|
| - HSeqStringSetChar* result = New<HSeqStringSetChar>(
|
| - String::ONE_BYTE_ENCODING, string, index, value);
|
| - return ast_context()->ReturnInstruction(result, call->id());
|
| + Add<HSeqStringSetChar>(String::ONE_BYTE_ENCODING, string,
|
| + index, value);
|
| + Add<HSimulate>(call->id(), FIXED_SIMULATE);
|
| + return ast_context()->ReturnValue(graph()->GetConstantUndefined());
|
| }
|
|
|
|
|
| @@ -9279,9 +9749,10 @@ void HOptimizedGraphBuilder::GenerateTwoByteSeqStringSetChar(
|
| HValue* value = Pop();
|
| HValue* index = Pop();
|
| HValue* string = Pop();
|
| - HSeqStringSetChar* result = New<HSeqStringSetChar>(
|
| - String::TWO_BYTE_ENCODING, string, index, value);
|
| - return ast_context()->ReturnInstruction(result, call->id());
|
| + Add<HSeqStringSetChar>(String::TWO_BYTE_ENCODING, string,
|
| + index, value);
|
| + Add<HSimulate>(call->id(), FIXED_SIMULATE);
|
| + return ast_context()->ReturnValue(graph()->GetConstantUndefined());
|
| }
|
|
|
|
|
| @@ -9375,14 +9846,6 @@ void HOptimizedGraphBuilder::GenerateLog(CallRuntime* call) {
|
| }
|
|
|
|
|
| -// Fast support for Math.random().
|
| -void HOptimizedGraphBuilder::GenerateRandomHeapNumber(CallRuntime* call) {
|
| - HGlobalObject* global_object = Add<HGlobalObject>();
|
| - HRandom* result = New<HRandom>(global_object);
|
| - return ast_context()->ReturnInstruction(result, call->id());
|
| -}
|
| -
|
| -
|
| // Fast support for StringAdd.
|
| void HOptimizedGraphBuilder::GenerateStringAdd(CallRuntime* call) {
|
| ASSERT_EQ(2, call->arguments()->length());
|
| @@ -9880,7 +10343,7 @@ void HTracer::TraceCompilation(CompilationInfo* info) {
|
|
|
|
|
| void HTracer::TraceLithium(const char* name, LChunk* chunk) {
|
| - ASSERT(!FLAG_concurrent_recompilation);
|
| + ASSERT(!chunk->isolate()->concurrent_recompilation_enabled());
|
| AllowHandleDereference allow_deref;
|
| AllowDeferredHandleDereference allow_deferred_deref;
|
| Trace(name, chunk->graph(), chunk);
|
| @@ -9888,7 +10351,7 @@ void HTracer::TraceLithium(const char* name, LChunk* chunk) {
|
|
|
|
|
| void HTracer::TraceHydrogen(const char* name, HGraph* graph) {
|
| - ASSERT(!FLAG_concurrent_recompilation);
|
| + ASSERT(!graph->isolate()->concurrent_recompilation_enabled());
|
| AllowHandleDereference allow_deref;
|
| AllowDeferredHandleDereference allow_deferred_deref;
|
| Trace(name, graph, NULL);
|
| @@ -9972,7 +10435,8 @@ void HTracer::Trace(const char* name, HGraph* graph, LChunk* chunk) {
|
| Tag HIR_tag(this, "HIR");
|
| for (HInstructionIterator it(current); !it.Done(); it.Advance()) {
|
| HInstruction* instruction = it.Current();
|
| - int bci = 0;
|
| + int bci = FLAG_emit_opt_code_positions && instruction->has_position() ?
|
| + instruction->position() : 0;
|
| int uses = instruction->UseCount();
|
| PrintIndent();
|
| trace_.Add("%d %d ", bci, uses);
|
| @@ -9997,6 +10461,9 @@ void HTracer::Trace(const char* name, HGraph* graph, LChunk* chunk) {
|
| trace_.Add("%d ",
|
| LifetimePosition::FromInstructionIndex(i).Value());
|
| linstr->PrintTo(&trace_);
|
| + trace_.Add(" [hir:");
|
| + linstr->hydrogen_value()->PrintNameTo(&trace_);
|
| + trace_.Add("]");
|
| trace_.Add(" <|@\n");
|
| }
|
| }
|
|
|