|
@ -25,9 +25,11 @@ |
|
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
|
|
|
|
#include "v8.h" |
|
|
|
|
|
#include "hydrogen.h" |
|
|
#include "hydrogen.h" |
|
|
|
|
|
|
|
|
|
|
|
#include <algorithm> |
|
|
|
|
|
|
|
|
|
|
|
#include "v8.h" |
|
|
#include "codegen.h" |
|
|
#include "codegen.h" |
|
|
#include "full-codegen.h" |
|
|
#include "full-codegen.h" |
|
|
#include "hashmap.h" |
|
|
#include "hashmap.h" |
|
@ -509,9 +511,8 @@ class ReachabilityAnalyzer BASE_EMBEDDED { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void HGraph::Verify(bool do_full_verify) const { |
|
|
void HGraph::Verify(bool do_full_verify) const { |
|
|
// Allow dereferencing for debug mode verification.
|
|
|
Heap::RelocationLock(isolate()->heap()); |
|
|
HandleDereferenceGuard allow_handle_deref(isolate(), |
|
|
ALLOW_HANDLE_DEREF(isolate(), "debug mode verification"); |
|
|
HandleDereferenceGuard::ALLOW); |
|
|
|
|
|
for (int i = 0; i < blocks_.length(); i++) { |
|
|
for (int i = 0; i < blocks_.length(); i++) { |
|
|
HBasicBlock* block = blocks_.at(i); |
|
|
HBasicBlock* block = blocks_.at(i); |
|
|
|
|
|
|
|
@ -603,6 +604,19 @@ HConstant* HGraph::GetConstantInt32(SetOncePointer<HConstant>* pointer, |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
HConstant* HGraph::GetConstantSmi(SetOncePointer<HConstant>* pointer, |
|
|
|
|
|
int32_t value) { |
|
|
|
|
|
if (!pointer->is_set()) { |
|
|
|
|
|
HConstant* constant = |
|
|
|
|
|
new(zone()) HConstant(Handle<Object>(Smi::FromInt(value), isolate()), |
|
|
|
|
|
Representation::Tagged()); |
|
|
|
|
|
constant->InsertAfter(GetConstantUndefined()); |
|
|
|
|
|
pointer->set(constant); |
|
|
|
|
|
} |
|
|
|
|
|
return pointer->get(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
HConstant* HGraph::GetConstant0() { |
|
|
HConstant* HGraph::GetConstant0() { |
|
|
return GetConstantInt32(&constant_0_, 0); |
|
|
return GetConstantInt32(&constant_0_, 0); |
|
|
} |
|
|
} |
|
@ -638,69 +652,20 @@ HConstant* HGraph::GetConstant##Name() { \ |
|
|
DEFINE_GET_CONSTANT(True, true, HType::Boolean(), true) |
|
|
DEFINE_GET_CONSTANT(True, true, HType::Boolean(), true) |
|
|
DEFINE_GET_CONSTANT(False, false, HType::Boolean(), false) |
|
|
DEFINE_GET_CONSTANT(False, false, HType::Boolean(), false) |
|
|
DEFINE_GET_CONSTANT(Hole, the_hole, HType::Tagged(), false) |
|
|
DEFINE_GET_CONSTANT(Hole, the_hole, HType::Tagged(), false) |
|
|
|
|
|
DEFINE_GET_CONSTANT(Null, null, HType::Tagged(), false) |
|
|
#undef DEFINE_GET_CONSTANT |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
HGraphBuilder::CheckBuilder::CheckBuilder(HGraphBuilder* builder) |
|
|
HConstant* HGraph::GetConstantSmi0() { |
|
|
: builder_(builder), |
|
|
return GetConstantSmi(&constant_smi_0_, 0); |
|
|
finished_(false) { |
|
|
|
|
|
HEnvironment* env = builder->environment(); |
|
|
|
|
|
failure_block_ = builder->CreateBasicBlock(env->Copy()); |
|
|
|
|
|
merge_block_ = builder->CreateBasicBlock(env->Copy()); |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
HValue* HGraphBuilder::CheckBuilder::CheckNotUndefined(HValue* value) { |
|
|
HConstant* HGraph::GetConstantSmi1() { |
|
|
HEnvironment* env = builder_->environment(); |
|
|
return GetConstantSmi(&constant_smi_1_, 1); |
|
|
HCompareObjectEqAndBranch* compare = |
|
|
|
|
|
new(zone()) HCompareObjectEqAndBranch( |
|
|
|
|
|
value, |
|
|
|
|
|
builder_->graph()->GetConstantUndefined()); |
|
|
|
|
|
HBasicBlock* success_block = builder_->CreateBasicBlock(env->Copy()); |
|
|
|
|
|
HBasicBlock* failure_block = builder_->CreateBasicBlock(env->Copy()); |
|
|
|
|
|
compare->SetSuccessorAt(0, failure_block); |
|
|
|
|
|
compare->SetSuccessorAt(1, success_block); |
|
|
|
|
|
failure_block->GotoNoSimulate(failure_block_); |
|
|
|
|
|
builder_->current_block()->Finish(compare); |
|
|
|
|
|
builder_->set_current_block(success_block); |
|
|
|
|
|
return compare; |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
HValue* HGraphBuilder::CheckBuilder::CheckIntegerCompare(HValue* left, |
|
|
#undef DEFINE_GET_CONSTANT |
|
|
HValue* right, |
|
|
|
|
|
Token::Value op) { |
|
|
|
|
|
HEnvironment* env = builder_->environment(); |
|
|
|
|
|
HCompareIDAndBranch* compare = |
|
|
|
|
|
new(zone()) HCompareIDAndBranch(left, right, op); |
|
|
|
|
|
compare->AssumeRepresentation(Representation::Integer32()); |
|
|
|
|
|
HBasicBlock* success_block = builder_->CreateBasicBlock(env->Copy()); |
|
|
|
|
|
HBasicBlock* failure_block = builder_->CreateBasicBlock(env->Copy()); |
|
|
|
|
|
compare->SetSuccessorAt(0, success_block); |
|
|
|
|
|
compare->SetSuccessorAt(1, failure_block); |
|
|
|
|
|
failure_block->GotoNoSimulate(failure_block_); |
|
|
|
|
|
builder_->current_block()->Finish(compare); |
|
|
|
|
|
builder_->set_current_block(success_block); |
|
|
|
|
|
return compare; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
HValue* HGraphBuilder::CheckBuilder::CheckIntegerEq(HValue* left, |
|
|
|
|
|
HValue* right) { |
|
|
|
|
|
return CheckIntegerCompare(left, right, Token::EQ); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void HGraphBuilder::CheckBuilder::End() { |
|
|
|
|
|
ASSERT(!finished_); |
|
|
|
|
|
builder_->current_block()->GotoNoSimulate(merge_block_); |
|
|
|
|
|
if (failure_block_->HasPredecessor()) { |
|
|
|
|
|
failure_block_->FinishExitWithDeoptimization(HDeoptimize::kUseAll); |
|
|
|
|
|
} |
|
|
|
|
|
builder_->set_current_block(merge_block_); |
|
|
|
|
|
finished_ = true; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
HConstant* HGraph::GetInvalidContext() { |
|
|
HConstant* HGraph::GetInvalidContext() { |
|
@ -714,8 +679,6 @@ HGraphBuilder::IfBuilder::IfBuilder(HGraphBuilder* builder, int position) |
|
|
finished_(false), |
|
|
finished_(false), |
|
|
did_then_(false), |
|
|
did_then_(false), |
|
|
did_else_(false), |
|
|
did_else_(false), |
|
|
deopt_then_(false), |
|
|
|
|
|
deopt_else_(false), |
|
|
|
|
|
did_and_(false), |
|
|
did_and_(false), |
|
|
did_or_(false), |
|
|
did_or_(false), |
|
|
captured_(false), |
|
|
captured_(false), |
|
@ -736,8 +699,6 @@ HGraphBuilder::IfBuilder::IfBuilder( |
|
|
finished_(false), |
|
|
finished_(false), |
|
|
did_then_(false), |
|
|
did_then_(false), |
|
|
did_else_(false), |
|
|
did_else_(false), |
|
|
deopt_then_(false), |
|
|
|
|
|
deopt_else_(false), |
|
|
|
|
|
did_and_(false), |
|
|
did_and_(false), |
|
|
did_or_(false), |
|
|
did_or_(false), |
|
|
captured_(false), |
|
|
captured_(false), |
|
@ -835,8 +796,9 @@ void HGraphBuilder::IfBuilder::CaptureContinuation( |
|
|
HBasicBlock* true_block = last_true_block_ == NULL |
|
|
HBasicBlock* true_block = last_true_block_ == NULL |
|
|
? first_true_block_ |
|
|
? first_true_block_ |
|
|
: last_true_block_; |
|
|
: last_true_block_; |
|
|
HBasicBlock* false_block = |
|
|
HBasicBlock* false_block = did_else_ && (first_false_block_ != NULL) |
|
|
did_else_ ? builder_->current_block() : first_false_block_; |
|
|
? builder_->current_block() |
|
|
|
|
|
: first_false_block_; |
|
|
continuation->Capture(true_block, false_block, position_); |
|
|
continuation->Capture(true_block, false_block, position_); |
|
|
captured_ = true; |
|
|
captured_ = true; |
|
|
End(); |
|
|
End(); |
|
@ -869,12 +831,23 @@ void HGraphBuilder::IfBuilder::Else() { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void HGraphBuilder::IfBuilder::Deopt() { |
|
|
void HGraphBuilder::IfBuilder::Deopt() { |
|
|
ASSERT(!(did_then_ ^ did_else_)); |
|
|
|
|
|
HBasicBlock* block = builder_->current_block(); |
|
|
HBasicBlock* block = builder_->current_block(); |
|
|
block->FinishExitWithDeoptimization(HDeoptimize::kUseAll); |
|
|
block->FinishExitWithDeoptimization(HDeoptimize::kUseAll); |
|
|
if (did_else_) { |
|
|
if (did_else_) { |
|
|
first_false_block_ = NULL; |
|
|
first_false_block_ = NULL; |
|
|
did_else_ = false; |
|
|
} else { |
|
|
|
|
|
first_true_block_ = NULL; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void HGraphBuilder::IfBuilder::Return(HValue* value) { |
|
|
|
|
|
HBasicBlock* block = builder_->current_block(); |
|
|
|
|
|
block->Finish(new(zone()) HReturn(value, |
|
|
|
|
|
builder_->environment()->LookupContext(), |
|
|
|
|
|
builder_->graph()->GetConstantMinus1())); |
|
|
|
|
|
if (did_else_) { |
|
|
|
|
|
first_false_block_ = NULL; |
|
|
} else { |
|
|
} else { |
|
|
first_true_block_ = NULL; |
|
|
first_true_block_ = NULL; |
|
|
} |
|
|
} |
|
@ -888,8 +861,9 @@ void HGraphBuilder::IfBuilder::End() { |
|
|
last_true_block_ = builder_->current_block(); |
|
|
last_true_block_ = builder_->current_block(); |
|
|
} |
|
|
} |
|
|
if (first_true_block_ == NULL) { |
|
|
if (first_true_block_ == NULL) { |
|
|
// Deopt on true. Nothing to do, just continue the else block.
|
|
|
// Deopt on true. Nothing to do, just continue the false block.
|
|
|
} else if (first_false_block_ == NULL) { |
|
|
} else if (first_false_block_ == NULL) { |
|
|
|
|
|
// Deopt on false. Nothing to do except switching to the true block.
|
|
|
builder_->set_current_block(last_true_block_); |
|
|
builder_->set_current_block(last_true_block_); |
|
|
} else { |
|
|
} else { |
|
|
HEnvironment* merge_env = last_true_block_->last_environment()->Copy(); |
|
|
HEnvironment* merge_env = last_true_block_->last_environment()->Copy(); |
|
@ -1081,7 +1055,7 @@ HValue* HGraphBuilder::BuildCheckNonSmi(HValue* obj) { |
|
|
|
|
|
|
|
|
HValue* HGraphBuilder::BuildCheckMap(HValue* obj, |
|
|
HValue* HGraphBuilder::BuildCheckMap(HValue* obj, |
|
|
Handle<Map> map) { |
|
|
Handle<Map> map) { |
|
|
HCheckMaps* check = new(zone()) HCheckMaps(obj, map, zone()); |
|
|
HCheckMaps* check = HCheckMaps::New(obj, map, zone()); |
|
|
AddInstruction(check); |
|
|
AddInstruction(check); |
|
|
return check; |
|
|
return check; |
|
|
} |
|
|
} |
|
@ -1297,7 +1271,7 @@ HInstruction* HGraphBuilder::BuildUncheckedMonomorphicElementAccess( |
|
|
AddInstruction(new(zone) HLoadElements(object, mapcheck)); |
|
|
AddInstruction(new(zone) HLoadElements(object, mapcheck)); |
|
|
if (is_store && (fast_elements || fast_smi_only_elements) && |
|
|
if (is_store && (fast_elements || fast_smi_only_elements) && |
|
|
store_mode != STORE_NO_TRANSITION_HANDLE_COW) { |
|
|
store_mode != STORE_NO_TRANSITION_HANDLE_COW) { |
|
|
HCheckMaps* check_cow_map = new(zone) HCheckMaps( |
|
|
HCheckMaps* check_cow_map = HCheckMaps::New( |
|
|
elements, isolate()->factory()->fixed_array_map(), zone); |
|
|
elements, isolate()->factory()->fixed_array_map(), zone); |
|
|
check_cow_map->ClearGVNFlag(kDependsOnElementsKind); |
|
|
check_cow_map->ClearGVNFlag(kDependsOnElementsKind); |
|
|
AddInstruction(check_cow_map); |
|
|
AddInstruction(check_cow_map); |
|
@ -1319,14 +1293,15 @@ HInstruction* HGraphBuilder::BuildUncheckedMonomorphicElementAccess( |
|
|
IfBuilder length_checker(this); |
|
|
IfBuilder length_checker(this); |
|
|
length_checker.IfCompare(key, length, Token::LT); |
|
|
length_checker.IfCompare(key, length, Token::LT); |
|
|
length_checker.Then(); |
|
|
length_checker.Then(); |
|
|
CheckBuilder negative_checker(this); |
|
|
IfBuilder negative_checker(this); |
|
|
HValue* bounds_check = negative_checker.CheckIntegerCompare( |
|
|
HValue* bounds_check = negative_checker.IfCompare( |
|
|
key, graph()->GetConstant0(), Token::GTE); |
|
|
key, graph()->GetConstant0(), Token::GTE); |
|
|
negative_checker.End(); |
|
|
negative_checker.Then(); |
|
|
HInstruction* result = BuildExternalArrayElementAccess( |
|
|
HInstruction* result = BuildExternalArrayElementAccess( |
|
|
external_elements, key, val, bounds_check, |
|
|
external_elements, key, val, bounds_check, |
|
|
elements_kind, is_store); |
|
|
elements_kind, is_store); |
|
|
AddInstruction(result); |
|
|
AddInstruction(result); |
|
|
|
|
|
negative_checker.ElseDeopt(); |
|
|
length_checker.End(); |
|
|
length_checker.End(); |
|
|
return result; |
|
|
return result; |
|
|
} else { |
|
|
} else { |
|
@ -1371,7 +1346,7 @@ HInstruction* HGraphBuilder::BuildUncheckedMonomorphicElementAccess( |
|
|
elements = BuildCopyElementsOnWrite(object, elements, elements_kind, |
|
|
elements = BuildCopyElementsOnWrite(object, elements, elements_kind, |
|
|
length); |
|
|
length); |
|
|
} else { |
|
|
} else { |
|
|
HCheckMaps* check_cow_map = new(zone) HCheckMaps( |
|
|
HCheckMaps* check_cow_map = HCheckMaps::New( |
|
|
elements, isolate()->factory()->fixed_array_map(), zone); |
|
|
elements, isolate()->factory()->fixed_array_map(), zone); |
|
|
check_cow_map->ClearGVNFlag(kDependsOnElementsKind); |
|
|
check_cow_map->ClearGVNFlag(kDependsOnElementsKind); |
|
|
AddInstruction(check_cow_map); |
|
|
AddInstruction(check_cow_map); |
|
@ -1407,8 +1382,10 @@ HValue* HGraphBuilder::BuildAllocateElements(HValue* context, |
|
|
total_size->ChangeRepresentation(Representation::Integer32()); |
|
|
total_size->ChangeRepresentation(Representation::Integer32()); |
|
|
total_size->ClearFlag(HValue::kCanOverflow); |
|
|
total_size->ClearFlag(HValue::kCanOverflow); |
|
|
|
|
|
|
|
|
HAllocate::Flags flags = HAllocate::CAN_ALLOCATE_IN_NEW_SPACE; |
|
|
HAllocate::Flags flags = HAllocate::DefaultFlags(kind); |
|
|
if (FLAG_pretenure_literals) { |
|
|
if (FLAG_pretenure_literals) { |
|
|
|
|
|
// TODO(hpayer): When pretenuring can be internalized, flags can become
|
|
|
|
|
|
// private to HAllocate.
|
|
|
if (IsFastDoubleElementsKind(kind)) { |
|
|
if (IsFastDoubleElementsKind(kind)) { |
|
|
flags = static_cast<HAllocate::Flags>( |
|
|
flags = static_cast<HAllocate::Flags>( |
|
|
flags | HAllocate::CAN_ALLOCATE_IN_OLD_DATA_SPACE); |
|
|
flags | HAllocate::CAN_ALLOCATE_IN_OLD_DATA_SPACE); |
|
@ -1417,10 +1394,6 @@ HValue* HGraphBuilder::BuildAllocateElements(HValue* context, |
|
|
flags | HAllocate::CAN_ALLOCATE_IN_OLD_POINTER_SPACE); |
|
|
flags | HAllocate::CAN_ALLOCATE_IN_OLD_POINTER_SPACE); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
if (IsFastDoubleElementsKind(kind)) { |
|
|
|
|
|
flags = static_cast<HAllocate::Flags>( |
|
|
|
|
|
flags | HAllocate::ALLOCATE_DOUBLE_ALIGNED); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
HValue* elements = |
|
|
HValue* elements = |
|
|
AddInstruction(new(zone) HAllocate(context, total_size, |
|
|
AddInstruction(new(zone) HAllocate(context, total_size, |
|
@ -1456,6 +1429,63 @@ HValue* HGraphBuilder::BuildAllocateAndInitializeElements(HValue* context, |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
HInnerAllocatedObject* HGraphBuilder::BuildJSArrayHeader(HValue* array, |
|
|
|
|
|
HValue* array_map, |
|
|
|
|
|
AllocationSiteMode mode, |
|
|
|
|
|
HValue* allocation_site_payload, |
|
|
|
|
|
HValue* length_field) { |
|
|
|
|
|
|
|
|
|
|
|
BuildStoreMap(array, array_map); |
|
|
|
|
|
|
|
|
|
|
|
HConstant* empty_fixed_array = |
|
|
|
|
|
new(zone()) HConstant( |
|
|
|
|
|
Handle<FixedArray>(isolate()->heap()->empty_fixed_array()), |
|
|
|
|
|
Representation::Tagged()); |
|
|
|
|
|
AddInstruction(empty_fixed_array); |
|
|
|
|
|
|
|
|
|
|
|
AddInstruction(new(zone()) HStoreNamedField(array, |
|
|
|
|
|
isolate()->factory()->properties_field_symbol(), |
|
|
|
|
|
empty_fixed_array, |
|
|
|
|
|
true, |
|
|
|
|
|
JSArray::kPropertiesOffset)); |
|
|
|
|
|
|
|
|
|
|
|
HInstruction* length_store = AddInstruction( |
|
|
|
|
|
new(zone()) HStoreNamedField(array, |
|
|
|
|
|
isolate()->factory()->length_field_string(), |
|
|
|
|
|
length_field, |
|
|
|
|
|
true, |
|
|
|
|
|
JSArray::kLengthOffset)); |
|
|
|
|
|
length_store->SetGVNFlag(kChangesArrayLengths); |
|
|
|
|
|
|
|
|
|
|
|
if (mode == TRACK_ALLOCATION_SITE) { |
|
|
|
|
|
BuildCreateAllocationSiteInfo(array, |
|
|
|
|
|
JSArray::kSize, |
|
|
|
|
|
allocation_site_payload); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
int elements_location = JSArray::kSize; |
|
|
|
|
|
if (mode == TRACK_ALLOCATION_SITE) { |
|
|
|
|
|
elements_location += AllocationSiteInfo::kSize; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
HInnerAllocatedObject* elements = new(zone()) HInnerAllocatedObject( |
|
|
|
|
|
array, |
|
|
|
|
|
elements_location); |
|
|
|
|
|
AddInstruction(elements); |
|
|
|
|
|
|
|
|
|
|
|
HInstruction* elements_store = AddInstruction( |
|
|
|
|
|
new(zone()) HStoreNamedField( |
|
|
|
|
|
array, |
|
|
|
|
|
isolate()->factory()->elements_field_string(), |
|
|
|
|
|
elements, |
|
|
|
|
|
true, |
|
|
|
|
|
JSArray::kElementsOffset)); |
|
|
|
|
|
elements_store->SetGVNFlag(kChangesElementsPointer); |
|
|
|
|
|
|
|
|
|
|
|
return elements; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
HInstruction* HGraphBuilder::BuildStoreMap(HValue* object, |
|
|
HInstruction* HGraphBuilder::BuildStoreMap(HValue* object, |
|
|
HValue* map) { |
|
|
HValue* map) { |
|
|
Zone* zone = this->zone(); |
|
|
Zone* zone = this->zone(); |
|
@ -1569,6 +1599,30 @@ void HGraphBuilder::BuildFillElementsWithHole(HValue* context, |
|
|
: AddInstruction(new(zone) HConstant(nan_double, |
|
|
: AddInstruction(new(zone) HConstant(nan_double, |
|
|
Representation::Double())); |
|
|
Representation::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); |
|
|
|
|
|
|
|
|
|
|
|
if (constant_from->HasInteger32Value() && |
|
|
|
|
|
constant_from->Integer32Value() == 0 && |
|
|
|
|
|
constant_to->HasInteger32Value() && |
|
|
|
|
|
constant_to->Integer32Value() == initial_capacity) { |
|
|
|
|
|
unfold_loop = true; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (unfold_loop) { |
|
|
|
|
|
for (int i = 0; i < initial_capacity; i++) { |
|
|
|
|
|
HInstruction* key = AddInstruction(new(zone) |
|
|
|
|
|
HConstant(i, Representation::Integer32())); |
|
|
|
|
|
AddInstruction(new(zone) HStoreKeyed(elements, key, hole, elements_kind)); |
|
|
|
|
|
} |
|
|
|
|
|
} else { |
|
|
LoopBuilder builder(this, context, LoopBuilder::kPostIncrement); |
|
|
LoopBuilder builder(this, context, LoopBuilder::kPostIncrement); |
|
|
|
|
|
|
|
|
HValue* key = builder.BeginBody(from, to, Token::LT); |
|
|
HValue* key = builder.BeginBody(from, to, Token::LT); |
|
@ -1577,6 +1631,7 @@ void HGraphBuilder::BuildFillElementsWithHole(HValue* context, |
|
|
|
|
|
|
|
|
builder.EndBody(); |
|
|
builder.EndBody(); |
|
|
} |
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void HGraphBuilder::BuildCopyElements(HValue* context, |
|
|
void HGraphBuilder::BuildCopyElements(HValue* context, |
|
@ -1642,12 +1697,7 @@ HValue* HGraphBuilder::BuildCloneShallowArray(HContext* context, |
|
|
: FixedArray::SizeFor(length); |
|
|
: FixedArray::SizeFor(length); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
HAllocate::Flags allocate_flags = HAllocate::CAN_ALLOCATE_IN_NEW_SPACE; |
|
|
HAllocate::Flags allocate_flags = HAllocate::DefaultFlags(kind); |
|
|
if (IsFastDoubleElementsKind(kind)) { |
|
|
|
|
|
allocate_flags = static_cast<HAllocate::Flags>( |
|
|
|
|
|
allocate_flags | HAllocate::ALLOCATE_DOUBLE_ALIGNED); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Allocate both the JS array and the elements array in one big
|
|
|
// Allocate both the JS array and the elements array in one big
|
|
|
// allocation. This avoids multiple limit checks.
|
|
|
// allocation. This avoids multiple limit checks.
|
|
|
HValue* size_in_bytes = |
|
|
HValue* size_in_bytes = |
|
@ -1676,15 +1726,7 @@ HValue* HGraphBuilder::BuildCloneShallowArray(HContext* context, |
|
|
|
|
|
|
|
|
// Create an allocation site info if requested.
|
|
|
// Create an allocation site info if requested.
|
|
|
if (mode == TRACK_ALLOCATION_SITE) { |
|
|
if (mode == TRACK_ALLOCATION_SITE) { |
|
|
HValue* alloc_site = |
|
|
BuildCreateAllocationSiteInfo(object, JSArray::kSize, boilerplate); |
|
|
AddInstruction(new(zone) HInnerAllocatedObject(object, JSArray::kSize)); |
|
|
|
|
|
Handle<Map> alloc_site_map(isolate()->heap()->allocation_site_info_map()); |
|
|
|
|
|
BuildStoreMap(alloc_site, alloc_site_map); |
|
|
|
|
|
int alloc_payload_offset = AllocationSiteInfo::kPayloadOffset; |
|
|
|
|
|
AddInstruction(new(zone) HStoreNamedField(alloc_site, |
|
|
|
|
|
factory->empty_string(), |
|
|
|
|
|
boilerplate, |
|
|
|
|
|
true, alloc_payload_offset)); |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (length > 0) { |
|
|
if (length > 0) { |
|
@ -1733,6 +1775,205 @@ HValue* HGraphBuilder::BuildCloneShallowArray(HContext* context, |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void HGraphBuilder::BuildCompareNil( |
|
|
|
|
|
HValue* value, |
|
|
|
|
|
EqualityKind kind, |
|
|
|
|
|
CompareNilICStub::Types types, |
|
|
|
|
|
Handle<Map> map, |
|
|
|
|
|
int position, |
|
|
|
|
|
HIfContinuation* continuation) { |
|
|
|
|
|
IfBuilder if_nil(this, position); |
|
|
|
|
|
bool needs_or = false; |
|
|
|
|
|
if ((types & CompareNilICStub::kCompareAgainstNull) != 0) { |
|
|
|
|
|
if (needs_or) if_nil.Or(); |
|
|
|
|
|
if_nil.If<HCompareObjectEqAndBranch>(value, graph()->GetConstantNull()); |
|
|
|
|
|
needs_or = true; |
|
|
|
|
|
} |
|
|
|
|
|
if ((types & CompareNilICStub::kCompareAgainstUndefined) != 0) { |
|
|
|
|
|
if (needs_or) if_nil.Or(); |
|
|
|
|
|
if_nil.If<HCompareObjectEqAndBranch>(value, |
|
|
|
|
|
graph()->GetConstantUndefined()); |
|
|
|
|
|
needs_or = true; |
|
|
|
|
|
} |
|
|
|
|
|
// Handle either undetectable or monomorphic, not both.
|
|
|
|
|
|
ASSERT(((types & CompareNilICStub::kCompareAgainstUndetectable) == 0) || |
|
|
|
|
|
((types & CompareNilICStub::kCompareAgainstMonomorphicMap) == 0)); |
|
|
|
|
|
if ((types & CompareNilICStub::kCompareAgainstUndetectable) != 0) { |
|
|
|
|
|
if (needs_or) if_nil.Or(); |
|
|
|
|
|
if_nil.If<HIsUndetectableAndBranch>(value); |
|
|
|
|
|
} else { |
|
|
|
|
|
if_nil.Then(); |
|
|
|
|
|
if_nil.Else(); |
|
|
|
|
|
if ((types & CompareNilICStub::kCompareAgainstMonomorphicMap) != 0) { |
|
|
|
|
|
BuildCheckNonSmi(value); |
|
|
|
|
|
// For ICs, the map checked below is a sentinel map that gets replaced by
|
|
|
|
|
|
// the monomorphic map when the code is used as a template to generate a
|
|
|
|
|
|
// new IC. For optimized functions, there is no sentinel map, the map
|
|
|
|
|
|
// emitted below is the actual monomorphic map.
|
|
|
|
|
|
BuildCheckMap(value, map); |
|
|
|
|
|
} else { |
|
|
|
|
|
if (kind == kNonStrictEquality) { |
|
|
|
|
|
if_nil.Deopt(); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if_nil.CaptureContinuation(continuation); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
HValue* HGraphBuilder::BuildCreateAllocationSiteInfo(HValue* previous_object, |
|
|
|
|
|
int previous_object_size, |
|
|
|
|
|
HValue* payload) { |
|
|
|
|
|
HInnerAllocatedObject* alloc_site = new(zone()) |
|
|
|
|
|
HInnerAllocatedObject(previous_object, previous_object_size); |
|
|
|
|
|
AddInstruction(alloc_site); |
|
|
|
|
|
Handle<Map> alloc_site_map(isolate()->heap()->allocation_site_info_map()); |
|
|
|
|
|
BuildStoreMap(alloc_site, alloc_site_map); |
|
|
|
|
|
AddInstruction(new(zone()) HStoreNamedField(alloc_site, |
|
|
|
|
|
isolate()->factory()->payload_string(), |
|
|
|
|
|
payload, |
|
|
|
|
|
true, |
|
|
|
|
|
AllocationSiteInfo::kPayloadOffset)); |
|
|
|
|
|
return alloc_site; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
HGraphBuilder::JSArrayBuilder::JSArrayBuilder(HGraphBuilder* builder, |
|
|
|
|
|
ElementsKind kind, |
|
|
|
|
|
HValue* allocation_site_payload, |
|
|
|
|
|
AllocationSiteMode mode) : |
|
|
|
|
|
builder_(builder), |
|
|
|
|
|
kind_(kind), |
|
|
|
|
|
allocation_site_payload_(allocation_site_payload) { |
|
|
|
|
|
if (mode == DONT_TRACK_ALLOCATION_SITE) { |
|
|
|
|
|
mode_ = mode; |
|
|
|
|
|
} else { |
|
|
|
|
|
mode_ = AllocationSiteInfo::GetMode(kind); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
HValue* HGraphBuilder::JSArrayBuilder::EmitMapCode(HValue* context) { |
|
|
|
|
|
// Get the global context, the native context, the map array
|
|
|
|
|
|
HInstruction* global_object = AddInstruction(new(zone()) |
|
|
|
|
|
HGlobalObject(context)); |
|
|
|
|
|
HInstruction* native_context = AddInstruction(new(zone()) |
|
|
|
|
|
HLoadNamedField(global_object, true, GlobalObject::kNativeContextOffset)); |
|
|
|
|
|
int offset = Context::kHeaderSize + |
|
|
|
|
|
kPointerSize * Context::JS_ARRAY_MAPS_INDEX; |
|
|
|
|
|
HInstruction* map_array = AddInstruction(new(zone()) |
|
|
|
|
|
HLoadNamedField(native_context, true, offset)); |
|
|
|
|
|
offset = kind_ * kPointerSize + FixedArrayBase::kHeaderSize; |
|
|
|
|
|
return AddInstruction(new(zone()) HLoadNamedField(map_array, true, offset)); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
HValue* HGraphBuilder::JSArrayBuilder::EstablishAllocationSize( |
|
|
|
|
|
HValue* length_node) { |
|
|
|
|
|
HValue* context = builder()->environment()->LookupContext(); |
|
|
|
|
|
ASSERT(length_node != NULL); |
|
|
|
|
|
|
|
|
|
|
|
int base_size = JSArray::kSize; |
|
|
|
|
|
if (mode_ == TRACK_ALLOCATION_SITE) { |
|
|
|
|
|
base_size += AllocationSiteInfo::kSize; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (IsFastDoubleElementsKind(kind_)) { |
|
|
|
|
|
base_size += FixedDoubleArray::kHeaderSize; |
|
|
|
|
|
} else { |
|
|
|
|
|
base_size += FixedArray::kHeaderSize; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
HInstruction* elements_size_value = new(zone()) |
|
|
|
|
|
HConstant(elements_size(), Representation::Integer32()); |
|
|
|
|
|
AddInstruction(elements_size_value); |
|
|
|
|
|
HInstruction* mul = HMul::New(zone(), context, length_node, |
|
|
|
|
|
elements_size_value); |
|
|
|
|
|
mul->ChangeRepresentation(Representation::Integer32()); |
|
|
|
|
|
mul->ClearFlag(HValue::kCanOverflow); |
|
|
|
|
|
AddInstruction(mul); |
|
|
|
|
|
|
|
|
|
|
|
HInstruction* base = new(zone()) HConstant(base_size, |
|
|
|
|
|
Representation::Integer32()); |
|
|
|
|
|
AddInstruction(base); |
|
|
|
|
|
HInstruction* total_size = HAdd::New(zone(), context, base, mul); |
|
|
|
|
|
total_size->ChangeRepresentation(Representation::Integer32()); |
|
|
|
|
|
total_size->ClearFlag(HValue::kCanOverflow); |
|
|
|
|
|
AddInstruction(total_size); |
|
|
|
|
|
return total_size; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
HValue* HGraphBuilder::JSArrayBuilder::EstablishEmptyArrayAllocationSize() { |
|
|
|
|
|
int base_size = JSArray::kSize; |
|
|
|
|
|
if (mode_ == TRACK_ALLOCATION_SITE) { |
|
|
|
|
|
base_size += AllocationSiteInfo::kSize; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
base_size += IsFastDoubleElementsKind(kind_) |
|
|
|
|
|
? FixedDoubleArray::SizeFor(initial_capacity()) |
|
|
|
|
|
: FixedArray::SizeFor(initial_capacity()); |
|
|
|
|
|
|
|
|
|
|
|
HConstant* array_size = |
|
|
|
|
|
new(zone()) HConstant(base_size, Representation::Integer32()); |
|
|
|
|
|
AddInstruction(array_size); |
|
|
|
|
|
return array_size; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
HValue* HGraphBuilder::JSArrayBuilder::AllocateEmptyArray() { |
|
|
|
|
|
HValue* size_in_bytes = EstablishEmptyArrayAllocationSize(); |
|
|
|
|
|
HConstant* capacity = |
|
|
|
|
|
new(zone()) HConstant(initial_capacity(), Representation::Integer32()); |
|
|
|
|
|
AddInstruction(capacity); |
|
|
|
|
|
return AllocateArray(size_in_bytes, |
|
|
|
|
|
capacity, |
|
|
|
|
|
builder()->graph()->GetConstant0(), |
|
|
|
|
|
true); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
HValue* HGraphBuilder::JSArrayBuilder::AllocateArray(HValue* capacity, |
|
|
|
|
|
HValue* length_field, |
|
|
|
|
|
bool fill_with_hole) { |
|
|
|
|
|
HValue* size_in_bytes = EstablishAllocationSize(capacity); |
|
|
|
|
|
return AllocateArray(size_in_bytes, capacity, length_field, fill_with_hole); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
HValue* HGraphBuilder::JSArrayBuilder::AllocateArray(HValue* size_in_bytes, |
|
|
|
|
|
HValue* capacity, |
|
|
|
|
|
HValue* length_field, |
|
|
|
|
|
bool fill_with_hole) { |
|
|
|
|
|
HValue* context = builder()->environment()->LookupContext(); |
|
|
|
|
|
|
|
|
|
|
|
// Allocate (dealing with failure appropriately)
|
|
|
|
|
|
HAllocate::Flags flags = HAllocate::DefaultFlags(kind_); |
|
|
|
|
|
HAllocate* new_object = new(zone()) HAllocate(context, size_in_bytes, |
|
|
|
|
|
HType::JSArray(), flags); |
|
|
|
|
|
AddInstruction(new_object); |
|
|
|
|
|
|
|
|
|
|
|
// Fill in the fields: map, properties, length
|
|
|
|
|
|
HValue* map = EmitMapCode(context); |
|
|
|
|
|
elements_location_ = builder()->BuildJSArrayHeader(new_object, |
|
|
|
|
|
map, |
|
|
|
|
|
mode_, |
|
|
|
|
|
allocation_site_payload_, |
|
|
|
|
|
length_field); |
|
|
|
|
|
|
|
|
|
|
|
// Initialize the elements
|
|
|
|
|
|
builder()->BuildInitializeElements(elements_location_, kind_, capacity); |
|
|
|
|
|
|
|
|
|
|
|
if (fill_with_hole) { |
|
|
|
|
|
builder()->BuildFillElementsWithHole(context, elements_location_, kind_, |
|
|
|
|
|
graph()->GetConstant0(), capacity); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return new_object; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
HOptimizedGraphBuilder::HOptimizedGraphBuilder(CompilationInfo* info, |
|
|
HOptimizedGraphBuilder::HOptimizedGraphBuilder(CompilationInfo* info, |
|
|
TypeFeedbackOracle* oracle) |
|
|
TypeFeedbackOracle* oracle) |
|
|
: HGraphBuilder(info), |
|
|
: HGraphBuilder(info), |
|
@ -3506,7 +3747,12 @@ void HInferRepresentation::Analyze() { |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// (3a) Use the phi reachability information from step 2 to
|
|
|
// Simplify constant phi inputs where possible.
|
|
|
|
|
|
for (int i = 0; i < phi_count; ++i) { |
|
|
|
|
|
phi_list->at(i)->SimplifyConstantInputs(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Use the phi reachability information from step 2 to
|
|
|
// push information about values which can't be converted to integer
|
|
|
// push information about values which can't be converted to integer
|
|
|
// without deoptimization through the phi use-def chains, avoiding
|
|
|
// without deoptimization through the phi use-def chains, avoiding
|
|
|
// unnecessary deoptimizations later.
|
|
|
// unnecessary deoptimizations later.
|
|
@ -3523,7 +3769,7 @@ void HInferRepresentation::Analyze() { |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// (3b) Use the phi reachability information from step 2 to
|
|
|
// Use the phi reachability information from step 2 to
|
|
|
// sum up the non-phi use counts of all connected phis.
|
|
|
// sum up the non-phi use counts of all connected phis.
|
|
|
for (int i = 0; i < phi_count; ++i) { |
|
|
for (int i = 0; i < phi_count; ++i) { |
|
|
HPhi* phi = phi_list->at(i); |
|
|
HPhi* phi = phi_list->at(i); |
|
@ -6412,9 +6658,11 @@ void HOptimizedGraphBuilder::VisitObjectLiteral(ObjectLiteral* expr) { |
|
|
pointer_size, |
|
|
pointer_size, |
|
|
DONT_TRACK_ALLOCATION_SITE); |
|
|
DONT_TRACK_ALLOCATION_SITE); |
|
|
} else { |
|
|
} else { |
|
|
|
|
|
Handle<FixedArray> closure_literals(closure->literals(), isolate()); |
|
|
literal = AddInstruction( |
|
|
literal = AddInstruction( |
|
|
new(zone()) HObjectLiteral(context, |
|
|
new(zone()) HObjectLiteral(context, |
|
|
expr->constant_properties(), |
|
|
expr->constant_properties(), |
|
|
|
|
|
closure_literals, |
|
|
expr->fast_elements(), |
|
|
expr->fast_elements(), |
|
|
expr->literal_index(), |
|
|
expr->literal_index(), |
|
|
expr->depth(), |
|
|
expr->depth(), |
|
@ -6503,7 +6751,7 @@ void HOptimizedGraphBuilder::VisitArrayLiteral(ArrayLiteral* expr) { |
|
|
HValue* context = environment()->LookupContext(); |
|
|
HValue* context = environment()->LookupContext(); |
|
|
HInstruction* literal; |
|
|
HInstruction* literal; |
|
|
|
|
|
|
|
|
Handle<FixedArray> literals(environment()->closure()->literals()); |
|
|
Handle<FixedArray> literals(environment()->closure()->literals(), isolate()); |
|
|
Handle<Object> raw_boilerplate(literals->get(expr->literal_index()), |
|
|
Handle<Object> raw_boilerplate(literals->get(expr->literal_index()), |
|
|
isolate()); |
|
|
isolate()); |
|
|
|
|
|
|
|
@ -6555,6 +6803,7 @@ void HOptimizedGraphBuilder::VisitArrayLiteral(ArrayLiteral* expr) { |
|
|
literal = AddInstruction( |
|
|
literal = AddInstruction( |
|
|
new(zone()) HArrayLiteral(context, |
|
|
new(zone()) HArrayLiteral(context, |
|
|
original_boilerplate_object, |
|
|
original_boilerplate_object, |
|
|
|
|
|
literals, |
|
|
length, |
|
|
length, |
|
|
expr->literal_index(), |
|
|
expr->literal_index(), |
|
|
expr->depth(), |
|
|
expr->depth(), |
|
@ -6652,7 +6901,7 @@ static int ComputeLoadStoreFieldIndex(Handle<Map> type, |
|
|
|
|
|
|
|
|
void HOptimizedGraphBuilder::AddCheckMap(HValue* object, Handle<Map> map) { |
|
|
void HOptimizedGraphBuilder::AddCheckMap(HValue* object, Handle<Map> map) { |
|
|
AddInstruction(new(zone()) HCheckNonSmi(object)); |
|
|
AddInstruction(new(zone()) HCheckNonSmi(object)); |
|
|
AddInstruction(new(zone()) HCheckMaps(object, map, zone())); |
|
|
AddInstruction(HCheckMaps::New(object, map, zone())); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -6781,7 +7030,7 @@ bool HOptimizedGraphBuilder::HandlePolymorphicArrayLengthLoad( |
|
|
AddInstruction(new(zone()) HCheckNonSmi(object)); |
|
|
AddInstruction(new(zone()) HCheckNonSmi(object)); |
|
|
|
|
|
|
|
|
HInstruction* typecheck = |
|
|
HInstruction* typecheck = |
|
|
AddInstruction(new(zone()) HCheckMaps(object, types, zone())); |
|
|
AddInstruction(HCheckMaps::New(object, types, zone())); |
|
|
HInstruction* instr = |
|
|
HInstruction* instr = |
|
|
HLoadNamedField::NewArrayLength(zone(), object, typecheck); |
|
|
HLoadNamedField::NewArrayLength(zone(), object, typecheck); |
|
|
instr->set_position(expr->position()); |
|
|
instr->set_position(expr->position()); |
|
@ -6833,7 +7082,7 @@ void HOptimizedGraphBuilder::HandlePolymorphicLoadNamedField(Property* expr, |
|
|
AddInstruction(new(zone()) HCheckNonSmi(object)); |
|
|
AddInstruction(new(zone()) HCheckNonSmi(object)); |
|
|
HInstruction* instr; |
|
|
HInstruction* instr; |
|
|
if (count == types->length() && is_monomorphic_field) { |
|
|
if (count == types->length() && is_monomorphic_field) { |
|
|
AddInstruction(new(zone()) HCheckMaps(object, types, zone())); |
|
|
AddInstruction(HCheckMaps::New(object, types, zone())); |
|
|
instr = BuildLoadNamedField(object, map, &lookup); |
|
|
instr = BuildLoadNamedField(object, map, &lookup); |
|
|
} else { |
|
|
} else { |
|
|
HValue* context = environment()->LookupContext(); |
|
|
HValue* context = environment()->LookupContext(); |
|
@ -7510,8 +7759,7 @@ HInstruction* HOptimizedGraphBuilder::BuildMonomorphicElementAccess( |
|
|
Handle<Map> map, |
|
|
Handle<Map> map, |
|
|
bool is_store, |
|
|
bool is_store, |
|
|
KeyedAccessStoreMode store_mode) { |
|
|
KeyedAccessStoreMode store_mode) { |
|
|
HCheckMaps* mapcheck = new(zone()) HCheckMaps(object, map, |
|
|
HCheckMaps* mapcheck = HCheckMaps::New(object, map, zone(), dependency); |
|
|
zone(), dependency); |
|
|
|
|
|
AddInstruction(mapcheck); |
|
|
AddInstruction(mapcheck); |
|
|
if (dependency) { |
|
|
if (dependency) { |
|
|
mapcheck->ClearGVNFlag(kDependsOnElementsKind); |
|
|
mapcheck->ClearGVNFlag(kDependsOnElementsKind); |
|
@ -7568,7 +7816,7 @@ HInstruction* HOptimizedGraphBuilder::TryBuildConsolidatedElementLoad( |
|
|
} |
|
|
} |
|
|
if (!has_double_maps && !has_smi_or_object_maps) return NULL; |
|
|
if (!has_double_maps && !has_smi_or_object_maps) return NULL; |
|
|
|
|
|
|
|
|
HCheckMaps* check_maps = new(zone()) HCheckMaps(object, maps, zone()); |
|
|
HCheckMaps* check_maps = HCheckMaps::New(object, maps, zone()); |
|
|
AddInstruction(check_maps); |
|
|
AddInstruction(check_maps); |
|
|
HInstruction* instr = BuildUncheckedMonomorphicElementAccess( |
|
|
HInstruction* instr = BuildUncheckedMonomorphicElementAccess( |
|
|
object, key, val, check_maps, |
|
|
object, key, val, check_maps, |
|
@ -7720,7 +7968,7 @@ HValue* HOptimizedGraphBuilder::HandlePolymorphicElementAccess( |
|
|
HInstruction* access; |
|
|
HInstruction* access; |
|
|
if (IsFastElementsKind(elements_kind)) { |
|
|
if (IsFastElementsKind(elements_kind)) { |
|
|
if (is_store && !IsFastDoubleElementsKind(elements_kind)) { |
|
|
if (is_store && !IsFastDoubleElementsKind(elements_kind)) { |
|
|
AddInstruction(new(zone()) HCheckMaps( |
|
|
AddInstruction(HCheckMaps::New( |
|
|
elements, isolate()->factory()->fixed_array_map(), |
|
|
elements, isolate()->factory()->fixed_array_map(), |
|
|
zone(), elements_kind_branch)); |
|
|
zone(), elements_kind_branch)); |
|
|
} |
|
|
} |
|
@ -7754,10 +8002,12 @@ HValue* HOptimizedGraphBuilder::HandlePolymorphicElementAccess( |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
*has_side_effects |= access->HasObservableSideEffects(); |
|
|
*has_side_effects |= access->HasObservableSideEffects(); |
|
|
|
|
|
// The caller will use has_side_effects and add correct Simulate.
|
|
|
|
|
|
access->SetFlag(HValue::kHasNoObservableSideEffects); |
|
|
if (position != -1) { |
|
|
if (position != -1) { |
|
|
access->set_position(position); |
|
|
access->set_position(position); |
|
|
} |
|
|
} |
|
|
if_jsarray->Goto(join); |
|
|
if_jsarray->GotoNoSimulate(join); |
|
|
|
|
|
|
|
|
set_current_block(if_fastobject); |
|
|
set_current_block(if_fastobject); |
|
|
length = AddInstruction(new(zone()) HFixedArrayBaseLength(elements)); |
|
|
length = AddInstruction(new(zone()) HFixedArrayBaseLength(elements)); |
|
@ -7777,18 +8027,19 @@ HValue* HOptimizedGraphBuilder::HandlePolymorphicElementAccess( |
|
|
elements_kind_branch, elements_kind, is_store)); |
|
|
elements_kind_branch, elements_kind, is_store)); |
|
|
} |
|
|
} |
|
|
*has_side_effects |= access->HasObservableSideEffects(); |
|
|
*has_side_effects |= access->HasObservableSideEffects(); |
|
|
|
|
|
// The caller will use has_side_effects and add correct Simulate.
|
|
|
|
|
|
access->SetFlag(HValue::kHasNoObservableSideEffects); |
|
|
if (position != RelocInfo::kNoPosition) access->set_position(position); |
|
|
if (position != RelocInfo::kNoPosition) access->set_position(position); |
|
|
if (!is_store) { |
|
|
if (!is_store) { |
|
|
Push(access); |
|
|
Push(access); |
|
|
} |
|
|
} |
|
|
current_block()->Goto(join); |
|
|
current_block()->GotoNoSimulate(join); |
|
|
set_current_block(if_false); |
|
|
set_current_block(if_false); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// Deopt if none of the cases matched.
|
|
|
// Deopt if none of the cases matched.
|
|
|
current_block()->FinishExitWithDeoptimization(HDeoptimize::kNoUses); |
|
|
current_block()->FinishExitWithDeoptimization(HDeoptimize::kNoUses); |
|
|
join->SetJoinId(ast_id); |
|
|
|
|
|
set_current_block(join); |
|
|
set_current_block(join); |
|
|
return is_store ? NULL : Pop(); |
|
|
return is_store ? NULL : Pop(); |
|
|
} |
|
|
} |
|
@ -8067,14 +8318,12 @@ class FunctionSorter { |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int CompareHotness(void const* a, void const* b) { |
|
|
inline bool operator<(const FunctionSorter& lhs, const FunctionSorter& rhs) { |
|
|
FunctionSorter const* function1 = reinterpret_cast<FunctionSorter const*>(a); |
|
|
int diff = lhs.ticks() - rhs.ticks(); |
|
|
FunctionSorter const* function2 = reinterpret_cast<FunctionSorter const*>(b); |
|
|
if (diff != 0) return diff > 0; |
|
|
int diff = function1->ticks() - function2->ticks(); |
|
|
diff = lhs.ast_length() - rhs.ast_length(); |
|
|
if (diff != 0) return -diff; |
|
|
if (diff != 0) return diff < 0; |
|
|
diff = function1->ast_length() - function2->ast_length(); |
|
|
return lhs.src_length() < rhs.src_length(); |
|
|
if (diff != 0) return diff; |
|
|
|
|
|
return function1->src_length() - function2->src_length(); |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -8117,10 +8366,7 @@ void HOptimizedGraphBuilder::HandlePolymorphicCallNamed( |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
qsort(reinterpret_cast<void*>(&order[0]), |
|
|
std::sort(order, order + ordered_functions); |
|
|
ordered_functions, |
|
|
|
|
|
sizeof(order[0]), |
|
|
|
|
|
&CompareHotness); |
|
|
|
|
|
|
|
|
|
|
|
HBasicBlock* number_block = NULL; |
|
|
HBasicBlock* number_block = NULL; |
|
|
|
|
|
|
|
@ -8697,6 +8943,18 @@ bool HOptimizedGraphBuilder::TryInlineBuiltinFunctionCall(Call* expr, |
|
|
return true; |
|
|
return true; |
|
|
} |
|
|
} |
|
|
break; |
|
|
break; |
|
|
|
|
|
case kMathImul: |
|
|
|
|
|
if (expr->arguments()->length() == 2) { |
|
|
|
|
|
HValue* right = Pop(); |
|
|
|
|
|
HValue* left = Pop(); |
|
|
|
|
|
Drop(1); // Receiver.
|
|
|
|
|
|
HValue* context = environment()->LookupContext(); |
|
|
|
|
|
HInstruction* op = HMul::NewImul(zone(), context, left, right); |
|
|
|
|
|
if (drop_extra) Drop(1); // Optionally drop the function.
|
|
|
|
|
|
ast_context()->ReturnInstruction(op, expr->id()); |
|
|
|
|
|
return true; |
|
|
|
|
|
} |
|
|
|
|
|
break; |
|
|
default: |
|
|
default: |
|
|
// Not supported for inlining yet.
|
|
|
// Not supported for inlining yet.
|
|
|
break; |
|
|
break; |
|
@ -8844,6 +9102,18 @@ bool HOptimizedGraphBuilder::TryInlineBuiltinMethodCall( |
|
|
return true; |
|
|
return true; |
|
|
} |
|
|
} |
|
|
break; |
|
|
break; |
|
|
|
|
|
case kMathImul: |
|
|
|
|
|
if (argument_count == 3 && check_type == RECEIVER_MAP_CHECK) { |
|
|
|
|
|
AddCheckConstantFunction(expr->holder(), receiver, receiver_map); |
|
|
|
|
|
HValue* right = Pop(); |
|
|
|
|
|
HValue* left = Pop(); |
|
|
|
|
|
Drop(1); // Receiver.
|
|
|
|
|
|
HValue* context = environment()->LookupContext(); |
|
|
|
|
|
HInstruction* result = HMul::NewImul(zone(), context, left, right); |
|
|
|
|
|
ast_context()->ReturnInstruction(result, expr->id()); |
|
|
|
|
|
return true; |
|
|
|
|
|
} |
|
|
|
|
|
break; |
|
|
default: |
|
|
default: |
|
|
// Not yet supported for inlining.
|
|
|
// Not yet supported for inlining.
|
|
|
break; |
|
|
break; |
|
@ -9276,19 +9546,31 @@ void HOptimizedGraphBuilder::VisitCallNew(CallNew* expr) { |
|
|
} else { |
|
|
} else { |
|
|
// The constructor function is both an operand to the instruction and an
|
|
|
// The constructor function is both an operand to the instruction and an
|
|
|
// argument to the construct call.
|
|
|
// argument to the construct call.
|
|
|
|
|
|
bool use_call_new_array = FLAG_optimize_constructed_arrays && |
|
|
|
|
|
!(expr->target().is_null()) && |
|
|
|
|
|
*(expr->target()) == isolate()->global_context()->array_function(); |
|
|
|
|
|
|
|
|
CHECK_ALIVE(VisitArgument(expr->expression())); |
|
|
CHECK_ALIVE(VisitArgument(expr->expression())); |
|
|
HValue* constructor = HPushArgument::cast(Top())->argument(); |
|
|
HValue* constructor = HPushArgument::cast(Top())->argument(); |
|
|
CHECK_ALIVE(VisitArgumentList(expr->arguments())); |
|
|
CHECK_ALIVE(VisitArgumentList(expr->arguments())); |
|
|
HCallNew* call; |
|
|
HCallNew* call; |
|
|
if (FLAG_optimize_constructed_arrays && |
|
|
if (use_call_new_array) { |
|
|
!(expr->target().is_null()) && |
|
|
AddInstruction(new(zone()) HCheckFunction(constructor, |
|
|
*(expr->target()) == isolate()->global_context()->array_function()) { |
|
|
Handle<JSFunction>(isolate()->global_context()->array_function()))); |
|
|
Handle<Object> feedback = oracle()->GetInfo(expr->CallNewFeedbackId()); |
|
|
Handle<Object> feedback = oracle()->GetInfo(expr->CallNewFeedbackId()); |
|
|
ASSERT(feedback->IsSmi()); |
|
|
ASSERT(feedback->IsSmi()); |
|
|
|
|
|
|
|
|
|
|
|
// TODO(mvstanton): It would be better to use the already created global
|
|
|
|
|
|
// property cell that is shared by full code gen. That way, any transition
|
|
|
|
|
|
// information that happened after crankshaft won't be lost. The right
|
|
|
|
|
|
// way to do that is to begin passing the cell to the type feedback oracle
|
|
|
|
|
|
// instead of just the value in the cell. Do this in a follow-up checkin.
|
|
|
Handle<JSGlobalPropertyCell> cell = |
|
|
Handle<JSGlobalPropertyCell> cell = |
|
|
isolate()->factory()->NewJSGlobalPropertyCell(feedback); |
|
|
isolate()->factory()->NewJSGlobalPropertyCell(feedback); |
|
|
AddInstruction(new(zone()) HCheckFunction(constructor, |
|
|
|
|
|
Handle<JSFunction>(isolate()->global_context()->array_function()))); |
|
|
// TODO(mvstanton): Here we should probably insert code to check if the
|
|
|
|
|
|
// type cell elements kind is different from when we compiled, and deopt
|
|
|
|
|
|
// in that case. Do this in a follow-up checin.
|
|
|
call = new(zone()) HCallNewArray(context, constructor, argument_count, |
|
|
call = new(zone()) HCallNewArray(context, constructor, argument_count, |
|
|
cell); |
|
|
cell); |
|
|
} else { |
|
|
} else { |
|
@ -9433,7 +9715,8 @@ void HOptimizedGraphBuilder::VisitSub(UnaryOperation* expr) { |
|
|
info = TypeInfo::Unknown(); |
|
|
info = TypeInfo::Unknown(); |
|
|
} |
|
|
} |
|
|
if (instr->IsBinaryOperation()) { |
|
|
if (instr->IsBinaryOperation()) { |
|
|
HBinaryOperation::cast(instr)->set_observed_input_representation(rep, rep); |
|
|
HBinaryOperation::cast(instr)->set_observed_input_representation(1, rep); |
|
|
|
|
|
HBinaryOperation::cast(instr)->set_observed_input_representation(2, rep); |
|
|
} |
|
|
} |
|
|
return ast_context()->ReturnInstruction(instr, expr->id()); |
|
|
return ast_context()->ReturnInstruction(instr, expr->id()); |
|
|
} |
|
|
} |
|
@ -9874,7 +10157,8 @@ HInstruction* HOptimizedGraphBuilder::BuildBinaryOperation( |
|
|
|
|
|
|
|
|
if (instr->IsBinaryOperation()) { |
|
|
if (instr->IsBinaryOperation()) { |
|
|
HBinaryOperation* binop = HBinaryOperation::cast(instr); |
|
|
HBinaryOperation* binop = HBinaryOperation::cast(instr); |
|
|
binop->set_observed_input_representation(left_rep, right_rep); |
|
|
binop->set_observed_input_representation(1, left_rep); |
|
|
|
|
|
binop->set_observed_input_representation(2, right_rep); |
|
|
binop->initialize_output_representation(result_rep); |
|
|
binop->initialize_output_representation(result_rep); |
|
|
} |
|
|
} |
|
|
return instr; |
|
|
return instr; |
|
@ -10254,7 +10538,8 @@ void HOptimizedGraphBuilder::VisitCompareOperation(CompareOperation* expr) { |
|
|
if (combined_rep.IsTagged() || combined_rep.IsNone()) { |
|
|
if (combined_rep.IsTagged() || combined_rep.IsNone()) { |
|
|
HCompareGeneric* result = |
|
|
HCompareGeneric* result = |
|
|
new(zone()) HCompareGeneric(context, left, right, op); |
|
|
new(zone()) HCompareGeneric(context, left, right, op); |
|
|
result->set_observed_input_representation(left_rep, right_rep); |
|
|
result->set_observed_input_representation(1, left_rep); |
|
|
|
|
|
result->set_observed_input_representation(2, right_rep); |
|
|
result->set_position(expr->position()); |
|
|
result->set_position(expr->position()); |
|
|
return ast_context()->ReturnInstruction(result, expr->id()); |
|
|
return ast_context()->ReturnInstruction(result, expr->id()); |
|
|
} else { |
|
|
} else { |
|
@ -10276,9 +10561,24 @@ void HOptimizedGraphBuilder::HandleLiteralCompareNil(CompareOperation* expr, |
|
|
ASSERT(current_block()->HasPredecessor()); |
|
|
ASSERT(current_block()->HasPredecessor()); |
|
|
EqualityKind kind = |
|
|
EqualityKind kind = |
|
|
expr->op() == Token::EQ_STRICT ? kStrictEquality : kNonStrictEquality; |
|
|
expr->op() == Token::EQ_STRICT ? kStrictEquality : kNonStrictEquality; |
|
|
HIsNilAndBranch* instr = new(zone()) HIsNilAndBranch(value, kind, nil); |
|
|
HIfContinuation continuation; |
|
|
instr->set_position(expr->position()); |
|
|
TypeFeedbackId id = expr->CompareOperationFeedbackId(); |
|
|
return ast_context()->ReturnControl(instr, expr->id()); |
|
|
CompareNilICStub::Types types; |
|
|
|
|
|
if (kind == kStrictEquality) { |
|
|
|
|
|
if (nil == kNullValue) { |
|
|
|
|
|
types = CompareNilICStub::kCompareAgainstNull; |
|
|
|
|
|
} else { |
|
|
|
|
|
types = CompareNilICStub::kCompareAgainstUndefined; |
|
|
|
|
|
} |
|
|
|
|
|
} else { |
|
|
|
|
|
types = static_cast<CompareNilICStub::Types>( |
|
|
|
|
|
oracle()->CompareNilTypes(id)); |
|
|
|
|
|
if (types == 0) types = CompareNilICStub::kFullCompare; |
|
|
|
|
|
} |
|
|
|
|
|
Handle<Map> map_handle(oracle()->CompareNilMonomorphicReceiverType(id)); |
|
|
|
|
|
BuildCompareNil(value, kind, types, map_handle, |
|
|
|
|
|
expr->position(), &continuation); |
|
|
|
|
|
return ast_context()->ReturnContinuation(&continuation, expr->id()); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -10401,15 +10701,7 @@ void HOptimizedGraphBuilder::BuildEmitDeepCopy( |
|
|
|
|
|
|
|
|
// Build Allocation Site Info if desired
|
|
|
// Build Allocation Site Info if desired
|
|
|
if (create_allocation_site_info) { |
|
|
if (create_allocation_site_info) { |
|
|
HValue* alloc_site = |
|
|
BuildCreateAllocationSiteInfo(target, JSArray::kSize, original_boilerplate); |
|
|
AddInstruction(new(zone) HInnerAllocatedObject(target, JSArray::kSize)); |
|
|
|
|
|
Handle<Map> alloc_site_map(isolate()->heap()->allocation_site_info_map()); |
|
|
|
|
|
BuildStoreMap(alloc_site, alloc_site_map); |
|
|
|
|
|
int alloc_payload_offset = AllocationSiteInfo::kPayloadOffset; |
|
|
|
|
|
AddInstruction(new(zone) HStoreNamedField(alloc_site, |
|
|
|
|
|
factory->payload_string(), |
|
|
|
|
|
original_boilerplate, |
|
|
|
|
|
true, alloc_payload_offset)); |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (object_elements != NULL) { |
|
|
if (object_elements != NULL) { |
|
@ -11201,6 +11493,17 @@ void HOptimizedGraphBuilder::GenerateFastAsciiArrayJoin(CallRuntime* call) { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Support for generators.
|
|
|
|
|
|
void HOptimizedGraphBuilder::GenerateGeneratorSend(CallRuntime* call) { |
|
|
|
|
|
return Bailout("inlined runtime function: GeneratorSend"); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void HOptimizedGraphBuilder::GenerateGeneratorThrow(CallRuntime* call) { |
|
|
|
|
|
return Bailout("inlined runtime function: GeneratorThrow"); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#undef CHECK_BAILOUT |
|
|
#undef CHECK_BAILOUT |
|
|
#undef CHECK_ALIVE |
|
|
#undef CHECK_ALIVE |
|
|
|
|
|
|
|
@ -11521,16 +11824,14 @@ void HTracer::TraceCompilation(CompilationInfo* info) { |
|
|
|
|
|
|
|
|
void HTracer::TraceLithium(const char* name, LChunk* chunk) { |
|
|
void HTracer::TraceLithium(const char* name, LChunk* chunk) { |
|
|
ASSERT(!FLAG_parallel_recompilation); |
|
|
ASSERT(!FLAG_parallel_recompilation); |
|
|
HandleDereferenceGuard allow_handle_deref(chunk->isolate(), |
|
|
ALLOW_HANDLE_DEREF(chunk->isolate(), "debug output"); |
|
|
HandleDereferenceGuard::ALLOW); |
|
|
|
|
|
Trace(name, chunk->graph(), chunk); |
|
|
Trace(name, chunk->graph(), chunk); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void HTracer::TraceHydrogen(const char* name, HGraph* graph) { |
|
|
void HTracer::TraceHydrogen(const char* name, HGraph* graph) { |
|
|
ASSERT(!FLAG_parallel_recompilation); |
|
|
ASSERT(!FLAG_parallel_recompilation); |
|
|
HandleDereferenceGuard allow_handle_deref(graph->isolate(), |
|
|
ALLOW_HANDLE_DEREF(graph->isolate(), "debug output"); |
|
|
HandleDereferenceGuard::ALLOW); |
|
|
|
|
|
Trace(name, graph, NULL); |
|
|
Trace(name, graph, NULL); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|