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:
parent
d8e9893c59
commit
acf24331e3
@ -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) {
|
||||
return new(zone()) LGoto(instr->FirstSuccessor());
|
||||
}
|
||||
|
@ -2697,18 +2697,17 @@ class LPlatformChunk V8_FINAL : public LChunk {
|
||||
};
|
||||
|
||||
|
||||
class LChunkBuilder V8_FINAL BASE_EMBEDDED {
|
||||
class LChunkBuilder V8_FINAL : public LChunkBuilderBase {
|
||||
public:
|
||||
LChunkBuilder(CompilationInfo* info, HGraph* graph, LAllocator* allocator)
|
||||
: chunk_(NULL),
|
||||
: LChunkBuilderBase(graph->zone()),
|
||||
chunk_(NULL),
|
||||
info_(info),
|
||||
graph_(graph),
|
||||
zone_(graph->zone()),
|
||||
status_(UNUSED),
|
||||
current_instruction_(NULL),
|
||||
current_block_(NULL),
|
||||
next_block_(NULL),
|
||||
argument_count_(0),
|
||||
allocator_(allocator),
|
||||
position_(RelocInfo::kNoPosition),
|
||||
instruction_pending_deoptimization_environment_(NULL),
|
||||
@ -2750,7 +2749,6 @@ class LChunkBuilder V8_FINAL BASE_EMBEDDED {
|
||||
LPlatformChunk* chunk() const { return chunk_; }
|
||||
CompilationInfo* info() const { return info_; }
|
||||
HGraph* graph() const { return graph_; }
|
||||
Zone* zone() const { return zone_; }
|
||||
|
||||
bool is_unused() const { return status_ == UNUSED; }
|
||||
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.
|
||||
// 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.
|
||||
MUST_USE_RESULT LUnallocated* TempRegister();
|
||||
@ -2838,10 +2836,6 @@ class LChunkBuilder V8_FINAL BASE_EMBEDDED {
|
||||
HInstruction* hinstr,
|
||||
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 DoBasicBlock(HBasicBlock* block, HBasicBlock* next_block);
|
||||
@ -2854,12 +2848,10 @@ class LChunkBuilder V8_FINAL BASE_EMBEDDED {
|
||||
LPlatformChunk* chunk_;
|
||||
CompilationInfo* info_;
|
||||
HGraph* const graph_;
|
||||
Zone* zone_;
|
||||
Status status_;
|
||||
HInstruction* current_instruction_;
|
||||
HBasicBlock* current_block_;
|
||||
HBasicBlock* next_block_;
|
||||
int argument_count_;
|
||||
LAllocator* allocator_;
|
||||
int position_;
|
||||
LInstruction* instruction_pending_deoptimization_environment_;
|
||||
|
@ -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) {
|
||||
return new(zone()) LGoto(instr->FirstSuccessor());
|
||||
}
|
||||
|
@ -2712,18 +2712,17 @@ class LPlatformChunk V8_FINAL : public LChunk {
|
||||
};
|
||||
|
||||
|
||||
class LChunkBuilder V8_FINAL BASE_EMBEDDED {
|
||||
class LChunkBuilder V8_FINAL : public LChunkBuilderBase {
|
||||
public:
|
||||
LChunkBuilder(CompilationInfo* info, HGraph* graph, LAllocator* allocator)
|
||||
: chunk_(NULL),
|
||||
: LChunkBuilderBase(graph->zone()),
|
||||
chunk_(NULL),
|
||||
info_(info),
|
||||
graph_(graph),
|
||||
zone_(graph->zone()),
|
||||
status_(UNUSED),
|
||||
current_instruction_(NULL),
|
||||
current_block_(NULL),
|
||||
next_block_(NULL),
|
||||
argument_count_(0),
|
||||
allocator_(allocator),
|
||||
instruction_pending_deoptimization_environment_(NULL),
|
||||
pending_deoptimization_ast_id_(BailoutId::None()) { }
|
||||
@ -2759,7 +2758,6 @@ class LChunkBuilder V8_FINAL BASE_EMBEDDED {
|
||||
LPlatformChunk* chunk() const { return chunk_; }
|
||||
CompilationInfo* info() const { return info_; }
|
||||
HGraph* graph() const { return graph_; }
|
||||
Zone* zone() const { return zone_; }
|
||||
|
||||
bool is_unused() const { return status_ == UNUSED; }
|
||||
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.
|
||||
// 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.
|
||||
MUST_USE_RESULT LUnallocated* TempRegister();
|
||||
@ -2860,10 +2858,6 @@ class LChunkBuilder V8_FINAL BASE_EMBEDDED {
|
||||
HInstruction* hinstr,
|
||||
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 DoBasicBlock(HBasicBlock* block, HBasicBlock* next_block);
|
||||
@ -2878,12 +2872,10 @@ class LChunkBuilder V8_FINAL BASE_EMBEDDED {
|
||||
LPlatformChunk* chunk_;
|
||||
CompilationInfo* info_;
|
||||
HGraph* const graph_;
|
||||
Zone* zone_;
|
||||
Status status_;
|
||||
HInstruction* current_instruction_;
|
||||
HBasicBlock* current_block_;
|
||||
HBasicBlock* next_block_;
|
||||
int argument_count_;
|
||||
LAllocator* allocator_;
|
||||
LInstruction* instruction_pending_deoptimization_environment_;
|
||||
BailoutId pending_deoptimization_ast_id_;
|
||||
|
133
src/lithium.cc
133
src/lithium.cc
@ -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(
|
||||
HControlInstruction* instr) {
|
||||
HBasicBlock* successor;
|
||||
|
@ -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);
|
||||
|
||||
enum NumberUntagDMode {
|
||||
|
@ -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) {
|
||||
return new(zone()) LGoto(instr->FirstSuccessor());
|
||||
}
|
||||
|
@ -2673,18 +2673,17 @@ class LPlatformChunk V8_FINAL : public LChunk {
|
||||
};
|
||||
|
||||
|
||||
class LChunkBuilder V8_FINAL BASE_EMBEDDED {
|
||||
class LChunkBuilder V8_FINAL : public LChunkBuilderBase {
|
||||
public:
|
||||
LChunkBuilder(CompilationInfo* info, HGraph* graph, LAllocator* allocator)
|
||||
: chunk_(NULL),
|
||||
: LChunkBuilderBase(graph->zone()),
|
||||
chunk_(NULL),
|
||||
info_(info),
|
||||
graph_(graph),
|
||||
zone_(graph->zone()),
|
||||
status_(UNUSED),
|
||||
current_instruction_(NULL),
|
||||
current_block_(NULL),
|
||||
next_block_(NULL),
|
||||
argument_count_(0),
|
||||
allocator_(allocator),
|
||||
position_(RelocInfo::kNoPosition),
|
||||
instruction_pending_deoptimization_environment_(NULL),
|
||||
@ -2724,7 +2723,6 @@ class LChunkBuilder V8_FINAL BASE_EMBEDDED {
|
||||
LPlatformChunk* chunk() const { return chunk_; }
|
||||
CompilationInfo* info() const { return info_; }
|
||||
HGraph* graph() const { return graph_; }
|
||||
Zone* zone() const { return zone_; }
|
||||
|
||||
bool is_unused() const { return status_ == UNUSED; }
|
||||
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.
|
||||
// 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.
|
||||
MUST_USE_RESULT LUnallocated* TempRegister();
|
||||
@ -2812,10 +2810,6 @@ class LChunkBuilder V8_FINAL BASE_EMBEDDED {
|
||||
HInstruction* hinstr,
|
||||
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 DoBasicBlock(HBasicBlock* block, HBasicBlock* next_block);
|
||||
@ -2829,12 +2823,10 @@ class LChunkBuilder V8_FINAL BASE_EMBEDDED {
|
||||
LPlatformChunk* chunk_;
|
||||
CompilationInfo* info_;
|
||||
HGraph* const graph_;
|
||||
Zone* zone_;
|
||||
Status status_;
|
||||
HInstruction* current_instruction_;
|
||||
HBasicBlock* current_block_;
|
||||
HBasicBlock* next_block_;
|
||||
int argument_count_;
|
||||
LAllocator* allocator_;
|
||||
int position_;
|
||||
LInstruction* instruction_pending_deoptimization_environment_;
|
||||
|
@ -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) {
|
||||
return new(zone()) LGoto(instr->FirstSuccessor());
|
||||
}
|
||||
|
@ -2630,18 +2630,17 @@ class LPlatformChunk V8_FINAL : public LChunk {
|
||||
};
|
||||
|
||||
|
||||
class LChunkBuilder V8_FINAL BASE_EMBEDDED {
|
||||
class LChunkBuilder V8_FINAL : public LChunkBuilderBase {
|
||||
public:
|
||||
LChunkBuilder(CompilationInfo* info, HGraph* graph, LAllocator* allocator)
|
||||
: chunk_(NULL),
|
||||
: LChunkBuilderBase(graph->zone()),
|
||||
chunk_(NULL),
|
||||
info_(info),
|
||||
graph_(graph),
|
||||
zone_(graph->zone()),
|
||||
status_(UNUSED),
|
||||
current_instruction_(NULL),
|
||||
current_block_(NULL),
|
||||
next_block_(NULL),
|
||||
argument_count_(0),
|
||||
allocator_(allocator),
|
||||
instruction_pending_deoptimization_environment_(NULL),
|
||||
pending_deoptimization_ast_id_(BailoutId::None()) { }
|
||||
@ -2677,7 +2676,6 @@ class LChunkBuilder V8_FINAL BASE_EMBEDDED {
|
||||
LPlatformChunk* chunk() const { return chunk_; }
|
||||
CompilationInfo* info() const { return info_; }
|
||||
HGraph* graph() const { return graph_; }
|
||||
Zone* zone() const { return zone_; }
|
||||
|
||||
bool is_unused() const { return status_ == UNUSED; }
|
||||
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.
|
||||
// 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.
|
||||
MUST_USE_RESULT LUnallocated* TempRegister();
|
||||
@ -2772,10 +2770,6 @@ class LChunkBuilder V8_FINAL BASE_EMBEDDED {
|
||||
HInstruction* hinstr,
|
||||
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 DoBasicBlock(HBasicBlock* block, HBasicBlock* next_block);
|
||||
@ -2788,12 +2782,10 @@ class LChunkBuilder V8_FINAL BASE_EMBEDDED {
|
||||
LPlatformChunk* chunk_;
|
||||
CompilationInfo* info_;
|
||||
HGraph* const graph_;
|
||||
Zone* zone_;
|
||||
Status status_;
|
||||
HInstruction* current_instruction_;
|
||||
HBasicBlock* current_block_;
|
||||
HBasicBlock* next_block_;
|
||||
int argument_count_;
|
||||
LAllocator* allocator_;
|
||||
LInstruction* instruction_pending_deoptimization_environment_;
|
||||
BailoutId pending_deoptimization_ast_id_;
|
||||
|
@ -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.
|
||||
(function testSmiField() {
|
||||
var deopt = { deopt:false };
|
||||
|
Loading…
Reference in New Issue
Block a user