Fixed Lithium environment generation bug for captured objects (created

by escape analysis). Added several tests that expose the bug.

Summary:
LCodegen::AddToTranslation assumes that Lithium environments are
generated by depth-first traversal, but LChunkBuilder::CreateEnvironment
was generating them in breadth-first fashion. This fixes the
CreateEnvironment to traverse the captured objects depth-first.

Note:
It might be worth considering representing LEnvironment by a list
with the same order as the serialized translation representation
rather than having two lists with a subtle relationship between
them (and then serialize in a slightly different order again).

R=titzer@chromium.org, mstarzinger@chromium.org
LOG=N
BUG=

Review URL: https://codereview.chromium.org/93803003

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@18470 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
jarin@chromium.org 2014-01-07 14:36:26 +00:00
parent d8e9893c59
commit acf24331e3
11 changed files with 273 additions and 384 deletions

View File

@ -926,90 +926,6 @@ void LChunkBuilder::VisitInstruction(HInstruction* current) {
} }
LEnvironment* LChunkBuilder::CreateEnvironment(
HEnvironment* hydrogen_env,
int* argument_index_accumulator,
ZoneList<HValue*>* objects_to_materialize) {
if (hydrogen_env == NULL) return NULL;
LEnvironment* outer = CreateEnvironment(hydrogen_env->outer(),
argument_index_accumulator,
objects_to_materialize);
BailoutId ast_id = hydrogen_env->ast_id();
ASSERT(!ast_id.IsNone() ||
hydrogen_env->frame_type() != JS_FUNCTION);
int value_count = hydrogen_env->length() - hydrogen_env->specials_count();
LEnvironment* result = new(zone()) LEnvironment(
hydrogen_env->closure(),
hydrogen_env->frame_type(),
ast_id,
hydrogen_env->parameter_count(),
argument_count_,
value_count,
outer,
hydrogen_env->entry(),
zone());
int argument_index = *argument_index_accumulator;
int object_index = objects_to_materialize->length();
for (int i = 0; i < hydrogen_env->length(); ++i) {
if (hydrogen_env->is_special_index(i)) continue;
LOperand* op;
HValue* value = hydrogen_env->values()->at(i);
if (value->IsArgumentsObject() || value->IsCapturedObject()) {
objects_to_materialize->Add(value, zone());
op = LEnvironment::materialization_marker();
} else if (value->IsPushArgument()) {
op = new(zone()) LArgument(argument_index++);
} else {
op = UseAny(value);
}
result->AddValue(op,
value->representation(),
value->CheckFlag(HInstruction::kUint32));
}
for (int i = object_index; i < objects_to_materialize->length(); ++i) {
HValue* object_to_materialize = objects_to_materialize->at(i);
int previously_materialized_object = -1;
for (int prev = 0; prev < i; ++prev) {
if (objects_to_materialize->at(prev) == objects_to_materialize->at(i)) {
previously_materialized_object = prev;
break;
}
}
int length = object_to_materialize->OperandCount();
bool is_arguments = object_to_materialize->IsArgumentsObject();
if (previously_materialized_object >= 0) {
result->AddDuplicateObject(previously_materialized_object);
continue;
} else {
result->AddNewObject(is_arguments ? length - 1 : length, is_arguments);
}
for (int i = is_arguments ? 1 : 0; i < length; ++i) {
LOperand* op;
HValue* value = object_to_materialize->OperandAt(i);
if (value->IsArgumentsObject() || value->IsCapturedObject()) {
objects_to_materialize->Add(value, zone());
op = LEnvironment::materialization_marker();
} else {
ASSERT(!value->IsPushArgument());
op = UseAny(value);
}
result->AddValue(op,
value->representation(),
value->CheckFlag(HInstruction::kUint32));
}
}
if (hydrogen_env->frame_type() == JS_FUNCTION) {
*argument_index_accumulator = argument_index;
}
return result;
}
LInstruction* LChunkBuilder::DoGoto(HGoto* instr) { LInstruction* LChunkBuilder::DoGoto(HGoto* instr) {
return new(zone()) LGoto(instr->FirstSuccessor()); return new(zone()) LGoto(instr->FirstSuccessor());
} }

View File

@ -2697,18 +2697,17 @@ class LPlatformChunk V8_FINAL : public LChunk {
}; };
class LChunkBuilder V8_FINAL BASE_EMBEDDED { class LChunkBuilder V8_FINAL : public LChunkBuilderBase {
public: public:
LChunkBuilder(CompilationInfo* info, HGraph* graph, LAllocator* allocator) LChunkBuilder(CompilationInfo* info, HGraph* graph, LAllocator* allocator)
: chunk_(NULL), : LChunkBuilderBase(graph->zone()),
chunk_(NULL),
info_(info), info_(info),
graph_(graph), graph_(graph),
zone_(graph->zone()),
status_(UNUSED), status_(UNUSED),
current_instruction_(NULL), current_instruction_(NULL),
current_block_(NULL), current_block_(NULL),
next_block_(NULL), next_block_(NULL),
argument_count_(0),
allocator_(allocator), allocator_(allocator),
position_(RelocInfo::kNoPosition), position_(RelocInfo::kNoPosition),
instruction_pending_deoptimization_environment_(NULL), instruction_pending_deoptimization_environment_(NULL),
@ -2750,7 +2749,6 @@ class LChunkBuilder V8_FINAL BASE_EMBEDDED {
LPlatformChunk* chunk() const { return chunk_; } LPlatformChunk* chunk() const { return chunk_; }
CompilationInfo* info() const { return info_; } CompilationInfo* info() const { return info_; }
HGraph* graph() const { return graph_; } HGraph* graph() const { return graph_; }
Zone* zone() const { return zone_; }
bool is_unused() const { return status_ == UNUSED; } bool is_unused() const { return status_ == UNUSED; }
bool is_building() const { return status_ == BUILDING; } bool is_building() const { return status_ == BUILDING; }
@ -2800,7 +2798,7 @@ class LChunkBuilder V8_FINAL BASE_EMBEDDED {
// An input operand in register, stack slot or a constant operand. // An input operand in register, stack slot or a constant operand.
// Will not be moved to a register even if one is freely available. // Will not be moved to a register even if one is freely available.
MUST_USE_RESULT LOperand* UseAny(HValue* value); virtual MUST_USE_RESULT LOperand* UseAny(HValue* value) V8_OVERRIDE;
// Temporary operand that must be in a register. // Temporary operand that must be in a register.
MUST_USE_RESULT LUnallocated* TempRegister(); MUST_USE_RESULT LUnallocated* TempRegister();
@ -2838,10 +2836,6 @@ class LChunkBuilder V8_FINAL BASE_EMBEDDED {
HInstruction* hinstr, HInstruction* hinstr,
CanDeoptimize can_deoptimize = CANNOT_DEOPTIMIZE_EAGERLY); CanDeoptimize can_deoptimize = CANNOT_DEOPTIMIZE_EAGERLY);
LEnvironment* CreateEnvironment(HEnvironment* hydrogen_env,
int* argument_index_accumulator,
ZoneList<HValue*>* objects_to_materialize);
void VisitInstruction(HInstruction* current); void VisitInstruction(HInstruction* current);
void DoBasicBlock(HBasicBlock* block, HBasicBlock* next_block); void DoBasicBlock(HBasicBlock* block, HBasicBlock* next_block);
@ -2854,12 +2848,10 @@ class LChunkBuilder V8_FINAL BASE_EMBEDDED {
LPlatformChunk* chunk_; LPlatformChunk* chunk_;
CompilationInfo* info_; CompilationInfo* info_;
HGraph* const graph_; HGraph* const graph_;
Zone* zone_;
Status status_; Status status_;
HInstruction* current_instruction_; HInstruction* current_instruction_;
HBasicBlock* current_block_; HBasicBlock* current_block_;
HBasicBlock* next_block_; HBasicBlock* next_block_;
int argument_count_;
LAllocator* allocator_; LAllocator* allocator_;
int position_; int position_;
LInstruction* instruction_pending_deoptimization_environment_; LInstruction* instruction_pending_deoptimization_environment_;

View File

@ -1000,90 +1000,6 @@ void LChunkBuilder::VisitInstruction(HInstruction* current) {
} }
LEnvironment* LChunkBuilder::CreateEnvironment(
HEnvironment* hydrogen_env,
int* argument_index_accumulator,
ZoneList<HValue*>* objects_to_materialize) {
if (hydrogen_env == NULL) return NULL;
LEnvironment* outer = CreateEnvironment(hydrogen_env->outer(),
argument_index_accumulator,
objects_to_materialize);
BailoutId ast_id = hydrogen_env->ast_id();
ASSERT(!ast_id.IsNone() ||
hydrogen_env->frame_type() != JS_FUNCTION);
int value_count = hydrogen_env->length() - hydrogen_env->specials_count();
LEnvironment* result =
new(zone()) LEnvironment(hydrogen_env->closure(),
hydrogen_env->frame_type(),
ast_id,
hydrogen_env->parameter_count(),
argument_count_,
value_count,
outer,
hydrogen_env->entry(),
zone());
int argument_index = *argument_index_accumulator;
int object_index = objects_to_materialize->length();
for (int i = 0; i < hydrogen_env->length(); ++i) {
if (hydrogen_env->is_special_index(i)) continue;
LOperand* op;
HValue* value = hydrogen_env->values()->at(i);
if (value->IsArgumentsObject() || value->IsCapturedObject()) {
objects_to_materialize->Add(value, zone());
op = LEnvironment::materialization_marker();
} else if (value->IsPushArgument()) {
op = new(zone()) LArgument(argument_index++);
} else {
op = UseAny(value);
}
result->AddValue(op,
value->representation(),
value->CheckFlag(HInstruction::kUint32));
}
for (int i = object_index; i < objects_to_materialize->length(); ++i) {
HValue* object_to_materialize = objects_to_materialize->at(i);
int previously_materialized_object = -1;
for (int prev = 0; prev < i; ++prev) {
if (objects_to_materialize->at(prev) == objects_to_materialize->at(i)) {
previously_materialized_object = prev;
break;
}
}
int length = object_to_materialize->OperandCount();
bool is_arguments = object_to_materialize->IsArgumentsObject();
if (previously_materialized_object >= 0) {
result->AddDuplicateObject(previously_materialized_object);
continue;
} else {
result->AddNewObject(is_arguments ? length - 1 : length, is_arguments);
}
for (int i = is_arguments ? 1 : 0; i < length; ++i) {
LOperand* op;
HValue* value = object_to_materialize->OperandAt(i);
if (value->IsArgumentsObject() || value->IsCapturedObject()) {
objects_to_materialize->Add(value, zone());
op = LEnvironment::materialization_marker();
} else {
ASSERT(!value->IsPushArgument());
op = UseAny(value);
}
result->AddValue(op,
value->representation(),
value->CheckFlag(HInstruction::kUint32));
}
}
if (hydrogen_env->frame_type() == JS_FUNCTION) {
*argument_index_accumulator = argument_index;
}
return result;
}
LInstruction* LChunkBuilder::DoGoto(HGoto* instr) { LInstruction* LChunkBuilder::DoGoto(HGoto* instr) {
return new(zone()) LGoto(instr->FirstSuccessor()); return new(zone()) LGoto(instr->FirstSuccessor());
} }

View File

@ -2712,18 +2712,17 @@ class LPlatformChunk V8_FINAL : public LChunk {
}; };
class LChunkBuilder V8_FINAL BASE_EMBEDDED { class LChunkBuilder V8_FINAL : public LChunkBuilderBase {
public: public:
LChunkBuilder(CompilationInfo* info, HGraph* graph, LAllocator* allocator) LChunkBuilder(CompilationInfo* info, HGraph* graph, LAllocator* allocator)
: chunk_(NULL), : LChunkBuilderBase(graph->zone()),
chunk_(NULL),
info_(info), info_(info),
graph_(graph), graph_(graph),
zone_(graph->zone()),
status_(UNUSED), status_(UNUSED),
current_instruction_(NULL), current_instruction_(NULL),
current_block_(NULL), current_block_(NULL),
next_block_(NULL), next_block_(NULL),
argument_count_(0),
allocator_(allocator), allocator_(allocator),
instruction_pending_deoptimization_environment_(NULL), instruction_pending_deoptimization_environment_(NULL),
pending_deoptimization_ast_id_(BailoutId::None()) { } pending_deoptimization_ast_id_(BailoutId::None()) { }
@ -2759,7 +2758,6 @@ class LChunkBuilder V8_FINAL BASE_EMBEDDED {
LPlatformChunk* chunk() const { return chunk_; } LPlatformChunk* chunk() const { return chunk_; }
CompilationInfo* info() const { return info_; } CompilationInfo* info() const { return info_; }
HGraph* graph() const { return graph_; } HGraph* graph() const { return graph_; }
Zone* zone() const { return zone_; }
bool is_unused() const { return status_ == UNUSED; } bool is_unused() const { return status_ == UNUSED; }
bool is_building() const { return status_ == BUILDING; } bool is_building() const { return status_ == BUILDING; }
@ -2814,7 +2812,7 @@ class LChunkBuilder V8_FINAL BASE_EMBEDDED {
// An input operand in register, stack slot or a constant operand. // An input operand in register, stack slot or a constant operand.
// Will not be moved to a register even if one is freely available. // Will not be moved to a register even if one is freely available.
MUST_USE_RESULT LOperand* UseAny(HValue* value); virtual MUST_USE_RESULT LOperand* UseAny(HValue* value) V8_OVERRIDE;
// Temporary operand that must be in a register. // Temporary operand that must be in a register.
MUST_USE_RESULT LUnallocated* TempRegister(); MUST_USE_RESULT LUnallocated* TempRegister();
@ -2860,10 +2858,6 @@ class LChunkBuilder V8_FINAL BASE_EMBEDDED {
HInstruction* hinstr, HInstruction* hinstr,
CanDeoptimize can_deoptimize = CANNOT_DEOPTIMIZE_EAGERLY); CanDeoptimize can_deoptimize = CANNOT_DEOPTIMIZE_EAGERLY);
LEnvironment* CreateEnvironment(HEnvironment* hydrogen_env,
int* argument_index_accumulator,
ZoneList<HValue*>* objects_to_materialize);
void VisitInstruction(HInstruction* current); void VisitInstruction(HInstruction* current);
void DoBasicBlock(HBasicBlock* block, HBasicBlock* next_block); void DoBasicBlock(HBasicBlock* block, HBasicBlock* next_block);
@ -2878,12 +2872,10 @@ class LChunkBuilder V8_FINAL BASE_EMBEDDED {
LPlatformChunk* chunk_; LPlatformChunk* chunk_;
CompilationInfo* info_; CompilationInfo* info_;
HGraph* const graph_; HGraph* const graph_;
Zone* zone_;
Status status_; Status status_;
HInstruction* current_instruction_; HInstruction* current_instruction_;
HBasicBlock* current_block_; HBasicBlock* current_block_;
HBasicBlock* next_block_; HBasicBlock* next_block_;
int argument_count_;
LAllocator* allocator_; LAllocator* allocator_;
LInstruction* instruction_pending_deoptimization_environment_; LInstruction* instruction_pending_deoptimization_environment_;
BailoutId pending_deoptimization_ast_id_; BailoutId pending_deoptimization_ast_id_;

View File

@ -463,6 +463,139 @@ void LChunk::set_allocated_double_registers(BitVector* allocated_registers) {
} }
LEnvironment* LChunkBuilderBase::CreateEnvironment(
HEnvironment* hydrogen_env,
int* argument_index_accumulator,
ZoneList<HValue*>* objects_to_materialize) {
if (hydrogen_env == NULL) return NULL;
LEnvironment* outer = CreateEnvironment(hydrogen_env->outer(),
argument_index_accumulator,
objects_to_materialize);
BailoutId ast_id = hydrogen_env->ast_id();
ASSERT(!ast_id.IsNone() ||
hydrogen_env->frame_type() != JS_FUNCTION);
int value_count = hydrogen_env->length() - hydrogen_env->specials_count();
LEnvironment* result =
new(zone()) LEnvironment(hydrogen_env->closure(),
hydrogen_env->frame_type(),
ast_id,
hydrogen_env->parameter_count(),
argument_count_,
value_count,
outer,
hydrogen_env->entry(),
zone());
int argument_index = *argument_index_accumulator;
// Store the environment description into the environment
// (with holes for nested objects)
for (int i = 0; i < hydrogen_env->length(); ++i) {
if (hydrogen_env->is_special_index(i)) continue;
LOperand* op;
HValue* value = hydrogen_env->values()->at(i);
if (value->IsArgumentsObject() || value->IsCapturedObject()) {
op = LEnvironment::materialization_marker();
} else if (value->IsPushArgument()) {
op = new(zone()) LArgument(argument_index++);
} else {
op = UseAny(value);
}
result->AddValue(op,
value->representation(),
value->CheckFlag(HInstruction::kUint32));
}
// Recursively store the nested objects into the environment
for (int i = 0; i < hydrogen_env->length(); ++i) {
if (hydrogen_env->is_special_index(i)) continue;
HValue* value = hydrogen_env->values()->at(i);
if (value->IsArgumentsObject() || value->IsCapturedObject()) {
AddObjectToMaterialize(value, objects_to_materialize, result);
}
}
if (hydrogen_env->frame_type() == JS_FUNCTION) {
*argument_index_accumulator = argument_index;
}
return result;
}
// Add an object to the supplied environment and object materialization list.
//
// Notes:
//
// We are building three lists here:
//
// 1. In the result->object_mapping_ list (added to by the
// LEnvironment::Add*Object methods), we store the lengths (number
// of fields) of the captured objects in depth-first traversal order, or
// in case of duplicated objects, we store the index to the duplicate object
// (with a tag to differentiate between captured and duplicated objects).
//
// 2. The object fields are stored in the result->values_ list
// (added to by the LEnvironment.AddValue method) sequentially as lists
// of fields with holes for nested objects (the holes will be expanded
// later by LCodegen::AddToTranslation according to the
// LEnvironment.object_mapping_ list).
//
// 3. The auxiliary objects_to_materialize array stores the hydrogen values
// in the same order as result->object_mapping_ list. This is used
// to detect duplicate values and calculate the corresponding object index.
void LChunkBuilderBase::AddObjectToMaterialize(HValue* value,
ZoneList<HValue*>* objects_to_materialize, LEnvironment* result) {
int object_index = objects_to_materialize->length();
// Store the hydrogen value into the de-duplication array
objects_to_materialize->Add(value, zone());
// Find out whether we are storing a duplicated value
int previously_materialized_object = -1;
for (int prev = 0; prev < object_index; ++prev) {
if (objects_to_materialize->at(prev) == value) {
previously_materialized_object = prev;
break;
}
}
// Store the captured object length (or duplicated object index)
// into the environment. For duplicated objects, we stop here.
int length = value->OperandCount();
bool is_arguments = value->IsArgumentsObject();
if (previously_materialized_object >= 0) {
result->AddDuplicateObject(previously_materialized_object);
return;
} else {
result->AddNewObject(is_arguments ? length - 1 : length, is_arguments);
}
// Store the captured object's fields into the environment
for (int i = is_arguments ? 1 : 0; i < length; ++i) {
LOperand* op;
HValue* arg_value = value->OperandAt(i);
if (arg_value->IsArgumentsObject() || arg_value->IsCapturedObject()) {
// Insert a hole for nested objects
op = LEnvironment::materialization_marker();
} else {
ASSERT(!arg_value->IsPushArgument());
// For ordinary values, tell the register allocator we need the value
// to be alive here
op = UseAny(arg_value);
}
result->AddValue(op,
arg_value->representation(),
arg_value->CheckFlag(HInstruction::kUint32));
}
// Recursively store all the nested captured objects into the environment
for (int i = is_arguments ? 1 : 0; i < length; ++i) {
HValue* arg_value = value->OperandAt(i);
if (arg_value->IsArgumentsObject() || arg_value->IsCapturedObject()) {
AddObjectToMaterialize(arg_value, objects_to_materialize, result);
}
}
}
LInstruction* LChunkBuilder::CheckElideControlInstruction( LInstruction* LChunkBuilder::CheckElideControlInstruction(
HControlInstruction* instr) { HControlInstruction* instr) {
HBasicBlock* successor; HBasicBlock* successor;

View File

@ -791,6 +791,35 @@ class LChunk : public ZoneObject {
}; };
class LChunkBuilderBase BASE_EMBEDDED {
public:
explicit LChunkBuilderBase(Zone* zone)
: argument_count_(0),
zone_(zone) { }
virtual ~LChunkBuilderBase() { }
protected:
// An input operand in register, stack slot or a constant operand.
// Will not be moved to a register even if one is freely available.
virtual MUST_USE_RESULT LOperand* UseAny(HValue* value) = 0;
LEnvironment* CreateEnvironment(HEnvironment* hydrogen_env,
int* argument_index_accumulator,
ZoneList<HValue*>* objects_to_materialize);
void AddObjectToMaterialize(HValue* value,
ZoneList<HValue*>* objects_to_materialize,
LEnvironment* result);
Zone* zone() const { return zone_; }
int argument_count_;
private:
Zone* zone_;
};
int StackSlotOffset(int index); int StackSlotOffset(int index);
enum NumberUntagDMode { enum NumberUntagDMode {

View File

@ -934,90 +934,6 @@ void LChunkBuilder::VisitInstruction(HInstruction* current) {
} }
LEnvironment* LChunkBuilder::CreateEnvironment(
HEnvironment* hydrogen_env,
int* argument_index_accumulator,
ZoneList<HValue*>* objects_to_materialize) {
if (hydrogen_env == NULL) return NULL;
LEnvironment* outer = CreateEnvironment(hydrogen_env->outer(),
argument_index_accumulator,
objects_to_materialize);
BailoutId ast_id = hydrogen_env->ast_id();
ASSERT(!ast_id.IsNone() ||
hydrogen_env->frame_type() != JS_FUNCTION);
int value_count = hydrogen_env->length() - hydrogen_env->specials_count();
LEnvironment* result = new(zone()) LEnvironment(
hydrogen_env->closure(),
hydrogen_env->frame_type(),
ast_id,
hydrogen_env->parameter_count(),
argument_count_,
value_count,
outer,
hydrogen_env->entry(),
zone());
int argument_index = *argument_index_accumulator;
int object_index = objects_to_materialize->length();
for (int i = 0; i < hydrogen_env->length(); ++i) {
if (hydrogen_env->is_special_index(i)) continue;
LOperand* op;
HValue* value = hydrogen_env->values()->at(i);
if (value->IsArgumentsObject() || value->IsCapturedObject()) {
objects_to_materialize->Add(value, zone());
op = LEnvironment::materialization_marker();
} else if (value->IsPushArgument()) {
op = new(zone()) LArgument(argument_index++);
} else {
op = UseAny(value);
}
result->AddValue(op,
value->representation(),
value->CheckFlag(HInstruction::kUint32));
}
for (int i = object_index; i < objects_to_materialize->length(); ++i) {
HValue* object_to_materialize = objects_to_materialize->at(i);
int previously_materialized_object = -1;
for (int prev = 0; prev < i; ++prev) {
if (objects_to_materialize->at(prev) == objects_to_materialize->at(i)) {
previously_materialized_object = prev;
break;
}
}
int length = object_to_materialize->OperandCount();
bool is_arguments = object_to_materialize->IsArgumentsObject();
if (previously_materialized_object >= 0) {
result->AddDuplicateObject(previously_materialized_object);
continue;
} else {
result->AddNewObject(is_arguments ? length - 1 : length, is_arguments);
}
for (int i = is_arguments ? 1 : 0; i < length; ++i) {
LOperand* op;
HValue* value = object_to_materialize->OperandAt(i);
if (value->IsArgumentsObject() || value->IsCapturedObject()) {
objects_to_materialize->Add(value, zone());
op = LEnvironment::materialization_marker();
} else {
ASSERT(!value->IsPushArgument());
op = UseAny(value);
}
result->AddValue(op,
value->representation(),
value->CheckFlag(HInstruction::kUint32));
}
}
if (hydrogen_env->frame_type() == JS_FUNCTION) {
*argument_index_accumulator = argument_index;
}
return result;
}
LInstruction* LChunkBuilder::DoGoto(HGoto* instr) { LInstruction* LChunkBuilder::DoGoto(HGoto* instr) {
return new(zone()) LGoto(instr->FirstSuccessor()); return new(zone()) LGoto(instr->FirstSuccessor());
} }

View File

@ -2673,18 +2673,17 @@ class LPlatformChunk V8_FINAL : public LChunk {
}; };
class LChunkBuilder V8_FINAL BASE_EMBEDDED { class LChunkBuilder V8_FINAL : public LChunkBuilderBase {
public: public:
LChunkBuilder(CompilationInfo* info, HGraph* graph, LAllocator* allocator) LChunkBuilder(CompilationInfo* info, HGraph* graph, LAllocator* allocator)
: chunk_(NULL), : LChunkBuilderBase(graph->zone()),
chunk_(NULL),
info_(info), info_(info),
graph_(graph), graph_(graph),
zone_(graph->zone()),
status_(UNUSED), status_(UNUSED),
current_instruction_(NULL), current_instruction_(NULL),
current_block_(NULL), current_block_(NULL),
next_block_(NULL), next_block_(NULL),
argument_count_(0),
allocator_(allocator), allocator_(allocator),
position_(RelocInfo::kNoPosition), position_(RelocInfo::kNoPosition),
instruction_pending_deoptimization_environment_(NULL), instruction_pending_deoptimization_environment_(NULL),
@ -2724,7 +2723,6 @@ class LChunkBuilder V8_FINAL BASE_EMBEDDED {
LPlatformChunk* chunk() const { return chunk_; } LPlatformChunk* chunk() const { return chunk_; }
CompilationInfo* info() const { return info_; } CompilationInfo* info() const { return info_; }
HGraph* graph() const { return graph_; } HGraph* graph() const { return graph_; }
Zone* zone() const { return zone_; }
bool is_unused() const { return status_ == UNUSED; } bool is_unused() const { return status_ == UNUSED; }
bool is_building() const { return status_ == BUILDING; } bool is_building() const { return status_ == BUILDING; }
@ -2774,7 +2772,7 @@ class LChunkBuilder V8_FINAL BASE_EMBEDDED {
// An input operand in register, stack slot or a constant operand. // An input operand in register, stack slot or a constant operand.
// Will not be moved to a register even if one is freely available. // Will not be moved to a register even if one is freely available.
MUST_USE_RESULT LOperand* UseAny(HValue* value); virtual MUST_USE_RESULT LOperand* UseAny(HValue* value) V8_OVERRIDE;
// Temporary operand that must be in a register. // Temporary operand that must be in a register.
MUST_USE_RESULT LUnallocated* TempRegister(); MUST_USE_RESULT LUnallocated* TempRegister();
@ -2812,10 +2810,6 @@ class LChunkBuilder V8_FINAL BASE_EMBEDDED {
HInstruction* hinstr, HInstruction* hinstr,
CanDeoptimize can_deoptimize = CANNOT_DEOPTIMIZE_EAGERLY); CanDeoptimize can_deoptimize = CANNOT_DEOPTIMIZE_EAGERLY);
LEnvironment* CreateEnvironment(HEnvironment* hydrogen_env,
int* argument_index_accumulator,
ZoneList<HValue*>* objects_to_materialize);
void VisitInstruction(HInstruction* current); void VisitInstruction(HInstruction* current);
void DoBasicBlock(HBasicBlock* block, HBasicBlock* next_block); void DoBasicBlock(HBasicBlock* block, HBasicBlock* next_block);
@ -2829,12 +2823,10 @@ class LChunkBuilder V8_FINAL BASE_EMBEDDED {
LPlatformChunk* chunk_; LPlatformChunk* chunk_;
CompilationInfo* info_; CompilationInfo* info_;
HGraph* const graph_; HGraph* const graph_;
Zone* zone_;
Status status_; Status status_;
HInstruction* current_instruction_; HInstruction* current_instruction_;
HBasicBlock* current_block_; HBasicBlock* current_block_;
HBasicBlock* next_block_; HBasicBlock* next_block_;
int argument_count_;
LAllocator* allocator_; LAllocator* allocator_;
int position_; int position_;
LInstruction* instruction_pending_deoptimization_environment_; LInstruction* instruction_pending_deoptimization_environment_;

View File

@ -936,90 +936,6 @@ void LChunkBuilder::VisitInstruction(HInstruction* current) {
} }
LEnvironment* LChunkBuilder::CreateEnvironment(
HEnvironment* hydrogen_env,
int* argument_index_accumulator,
ZoneList<HValue*>* objects_to_materialize) {
if (hydrogen_env == NULL) return NULL;
LEnvironment* outer = CreateEnvironment(hydrogen_env->outer(),
argument_index_accumulator,
objects_to_materialize);
BailoutId ast_id = hydrogen_env->ast_id();
ASSERT(!ast_id.IsNone() ||
hydrogen_env->frame_type() != JS_FUNCTION);
int value_count = hydrogen_env->length() - hydrogen_env->specials_count();
LEnvironment* result = new(zone()) LEnvironment(
hydrogen_env->closure(),
hydrogen_env->frame_type(),
ast_id,
hydrogen_env->parameter_count(),
argument_count_,
value_count,
outer,
hydrogen_env->entry(),
zone());
int argument_index = *argument_index_accumulator;
int object_index = objects_to_materialize->length();
for (int i = 0; i < hydrogen_env->length(); ++i) {
if (hydrogen_env->is_special_index(i)) continue;
LOperand* op;
HValue* value = hydrogen_env->values()->at(i);
if (value->IsArgumentsObject() || value->IsCapturedObject()) {
objects_to_materialize->Add(value, zone());
op = LEnvironment::materialization_marker();
} else if (value->IsPushArgument()) {
op = new(zone()) LArgument(argument_index++);
} else {
op = UseAny(value);
}
result->AddValue(op,
value->representation(),
value->CheckFlag(HInstruction::kUint32));
}
for (int i = object_index; i < objects_to_materialize->length(); ++i) {
HValue* object_to_materialize = objects_to_materialize->at(i);
int previously_materialized_object = -1;
for (int prev = 0; prev < i; ++prev) {
if (objects_to_materialize->at(prev) == objects_to_materialize->at(i)) {
previously_materialized_object = prev;
break;
}
}
int length = object_to_materialize->OperandCount();
bool is_arguments = object_to_materialize->IsArgumentsObject();
if (previously_materialized_object >= 0) {
result->AddDuplicateObject(previously_materialized_object);
continue;
} else {
result->AddNewObject(is_arguments ? length - 1 : length, is_arguments);
}
for (int i = is_arguments ? 1 : 0; i < length; ++i) {
LOperand* op;
HValue* value = object_to_materialize->OperandAt(i);
if (value->IsArgumentsObject() || value->IsCapturedObject()) {
objects_to_materialize->Add(value, zone());
op = LEnvironment::materialization_marker();
} else {
ASSERT(!value->IsPushArgument());
op = UseAny(value);
}
result->AddValue(op,
value->representation(),
value->CheckFlag(HInstruction::kUint32));
}
}
if (hydrogen_env->frame_type() == JS_FUNCTION) {
*argument_index_accumulator = argument_index;
}
return result;
}
LInstruction* LChunkBuilder::DoGoto(HGoto* instr) { LInstruction* LChunkBuilder::DoGoto(HGoto* instr) {
return new(zone()) LGoto(instr->FirstSuccessor()); return new(zone()) LGoto(instr->FirstSuccessor());
} }

View File

@ -2630,18 +2630,17 @@ class LPlatformChunk V8_FINAL : public LChunk {
}; };
class LChunkBuilder V8_FINAL BASE_EMBEDDED { class LChunkBuilder V8_FINAL : public LChunkBuilderBase {
public: public:
LChunkBuilder(CompilationInfo* info, HGraph* graph, LAllocator* allocator) LChunkBuilder(CompilationInfo* info, HGraph* graph, LAllocator* allocator)
: chunk_(NULL), : LChunkBuilderBase(graph->zone()),
chunk_(NULL),
info_(info), info_(info),
graph_(graph), graph_(graph),
zone_(graph->zone()),
status_(UNUSED), status_(UNUSED),
current_instruction_(NULL), current_instruction_(NULL),
current_block_(NULL), current_block_(NULL),
next_block_(NULL), next_block_(NULL),
argument_count_(0),
allocator_(allocator), allocator_(allocator),
instruction_pending_deoptimization_environment_(NULL), instruction_pending_deoptimization_environment_(NULL),
pending_deoptimization_ast_id_(BailoutId::None()) { } pending_deoptimization_ast_id_(BailoutId::None()) { }
@ -2677,7 +2676,6 @@ class LChunkBuilder V8_FINAL BASE_EMBEDDED {
LPlatformChunk* chunk() const { return chunk_; } LPlatformChunk* chunk() const { return chunk_; }
CompilationInfo* info() const { return info_; } CompilationInfo* info() const { return info_; }
HGraph* graph() const { return graph_; } HGraph* graph() const { return graph_; }
Zone* zone() const { return zone_; }
bool is_unused() const { return status_ == UNUSED; } bool is_unused() const { return status_ == UNUSED; }
bool is_building() const { return status_ == BUILDING; } bool is_building() const { return status_ == BUILDING; }
@ -2730,7 +2728,7 @@ class LChunkBuilder V8_FINAL BASE_EMBEDDED {
// An input operand in register, stack slot or a constant operand. // An input operand in register, stack slot or a constant operand.
// Will not be moved to a register even if one is freely available. // Will not be moved to a register even if one is freely available.
MUST_USE_RESULT LOperand* UseAny(HValue* value); virtual MUST_USE_RESULT LOperand* UseAny(HValue* value) V8_OVERRIDE;
// Temporary operand that must be in a register. // Temporary operand that must be in a register.
MUST_USE_RESULT LUnallocated* TempRegister(); MUST_USE_RESULT LUnallocated* TempRegister();
@ -2772,10 +2770,6 @@ class LChunkBuilder V8_FINAL BASE_EMBEDDED {
HInstruction* hinstr, HInstruction* hinstr,
CanDeoptimize can_deoptimize = CANNOT_DEOPTIMIZE_EAGERLY); CanDeoptimize can_deoptimize = CANNOT_DEOPTIMIZE_EAGERLY);
LEnvironment* CreateEnvironment(HEnvironment* hydrogen_env,
int* argument_index_accumulator,
ZoneList<HValue*>* objects_to_materialize);
void VisitInstruction(HInstruction* current); void VisitInstruction(HInstruction* current);
void DoBasicBlock(HBasicBlock* block, HBasicBlock* next_block); void DoBasicBlock(HBasicBlock* block, HBasicBlock* next_block);
@ -2788,12 +2782,10 @@ class LChunkBuilder V8_FINAL BASE_EMBEDDED {
LPlatformChunk* chunk_; LPlatformChunk* chunk_;
CompilationInfo* info_; CompilationInfo* info_;
HGraph* const graph_; HGraph* const graph_;
Zone* zone_;
Status status_; Status status_;
HInstruction* current_instruction_; HInstruction* current_instruction_;
HBasicBlock* current_block_; HBasicBlock* current_block_;
HBasicBlock* next_block_; HBasicBlock* next_block_;
int argument_count_;
LAllocator* allocator_; LAllocator* allocator_;
LInstruction* instruction_pending_deoptimization_environment_; LInstruction* instruction_pending_deoptimization_environment_;
BailoutId pending_deoptimization_ast_id_; BailoutId pending_deoptimization_ast_id_;

View File

@ -303,6 +303,101 @@
})(); })();
// Test non-shallow nested graph of captured objects with duplicates
(function testDeepDuplicate() {
function constructor1() {
this.x = 23;
}
function constructor2(nested) {
this.a = 17;
this.b = nested;
this.c = 42;
}
function deep(shouldDeopt) {
var o1 = new constructor1();
var o2 = new constructor2(o1);
var o3 = new constructor2(o1);
assertEquals(17, o2.a);
assertEquals(23, o2.b.x);
assertEquals(42, o2.c);
o3.c = 54;
o1.x = 99;
if (shouldDeopt) %DeoptimizeFunction(deep);
assertEquals(99, o1.x);
assertEquals(99, o2.b.x);
assertEquals(99, o3.b.x);
assertEquals(54, o3.c);
assertEquals(17, o3.a);
assertEquals(42, o2.c);
assertEquals(17, o2.a);
o3.b.x = 1;
assertEquals(1, o1.x);
}
deep(false); deep(false);
%OptimizeFunctionOnNextCall(deep);
deep(false); deep(false);
deep(true); deep(true);
})();
// Test non-shallow nested graph of captured objects with inline
(function testDeepInline() {
function h() {
return { y : 3 };
}
function g(x) {
var u = { x : h() };
%DeoptimizeFunction(f);
return u;
}
function f() {
var l = { dummy : { } };
var r = g(l);
assertEquals(3, r.x.y);
}
f(); f(); f();
%OptimizeFunctionOnNextCall(f);
f();
})();
// Test two nested objects
(function testTwoNestedObjects() {
function f() {
var l = { x : { y : 111 } };
var l2 = { x : { y : 111 } };
%DeoptimizeFunction(f);
assertEquals(111, l.x.y);
assertEquals(111, l2.x.y);
}
f(); f(); f();
%OptimizeFunctionOnNextCall(f);
f();
})();
// Test a nested object and a duplicate
(function testTwoObjectsWithDuplicate() {
function f() {
var l = { x : { y : 111 } };
var dummy = { d : 0 };
var l2 = l.x;
%DeoptimizeFunction(f);
assertEquals(111, l.x.y);
assertEquals(111, l2.y);
assertEquals(0, dummy.d);
}
f(); f(); f();
%OptimizeFunctionOnNextCall(f);
f();
})();
// Test materialization of a field that requires a Smi value. // Test materialization of a field that requires a Smi value.
(function testSmiField() { (function testSmiField() {
var deopt = { deopt:false }; var deopt = { deopt:false };