Commit 3b473d7a authored by sigurds's avatar sigurds Committed by Commit bot

[turbofan] Deopt support for escape analysis

Deopt support is added on two levels. On the IR level,
a new ObjectState node is added, which represenents an
object to be materialized. ObjectState nodes appear as
inputs of FrameState and StateValues nodes. On the
instruction select/code-generation level, the
FrameStateDescriptor class handles the nesting
introduced by ObjectState, and ensures that deopt code
with CAPTURED_OBJECT/DUPLICATED_OBJECT entries are
generated similarly to what crankshaft's escape
analysis does.

Two unittests test correctness of the IR level implementation.

Correctness for instruction selection / code generation
is tested by mjsunit tests.

R=jarin@chromium.org,mstarzinger@chromium.org
BUG=v8:4586
LOG=n

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

Cr-Commit-Position: refs/heads/master@{#33115}
parent 06738d64
...@@ -216,8 +216,10 @@ Handle<Code> CodeGenerator::GenerateCode() { ...@@ -216,8 +216,10 @@ Handle<Code> CodeGenerator::GenerateCode() {
bool CodeGenerator::IsNextInAssemblyOrder(RpoNumber block) const { bool CodeGenerator::IsNextInAssemblyOrder(RpoNumber block) const {
return code()->InstructionBlockAt(current_block_)->ao_number().IsNext( return code()
code()->InstructionBlockAt(block)->ao_number()); ->InstructionBlockAt(current_block_)
->ao_number()
.IsNext(code()->InstructionBlockAt(block)->ao_number());
} }
...@@ -489,64 +491,84 @@ FrameStateDescriptor* CodeGenerator::GetFrameStateDescriptor( ...@@ -489,64 +491,84 @@ FrameStateDescriptor* CodeGenerator::GetFrameStateDescriptor(
} }
namespace { void CodeGenerator::TranslateStateValueDescriptor(
StateValueDescriptor* desc, Translation* translation,
struct OperandAndType { InstructionOperandIterator* iter) {
InstructionOperand* const operand; if (desc->IsNested()) {
MachineType const type; translation->BeginCapturedObject(static_cast<int>(desc->size()));
}; for (size_t index = 0; index < desc->fields().size(); index++) {
TranslateStateValueDescriptor(&desc->fields()[index], translation, iter);
}
} else if (desc->IsDuplicate()) {
translation->DuplicateObject(static_cast<int>(desc->id()));
} else {
DCHECK(desc->IsPlain());
AddTranslationForOperand(translation, iter->instruction(), iter->Advance(),
desc->type());
}
}
OperandAndType TypedOperandForFrameState(FrameStateDescriptor* descriptor, void CodeGenerator::TranslateFrameStateDescriptorOperands(
Instruction* instr, FrameStateDescriptor* desc, InstructionOperandIterator* iter,
size_t frame_state_offset, OutputFrameStateCombine combine, Translation* translation) {
size_t index, for (size_t index = 0; index < desc->GetSize(combine); index++) {
OutputFrameStateCombine combine) { switch (combine.kind()) {
DCHECK(index < descriptor->GetSize(combine)); case OutputFrameStateCombine::kPushOutput: {
switch (combine.kind()) { DCHECK(combine.GetPushCount() <= iter->instruction()->OutputCount());
case OutputFrameStateCombine::kPushOutput: { size_t size_without_output =
DCHECK(combine.GetPushCount() <= instr->OutputCount()); desc->GetSize(OutputFrameStateCombine::Ignore());
size_t size_without_output = // If the index is past the existing stack items in values_.
descriptor->GetSize(OutputFrameStateCombine::Ignore()); if (index >= size_without_output) {
// If the index is past the existing stack items, return the output. // Materialize the result of the call instruction in this slot.
if (index >= size_without_output) { AddTranslationForOperand(
return {instr->OutputAt(index - size_without_output), translation, iter->instruction(),
MachineType::AnyTagged()}; iter->instruction()->OutputAt(index - size_without_output),
MachineType::AnyTagged());
continue;
}
break;
} }
break; case OutputFrameStateCombine::kPokeAt:
// The result of the call should be placed at position
// [index_from_top] in the stack (overwriting whatever was
// previously there).
size_t index_from_top =
desc->GetSize(combine) - 1 - combine.GetOffsetToPokeAt();
if (index >= index_from_top &&
index < index_from_top + iter->instruction()->OutputCount()) {
AddTranslationForOperand(
translation, iter->instruction(),
iter->instruction()->OutputAt(index - index_from_top),
MachineType::AnyTagged());
iter->Advance(); // We do not use this input, but we need to
// advace, as the input got replaced.
continue;
}
break;
} }
case OutputFrameStateCombine::kPokeAt: StateValueDescriptor* value_desc = desc->GetStateValueDescriptor();
size_t index_from_top = TranslateStateValueDescriptor(&value_desc->fields()[index], translation,
descriptor->GetSize(combine) - 1 - combine.GetOffsetToPokeAt(); iter);
if (index >= index_from_top &&
index < index_from_top + instr->OutputCount()) {
return {instr->OutputAt(index - index_from_top),
MachineType::AnyTagged()};
}
break;
} }
return {instr->InputAt(frame_state_offset + index),
descriptor->GetType(index)};
} }
} // namespace
void CodeGenerator::BuildTranslationForFrameStateDescriptor( void CodeGenerator::BuildTranslationForFrameStateDescriptor(
FrameStateDescriptor* descriptor, Instruction* instr, FrameStateDescriptor* descriptor, InstructionOperandIterator* iter,
Translation* translation, size_t frame_state_offset, Translation* translation, OutputFrameStateCombine state_combine) {
OutputFrameStateCombine state_combine) {
// Outer-most state must be added to translation first. // Outer-most state must be added to translation first.
if (descriptor->outer_state() != nullptr) { if (descriptor->outer_state() != nullptr) {
BuildTranslationForFrameStateDescriptor(descriptor->outer_state(), instr, BuildTranslationForFrameStateDescriptor(descriptor->outer_state(), iter,
translation, frame_state_offset, translation,
OutputFrameStateCombine::Ignore()); OutputFrameStateCombine::Ignore());
} }
frame_state_offset += descriptor->outer_state()->GetTotalSize();
Handle<SharedFunctionInfo> shared_info; Handle<SharedFunctionInfo> shared_info;
if (!descriptor->shared_info().ToHandle(&shared_info)) { if (!descriptor->shared_info().ToHandle(&shared_info)) {
if (!info()->has_shared_info()) return; // Stub with no SharedFunctionInfo. if (!info()->has_shared_info()) {
return; // Stub with no SharedFunctionInfo.
}
shared_info = info()->shared_info(); shared_info = info()->shared_info();
} }
int shared_info_id = DefineDeoptimizationLiteral(shared_info); int shared_info_id = DefineDeoptimizationLiteral(shared_info);
...@@ -575,11 +597,8 @@ void CodeGenerator::BuildTranslationForFrameStateDescriptor( ...@@ -575,11 +597,8 @@ void CodeGenerator::BuildTranslationForFrameStateDescriptor(
break; break;
} }
for (size_t i = 0; i < descriptor->GetSize(state_combine); i++) { TranslateFrameStateDescriptorOperands(descriptor, iter, state_combine,
OperandAndType op = TypedOperandForFrameState( translation);
descriptor, instr, frame_state_offset, i, state_combine);
AddTranslationForOperand(translation, instr, op.operand, op.type);
}
} }
...@@ -593,8 +612,9 @@ int CodeGenerator::BuildTranslation(Instruction* instr, int pc_offset, ...@@ -593,8 +612,9 @@ int CodeGenerator::BuildTranslation(Instruction* instr, int pc_offset,
Translation translation( Translation translation(
&translations_, static_cast<int>(descriptor->GetFrameCount()), &translations_, static_cast<int>(descriptor->GetFrameCount()),
static_cast<int>(descriptor->GetJSFrameCount()), zone()); static_cast<int>(descriptor->GetJSFrameCount()), zone());
BuildTranslationForFrameStateDescriptor(descriptor, instr, &translation, InstructionOperandIterator iter(instr, frame_state_offset);
frame_state_offset, state_combine); BuildTranslationForFrameStateDescriptor(descriptor, &iter, &translation,
state_combine);
int deoptimization_id = static_cast<int>(deoptimization_states_.size()); int deoptimization_id = static_cast<int>(deoptimization_states_.size());
......
...@@ -28,6 +28,20 @@ struct BranchInfo { ...@@ -28,6 +28,20 @@ struct BranchInfo {
}; };
class InstructionOperandIterator {
public:
InstructionOperandIterator(Instruction* instr, size_t pos)
: instr_(instr), pos_(pos) {}
Instruction* instruction() const { return instr_; }
InstructionOperand* Advance() { return instr_->InputAt(pos_++); }
private:
Instruction* instr_;
size_t pos_;
};
// Generates native code for a sequence of instructions. // Generates native code for a sequence of instructions.
class CodeGenerator final : public GapResolver::Assembler { class CodeGenerator final : public GapResolver::Assembler {
public: public:
...@@ -136,9 +150,15 @@ class CodeGenerator final : public GapResolver::Assembler { ...@@ -136,9 +150,15 @@ class CodeGenerator final : public GapResolver::Assembler {
size_t frame_access_state_offset, size_t frame_access_state_offset,
OutputFrameStateCombine state_combine); OutputFrameStateCombine state_combine);
void BuildTranslationForFrameStateDescriptor( void BuildTranslationForFrameStateDescriptor(
FrameStateDescriptor* descriptor, Instruction* instr, FrameStateDescriptor* descriptor, InstructionOperandIterator* iter,
Translation* translation, size_t frame_access_state_offset, Translation* translation, OutputFrameStateCombine state_combine);
OutputFrameStateCombine state_combine); void TranslateStateValueDescriptor(StateValueDescriptor* desc,
Translation* translation,
InstructionOperandIterator* iter);
void TranslateFrameStateDescriptorOperands(FrameStateDescriptor* desc,
InstructionOperandIterator* iter,
OutputFrameStateCombine combine,
Translation* translation);
void AddTranslationForOperand(Translation* translation, Instruction* instr, void AddTranslationForOperand(Translation* translation, Instruction* instr,
InstructionOperand* op, MachineType type); InstructionOperand* op, MachineType type);
void AddNopForSmiCodeInlining(); void AddNopForSmiCodeInlining();
......
...@@ -753,6 +753,14 @@ const Operator* CommonOperatorBuilder::StateValues(int arguments) { ...@@ -753,6 +753,14 @@ const Operator* CommonOperatorBuilder::StateValues(int arguments) {
} }
const Operator* CommonOperatorBuilder::ObjectState(int pointer_slots, int id) {
return new (zone()) Operator1<int>( // --
IrOpcode::kObjectState, Operator::kPure, // opcode
"ObjectState", // name
pointer_slots, 0, 0, 1, 0, 0, id); // counts
}
const Operator* CommonOperatorBuilder::TypedStateValues( const Operator* CommonOperatorBuilder::TypedStateValues(
const ZoneVector<MachineType>* types) { const ZoneVector<MachineType>* types) {
return new (zone()) Operator1<const ZoneVector<MachineType>*>( // -- return new (zone()) Operator1<const ZoneVector<MachineType>*>( // --
......
...@@ -166,6 +166,7 @@ class CommonOperatorBuilder final : public ZoneObject { ...@@ -166,6 +166,7 @@ class CommonOperatorBuilder final : public ZoneObject {
const Operator* BeginRegion(); const Operator* BeginRegion();
const Operator* FinishRegion(); const Operator* FinishRegion();
const Operator* StateValues(int arguments); const Operator* StateValues(int arguments);
const Operator* ObjectState(int pointer_slots, int id);
const Operator* TypedStateValues(const ZoneVector<MachineType>* types); const Operator* TypedStateValues(const ZoneVector<MachineType>* types);
const Operator* FrameState(BailoutId bailout_id, const Operator* FrameState(BailoutId bailout_id,
OutputFrameStateCombine state_combine, OutputFrameStateCombine state_combine,
......
...@@ -36,10 +36,13 @@ Reduction EscapeAnalysisReducer::Reduce(Node* node) { ...@@ -36,10 +36,13 @@ Reduction EscapeAnalysisReducer::Reduce(Node* node) {
return ReduceReferenceEqual(node); return ReduceReferenceEqual(node);
case IrOpcode::kObjectIsSmi: case IrOpcode::kObjectIsSmi:
return ReduceObjectIsSmi(node); return ReduceObjectIsSmi(node);
case IrOpcode::kStateValues:
case IrOpcode::kFrameState:
return ReplaceWithDeoptDummy(node);
default: default:
// TODO(sigurds): Change this to GetFrameStateInputCount once
// it is working. For now we use EffectInputCount > 0 to determine
// whether a node might have a frame state input.
if (node->op()->EffectInputCount() > 0) {
return ReduceFrameStateUses(node);
}
break; break;
} }
return NoChange(); return NoChange();
...@@ -117,23 +120,14 @@ Reduction EscapeAnalysisReducer::ReduceReferenceEqual(Node* node) { ...@@ -117,23 +120,14 @@ Reduction EscapeAnalysisReducer::ReduceReferenceEqual(Node* node) {
Node* left = NodeProperties::GetValueInput(node, 0); Node* left = NodeProperties::GetValueInput(node, 0);
Node* right = NodeProperties::GetValueInput(node, 1); Node* right = NodeProperties::GetValueInput(node, 1);
if (escape_analysis()->IsVirtual(left)) { if (escape_analysis()->IsVirtual(left)) {
if (escape_analysis()->IsVirtual(right)) { if (escape_analysis()->IsVirtual(right) &&
if (Node* rep = escape_analysis()->GetReplacement(left)) { escape_analysis()->CompareVirtualObjects(left, right)) {
left = rep; ReplaceWithValue(node, jsgraph()->TrueConstant());
} if (FLAG_trace_turbo_escape) {
if (Node* rep = escape_analysis()->GetReplacement(right)) { PrintF("Replaced ref eq #%d with true\n", node->id());
right = rep;
}
// TODO(sigurds): What to do if either is a PHI?
if (left == right) {
ReplaceWithValue(node, jsgraph()->TrueConstant());
if (FLAG_trace_turbo_escape) {
PrintF("Replaced ref eq #%d with true\n", node->id());
}
return Replace(node);
} }
} }
// Right-hand side is either not virtual, or a different node. // Right-hand side is not a virtual object, or a different one.
ReplaceWithValue(node, jsgraph()->FalseConstant()); ReplaceWithValue(node, jsgraph()->FalseConstant());
if (FLAG_trace_turbo_escape) { if (FLAG_trace_turbo_escape) {
PrintF("Replaced ref eq #%d with false\n", node->id()); PrintF("Replaced ref eq #%d with false\n", node->id());
...@@ -164,28 +158,132 @@ Reduction EscapeAnalysisReducer::ReduceObjectIsSmi(Node* node) { ...@@ -164,28 +158,132 @@ Reduction EscapeAnalysisReducer::ReduceObjectIsSmi(Node* node) {
} }
// TODO(sigurds): This is a temporary solution until escape analysis Reduction EscapeAnalysisReducer::ReduceFrameStateUses(Node* node) {
// supports deoptimization. DCHECK_GE(node->op()->EffectInputCount(), 1);
Reduction EscapeAnalysisReducer::ReplaceWithDeoptDummy(Node* node) { bool changed = false;
DCHECK(node->opcode() == IrOpcode::kStateValues || for (int i = 0; i < node->InputCount(); ++i) {
node->opcode() == IrOpcode::kFrameState); Node* input = node->InputAt(i);
Reduction r = NoChange(); if (input->opcode() == IrOpcode::kFrameState) {
if (Node* ret = ReduceFrameState(input, node, false)) {
node->ReplaceInput(i, ret);
changed = true;
}
}
}
if (changed) {
return Changed(node);
}
return NoChange();
}
// Returns the clone if it duplicated the node, and null otherwise.
Node* EscapeAnalysisReducer::ReduceFrameState(Node* node, Node* effect,
bool multiple_users) {
DCHECK(node->opcode() == IrOpcode::kFrameState);
if (FLAG_trace_turbo_escape) {
PrintF("Reducing FrameState %d\n", node->id());
}
Node* clone = nullptr;
for (int i = 0; i < node->op()->ValueInputCount(); ++i) { for (int i = 0; i < node->op()->ValueInputCount(); ++i) {
Node* input = NodeProperties::GetValueInput(node, i); Node* input = NodeProperties::GetValueInput(node, i);
if (input->opcode() == IrOpcode::kFinishRegion || Node* ret =
input->opcode() == IrOpcode::kAllocate || input->opcode() == IrOpcode::kStateValues
input->opcode() == IrOpcode::kPhi) { ? ReduceStateValueInputs(input, effect, node->UseCount() > 1)
if (escape_analysis()->IsVirtual(input)) { : ReduceStateValueInput(node, i, effect, node->UseCount() > 1);
NodeProperties::ReplaceValueInput(node, jsgraph()->UndefinedConstant(), if (ret) {
i); if (node->UseCount() > 1 || multiple_users) {
if (FLAG_trace_turbo_escape) {
PrintF(" Cloning #%d", node->id());
}
node = clone = jsgraph()->graph()->CloneNode(node);
if (FLAG_trace_turbo_escape) {
PrintF(" to #%d\n", node->id());
}
multiple_users = false; // Don't clone anymore.
}
NodeProperties::ReplaceValueInput(node, ret, i);
}
}
Node* outer_frame_state = NodeProperties::GetFrameStateInput(node, 0);
if (outer_frame_state->opcode() == IrOpcode::kFrameState) {
if (Node* ret =
ReduceFrameState(outer_frame_state, effect, node->UseCount() > 1)) {
if (node->UseCount() > 1 || multiple_users) {
if (FLAG_trace_turbo_escape) {
PrintF(" Cloning #%d", node->id());
}
node = clone = jsgraph()->graph()->CloneNode(node);
if (FLAG_trace_turbo_escape) {
PrintF(" to #%d\n", node->id());
}
multiple_users = false;
}
NodeProperties::ReplaceFrameStateInput(node, 0, ret);
}
}
return clone;
}
// Returns the clone if it duplicated the node, and null otherwise.
Node* EscapeAnalysisReducer::ReduceStateValueInputs(Node* node, Node* effect,
bool multiple_users) {
if (FLAG_trace_turbo_escape) {
PrintF("Reducing StateValue #%d\n", node->id());
}
DCHECK(node->opcode() == IrOpcode::kStateValues);
DCHECK_NOT_NULL(effect);
Node* clone = nullptr;
for (int i = 0; i < node->op()->ValueInputCount(); ++i) {
if (Node* ret = ReduceStateValueInput(node, i, effect, multiple_users)) {
node = ret;
DCHECK_NULL(clone);
clone = ret;
multiple_users = false;
}
}
return clone;
}
// Returns the clone if it duplicated the node, and null otherwise.
Node* EscapeAnalysisReducer::ReduceStateValueInput(Node* node, int node_index,
Node* effect,
bool multiple_users) {
Node* input = NodeProperties::GetValueInput(node, node_index);
if (FLAG_trace_turbo_escape) {
PrintF("Reducing State Input #%d (%s)\n", input->id(),
input->op()->mnemonic());
}
Node* clone = nullptr;
if (input->opcode() == IrOpcode::kFinishRegion ||
input->opcode() == IrOpcode::kAllocate) {
if (escape_analysis()->IsVirtual(input)) {
if (Node* object_state =
escape_analysis()->GetOrCreateObjectState(effect, input)) {
if (node->UseCount() > 1 || multiple_users) {
if (FLAG_trace_turbo_escape) {
PrintF("Cloning #%d", node->id());
}
node = clone = jsgraph()->graph()->CloneNode(node);
if (FLAG_trace_turbo_escape) {
PrintF(" to #%d\n", node->id());
}
}
NodeProperties::ReplaceValueInput(node, object_state, node_index);
if (FLAG_trace_turbo_escape) {
PrintF("Replaced state #%d input #%d with object state #%d\n",
node->id(), input->id(), object_state->id());
}
} else {
if (FLAG_trace_turbo_escape) { if (FLAG_trace_turbo_escape) {
PrintF("Replaced state value (#%d) input with dummy\n", node->id()); PrintF("No object state replacement available.\n");
} }
r = Changed(node);
} }
} }
} }
return r; return clone;
} }
......
...@@ -35,8 +35,11 @@ class EscapeAnalysisReducer final : public AdvancedReducer { ...@@ -35,8 +35,11 @@ class EscapeAnalysisReducer final : public AdvancedReducer {
Reduction ReduceFinishRegion(Node* node); Reduction ReduceFinishRegion(Node* node);
Reduction ReduceReferenceEqual(Node* node); Reduction ReduceReferenceEqual(Node* node);
Reduction ReduceObjectIsSmi(Node* node); Reduction ReduceObjectIsSmi(Node* node);
Reduction ReduceFrameStateUses(Node* node);
Reduction ReplaceWithDeoptDummy(Node* node); Node* ReduceFrameState(Node* node, Node* effect, bool multiple_users);
Node* ReduceStateValueInputs(Node* node, Node* effect, bool multiple_users);
Node* ReduceStateValueInput(Node* node, int node_index, Node* effect,
bool multiple_users);
JSGraph* jsgraph() const { return jsgraph_; } JSGraph* jsgraph() const { return jsgraph_; }
EscapeAnalysis* escape_analysis() const { return escape_analysis_; } EscapeAnalysis* escape_analysis() const { return escape_analysis_; }
......
...@@ -21,24 +21,29 @@ namespace v8 { ...@@ -21,24 +21,29 @@ namespace v8 {
namespace internal { namespace internal {
namespace compiler { namespace compiler {
// ------------------------------VirtualObject----------------------------------
class VirtualObject : public ZoneObject { class VirtualObject : public ZoneObject {
public: public:
enum Status { kUntracked = 0, kTracked = 1 }; enum Status { kUntracked = 0, kTracked = 1 };
VirtualObject(NodeId id, Zone* zone) VirtualObject(NodeId id, Zone* zone)
: id_(id), status_(kUntracked), fields_(zone), phi_(zone) {} : id_(id),
status_(kUntracked),
fields_(zone),
phi_(zone),
object_state_(nullptr) {}
VirtualObject(const VirtualObject& other) VirtualObject(const VirtualObject& other)
: id_(other.id_), : id_(other.id_),
status_(other.status_), status_(other.status_),
fields_(other.fields_), fields_(other.fields_),
phi_(other.phi_) {} phi_(other.phi_),
object_state_(other.object_state_) {}
VirtualObject(NodeId id, Zone* zone, size_t field_number) VirtualObject(NodeId id, Zone* zone, size_t field_number)
: id_(id), status_(kTracked), fields_(zone), phi_(zone) { : id_(id),
status_(kTracked),
fields_(zone),
phi_(zone),
object_state_(nullptr) {
fields_.resize(field_number); fields_.resize(field_number);
phi_.resize(field_number, false); phi_.resize(field_number, false);
} }
...@@ -92,6 +97,8 @@ class VirtualObject : public ZoneObject { ...@@ -92,6 +97,8 @@ class VirtualObject : public ZoneObject {
return changed; return changed;
} }
bool UpdateFrom(const VirtualObject& other); bool UpdateFrom(const VirtualObject& other);
void SetObjectState(Node* node) { object_state_ = node; }
Node* GetObjectState() { return object_state_; }
NodeId id() { return id_; } NodeId id() { return id_; }
void id(NodeId id) { id_ = id; } void id(NodeId id) { id_ = id; }
...@@ -101,6 +108,7 @@ class VirtualObject : public ZoneObject { ...@@ -101,6 +108,7 @@ class VirtualObject : public ZoneObject {
Status status_; Status status_;
ZoneVector<Node*> fields_; ZoneVector<Node*> fields_;
ZoneVector<bool> phi_; ZoneVector<bool> phi_;
Node* object_state_;
}; };
...@@ -121,9 +129,6 @@ bool VirtualObject::UpdateFrom(const VirtualObject& other) { ...@@ -121,9 +129,6 @@ bool VirtualObject::UpdateFrom(const VirtualObject& other) {
} }
// ------------------------------VirtualState-----------------------------------
class VirtualState : public ZoneObject { class VirtualState : public ZoneObject {
public: public:
VirtualState(Zone* zone, size_t size); VirtualState(Zone* zone, size_t size);
...@@ -224,15 +229,12 @@ Node* MergeCache::GetFields(size_t pos) { ...@@ -224,15 +229,12 @@ Node* MergeCache::GetFields(size_t pos) {
VirtualState::VirtualState(Zone* zone, size_t size) VirtualState::VirtualState(Zone* zone, size_t size)
: info_(zone), last_changed_(nullptr) { : info_(size, nullptr, zone), last_changed_(nullptr) {}
info_.resize(size);
}
VirtualState::VirtualState(const VirtualState& state) VirtualState::VirtualState(const VirtualState& state)
: info_(state.info_.get_allocator().zone()), : info_(state.info_.size(), nullptr, state.info_.get_allocator().zone()),
last_changed_(state.last_changed_) { last_changed_(state.last_changed_) {
info_.resize(state.info_.size());
for (size_t i = 0; i < state.info_.size(); ++i) { for (size_t i = 0; i < state.info_.size(); ++i) {
if (state.info_[i] && state.info_[i]->id() == i) { if (state.info_[i] && state.info_[i]->id() == i) {
info_[i] = new (state.info_.get_allocator().zone()) info_[i] = new (state.info_.get_allocator().zone())
...@@ -428,8 +430,10 @@ bool VirtualState::MergeFrom(MergeCache* cache, Zone* zone, Graph* graph, ...@@ -428,8 +430,10 @@ bool VirtualState::MergeFrom(MergeCache* cache, Zone* zone, Graph* graph,
} }
} }
if (rep->op()->ValueInputCount() != value_input_count) { if (rep->op()->ValueInputCount() != value_input_count) {
PrintF(" Widening Phi #%d of arity %d to %d", rep->id(), if (FLAG_trace_turbo_escape) {
rep->op()->ValueInputCount(), value_input_count); PrintF(" Widening Phi #%d of arity %d to %d", rep->id(),
rep->op()->ValueInputCount(), value_input_count);
}
NodeProperties::ChangeOp( NodeProperties::ChangeOp(
rep, common->Phi(MachineRepresentation::kTagged, rep, common->Phi(MachineRepresentation::kTagged,
value_input_count)); value_input_count));
...@@ -440,7 +444,6 @@ bool VirtualState::MergeFrom(MergeCache* cache, Zone* zone, Graph* graph, ...@@ -440,7 +444,6 @@ bool VirtualState::MergeFrom(MergeCache* cache, Zone* zone, Graph* graph,
} }
} }
} }
} else { } else {
SetVirtualObject(id, nullptr); SetVirtualObject(id, nullptr);
} }
...@@ -457,9 +460,6 @@ bool VirtualState::MergeFrom(MergeCache* cache, Zone* zone, Graph* graph, ...@@ -457,9 +460,6 @@ bool VirtualState::MergeFrom(MergeCache* cache, Zone* zone, Graph* graph,
} }
// ------------------------------EscapeStatusAnalysis---------------------------
EscapeStatusAnalysis::EscapeStatusAnalysis(EscapeAnalysis* object_analysis, EscapeStatusAnalysis::EscapeStatusAnalysis(EscapeAnalysis* object_analysis,
Graph* graph, Zone* zone) Graph* graph, Zone* zone)
: object_analysis_(object_analysis), : object_analysis_(object_analysis),
...@@ -571,6 +571,10 @@ void EscapeStatusAnalysis::Process(Node* node) { ...@@ -571,6 +571,10 @@ void EscapeStatusAnalysis::Process(Node* node) {
case IrOpcode::kPhi: case IrOpcode::kPhi:
if (!HasEntry(node)) { if (!HasEntry(node)) {
info_[node->id()] = kVirtual; info_[node->id()] = kVirtual;
if (!IsAllocationPhi(node)) {
SetEscaped(node);
RevisitUses(node);
}
} }
CheckUsesForEscape(node); CheckUsesForEscape(node);
default: default:
...@@ -579,6 +583,17 @@ void EscapeStatusAnalysis::Process(Node* node) { ...@@ -579,6 +583,17 @@ void EscapeStatusAnalysis::Process(Node* node) {
} }
bool EscapeStatusAnalysis::IsAllocationPhi(Node* node) {
for (Edge edge : node->input_edges()) {
Node* input = edge.to();
if (input->opcode() == IrOpcode::kPhi && !IsEscaped(input)) continue;
if (IsAllocation(input)) continue;
return false;
}
return true;
}
void EscapeStatusAnalysis::ProcessStoreField(Node* node) { void EscapeStatusAnalysis::ProcessStoreField(Node* node) {
DCHECK_EQ(node->opcode(), IrOpcode::kStoreField); DCHECK_EQ(node->opcode(), IrOpcode::kStoreField);
Node* to = NodeProperties::GetValueInput(node, 0); Node* to = NodeProperties::GetValueInput(node, 0);
...@@ -618,6 +633,10 @@ void EscapeStatusAnalysis::ProcessAllocate(Node* node) { ...@@ -618,6 +633,10 @@ void EscapeStatusAnalysis::ProcessAllocate(Node* node) {
node->op()->mnemonic()); node->op()->mnemonic());
} }
NumberMatcher size(node->InputAt(0)); NumberMatcher size(node->InputAt(0));
DCHECK(node->InputAt(0)->opcode() != IrOpcode::kInt32Constant &&
node->InputAt(0)->opcode() != IrOpcode::kInt64Constant &&
node->InputAt(0)->opcode() != IrOpcode::kFloat32Constant &&
node->InputAt(0)->opcode() != IrOpcode::kFloat64Constant);
if (!size.HasValue() && SetEscaped(node)) { if (!size.HasValue() && SetEscaped(node)) {
RevisitUses(node); RevisitUses(node);
if (FLAG_trace_turbo_escape) { if (FLAG_trace_turbo_escape) {
...@@ -724,9 +743,6 @@ void EscapeStatusAnalysis::DebugPrint() { ...@@ -724,9 +743,6 @@ void EscapeStatusAnalysis::DebugPrint() {
} }
// -----------------------------EscapeAnalysis----------------------------------
EscapeAnalysis::EscapeAnalysis(Graph* graph, CommonOperatorBuilder* common, EscapeAnalysis::EscapeAnalysis(Graph* graph, CommonOperatorBuilder* common,
Zone* zone) Zone* zone)
: graph_(graph), : graph_(graph),
...@@ -1005,6 +1021,10 @@ void EscapeAnalysis::ProcessAllocation(Node* node) { ...@@ -1005,6 +1021,10 @@ void EscapeAnalysis::ProcessAllocation(Node* node) {
if (virtual_states_[node->id()]->GetVirtualObject(node)) return; if (virtual_states_[node->id()]->GetVirtualObject(node)) return;
NumberMatcher size(node->InputAt(0)); NumberMatcher size(node->InputAt(0));
DCHECK(node->InputAt(0)->opcode() != IrOpcode::kInt32Constant &&
node->InputAt(0)->opcode() != IrOpcode::kInt64Constant &&
node->InputAt(0)->opcode() != IrOpcode::kFloat32Constant &&
node->InputAt(0)->opcode() != IrOpcode::kFloat64Constant);
if (size.HasValue()) { if (size.HasValue()) {
virtual_states_[node->id()]->SetVirtualObject( virtual_states_[node->id()]->SetVirtualObject(
node->id(), new (zone()) VirtualObject(node->id(), zone(), node->id(), new (zone()) VirtualObject(node->id(), zone(),
...@@ -1136,6 +1156,17 @@ VirtualObject* EscapeAnalysis::ResolveVirtualObject(VirtualState* state, ...@@ -1136,6 +1156,17 @@ VirtualObject* EscapeAnalysis::ResolveVirtualObject(VirtualState* state,
} }
bool EscapeAnalysis::CompareVirtualObjects(Node* left, Node* right) {
DCHECK(IsVirtual(left) && IsVirtual(right));
left = ResolveReplacement(left);
right = ResolveReplacement(right);
if (IsEquivalentPhi(left, right)) {
return true;
}
return false;
}
int EscapeAnalysis::OffsetFromAccess(Node* node) { int EscapeAnalysis::OffsetFromAccess(Node* node) {
DCHECK(OpParameter<FieldAccess>(node).offset % kPointerSize == 0); DCHECK(OpParameter<FieldAccess>(node).offset % kPointerSize == 0);
return OpParameter<FieldAccess>(node).offset / kPointerSize; return OpParameter<FieldAccess>(node).offset / kPointerSize;
...@@ -1215,6 +1246,10 @@ void EscapeAnalysis::ProcessLoadElement(Node* node) { ...@@ -1215,6 +1246,10 @@ void EscapeAnalysis::ProcessLoadElement(Node* node) {
VirtualState* state = virtual_states_[node->id()]; VirtualState* state = virtual_states_[node->id()];
Node* index_node = node->InputAt(1); Node* index_node = node->InputAt(1);
NumberMatcher index(index_node); NumberMatcher index(index_node);
DCHECK(index_node->opcode() != IrOpcode::kInt32Constant &&
index_node->opcode() != IrOpcode::kInt64Constant &&
index_node->opcode() != IrOpcode::kFloat32Constant &&
index_node->opcode() != IrOpcode::kFloat64Constant);
ElementAccess access = OpParameter<ElementAccess>(node); ElementAccess access = OpParameter<ElementAccess>(node);
if (index.HasValue()) { if (index.HasValue()) {
int offset = index.Value() + access.header_size / kPointerSize; int offset = index.Value() + access.header_size / kPointerSize;
...@@ -1273,6 +1308,10 @@ void EscapeAnalysis::ProcessStoreElement(Node* node) { ...@@ -1273,6 +1308,10 @@ void EscapeAnalysis::ProcessStoreElement(Node* node) {
Node* to = NodeProperties::GetValueInput(node, 0); Node* to = NodeProperties::GetValueInput(node, 0);
Node* index_node = node->InputAt(1); Node* index_node = node->InputAt(1);
NumberMatcher index(index_node); NumberMatcher index(index_node);
DCHECK(index_node->opcode() != IrOpcode::kInt32Constant &&
index_node->opcode() != IrOpcode::kInt64Constant &&
index_node->opcode() != IrOpcode::kFloat32Constant &&
index_node->opcode() != IrOpcode::kFloat64Constant);
ElementAccess access = OpParameter<ElementAccess>(node); ElementAccess access = OpParameter<ElementAccess>(node);
Node* val = NodeProperties::GetValueInput(node, 2); Node* val = NodeProperties::GetValueInput(node, 2);
if (index.HasValue()) { if (index.HasValue()) {
...@@ -1303,6 +1342,51 @@ void EscapeAnalysis::ProcessStoreElement(Node* node) { ...@@ -1303,6 +1342,51 @@ void EscapeAnalysis::ProcessStoreElement(Node* node) {
} }
Node* EscapeAnalysis::GetOrCreateObjectState(Node* effect, Node* node) {
if ((node->opcode() == IrOpcode::kFinishRegion ||
node->opcode() == IrOpcode::kAllocate) &&
IsVirtual(node)) {
if (VirtualObject* vobj =
ResolveVirtualObject(virtual_states_[effect->id()], node)) {
if (Node* object_state = vobj->GetObjectState()) {
return object_state;
} else {
cache_->fields().clear();
for (size_t i = 0; i < vobj->field_count(); ++i) {
if (Node* field = vobj->GetField(i)) {
cache_->fields().push_back(field);
}
}
int input_count = static_cast<int>(cache_->fields().size());
Node* new_object_state =
graph()->NewNode(common()->ObjectState(input_count, vobj->id()),
input_count, &cache_->fields().front());
vobj->SetObjectState(new_object_state);
if (FLAG_trace_turbo_escape) {
PrintF(
"Creating object state #%d for vobj %p (from node #%d) at effect "
"#%d\n",
new_object_state->id(), static_cast<void*>(vobj), node->id(),
effect->id());
}
// Now fix uses of other objects.
for (size_t i = 0; i < vobj->field_count(); ++i) {
if (Node* field = vobj->GetField(i)) {
if (Node* field_object_state =
GetOrCreateObjectState(effect, field)) {
NodeProperties::ReplaceValueInput(
new_object_state, field_object_state, static_cast<int>(i));
}
}
}
return new_object_state;
}
}
}
return nullptr;
}
void EscapeAnalysis::DebugPrintObject(VirtualObject* object, NodeId id) { void EscapeAnalysis::DebugPrintObject(VirtualObject* object, NodeId id) {
PrintF(" Object #%d with %zu fields\n", id, object->field_count()); PrintF(" Object #%d with %zu fields\n", id, object->field_count());
for (size_t i = 0; i < object->field_count(); ++i) { for (size_t i = 0; i < object->field_count(); ++i) {
......
...@@ -59,6 +59,7 @@ class EscapeStatusAnalysis { ...@@ -59,6 +59,7 @@ class EscapeStatusAnalysis {
bool HasEntry(Node* node); bool HasEntry(Node* node);
void Resize(); void Resize();
size_t size(); size_t size();
bool IsAllocationPhi(Node* node);
Graph* graph() const { return graph_; } Graph* graph() const { return graph_; }
Zone* zone() const { return zone_; } Zone* zone() const { return zone_; }
...@@ -92,6 +93,8 @@ class EscapeAnalysis { ...@@ -92,6 +93,8 @@ class EscapeAnalysis {
Node* GetReplacement(Node* node); Node* GetReplacement(Node* node);
bool IsVirtual(Node* node); bool IsVirtual(Node* node);
bool IsEscaped(Node* node); bool IsEscaped(Node* node);
bool CompareVirtualObjects(Node* left, Node* right);
Node* GetOrCreateObjectState(Node* effect, Node* node);
private: private:
void RunObjectAnalysis(); void RunObjectAnalysis();
...@@ -110,7 +113,6 @@ class EscapeAnalysis { ...@@ -110,7 +113,6 @@ class EscapeAnalysis {
VirtualState* states); VirtualState* states);
void ForwardVirtualState(Node* node); void ForwardVirtualState(Node* node);
bool IsEffectBranchPoint(Node* node); bool IsEffectBranchPoint(Node* node);
bool IsDanglingEffectNode(Node* node); bool IsDanglingEffectNode(Node* node);
int OffsetFromAccess(Node* node); int OffsetFromAccess(Node* node);
......
...@@ -2,8 +2,9 @@ ...@@ -2,8 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include "src/base/functional.h"
#include "src/compiler/frame-states.h" #include "src/compiler/frame-states.h"
#include "src/base/functional.h"
#include "src/handles-inl.h" #include "src/handles-inl.h"
namespace v8 { namespace v8 {
......
...@@ -307,6 +307,9 @@ InstructionOperand OperandForDeopt(OperandGenerator* g, Node* input, ...@@ -307,6 +307,9 @@ InstructionOperand OperandForDeopt(OperandGenerator* g, Node* input,
case IrOpcode::kFloat64Constant: case IrOpcode::kFloat64Constant:
case IrOpcode::kHeapConstant: case IrOpcode::kHeapConstant:
return g->UseImmediate(input); return g->UseImmediate(input);
case IrOpcode::kObjectState:
UNREACHABLE();
break;
default: default:
switch (kind) { switch (kind) {
case FrameStateInputKind::kStackSlot: case FrameStateInputKind::kStackSlot:
...@@ -314,21 +317,94 @@ InstructionOperand OperandForDeopt(OperandGenerator* g, Node* input, ...@@ -314,21 +317,94 @@ InstructionOperand OperandForDeopt(OperandGenerator* g, Node* input,
case FrameStateInputKind::kAny: case FrameStateInputKind::kAny:
return g->UseAny(input); return g->UseAny(input);
} }
UNREACHABLE(); }
return InstructionOperand(); UNREACHABLE();
return InstructionOperand();
}
class StateObjectDeduplicator {
public:
explicit StateObjectDeduplicator(Zone* zone) : objects_(zone) {}
static const size_t kNotDuplicated = SIZE_MAX;
size_t GetObjectId(Node* node) {
for (size_t i = 0; i < objects_.size(); ++i) {
if (objects_[i] == node) {
return i;
}
}
return kNotDuplicated;
}
size_t InsertObject(Node* node) {
size_t id = objects_.size();
objects_.push_back(node);
return id;
}
private:
ZoneVector<Node*> objects_;
};
// Returns the number of instruction operands added to inputs.
size_t AddOperandToStateValueDescriptor(StateValueDescriptor* descriptor,
InstructionOperandVector* inputs,
OperandGenerator* g,
StateObjectDeduplicator* deduplicator,
Node* input, MachineType type,
FrameStateInputKind kind, Zone* zone) {
switch (input->opcode()) {
case IrOpcode::kObjectState: {
size_t id = deduplicator->GetObjectId(input);
if (id == StateObjectDeduplicator::kNotDuplicated) {
size_t entries = 0;
id = deduplicator->InsertObject(input);
descriptor->fields().push_back(
StateValueDescriptor::Recursive(zone, id));
StateValueDescriptor* new_desc = &descriptor->fields().back();
for (Edge edge : input->input_edges()) {
entries += AddOperandToStateValueDescriptor(
new_desc, inputs, g, deduplicator, edge.to(),
MachineType::AnyTagged(), kind, zone);
}
return entries;
} else {
// Crankshaft counts duplicate objects for the running id, so we have
// to push the input again.
deduplicator->InsertObject(input);
descriptor->fields().push_back(
StateValueDescriptor::Duplicate(zone, id));
return 0;
}
break;
}
default: {
inputs->push_back(OperandForDeopt(g, input, kind));
descriptor->fields().push_back(StateValueDescriptor::Plain(zone, type));
return 1;
}
} }
} }
void AddFrameStateInputs(Node* state, OperandGenerator* g, // Returns the number of instruction operands added to inputs.
InstructionOperandVector* inputs, size_t AddInputsToFrameStateDescriptor(FrameStateDescriptor* descriptor,
FrameStateDescriptor* descriptor, Node* state, OperandGenerator* g,
FrameStateInputKind kind, Zone* zone) { StateObjectDeduplicator* deduplicator,
InstructionOperandVector* inputs,
FrameStateInputKind kind, Zone* zone) {
DCHECK_EQ(IrOpcode::kFrameState, state->op()->opcode()); DCHECK_EQ(IrOpcode::kFrameState, state->op()->opcode());
size_t entries = 0;
size_t initial_size = inputs->size();
USE(initial_size); // initial_size is only used for debug.
if (descriptor->outer_state()) { if (descriptor->outer_state()) {
AddFrameStateInputs(state->InputAt(kFrameStateOuterStateInput), g, inputs, entries += AddInputsToFrameStateDescriptor(
descriptor->outer_state(), kind, zone); descriptor->outer_state(), state->InputAt(kFrameStateOuterStateInput),
g, deduplicator, inputs, kind, zone);
} }
Node* parameters = state->InputAt(kFrameStateParametersInput); Node* parameters = state->InputAt(kFrameStateParametersInput);
...@@ -342,32 +418,34 @@ void AddFrameStateInputs(Node* state, OperandGenerator* g, ...@@ -342,32 +418,34 @@ void AddFrameStateInputs(Node* state, OperandGenerator* g,
DCHECK_EQ(descriptor->locals_count(), StateValuesAccess(locals).size()); DCHECK_EQ(descriptor->locals_count(), StateValuesAccess(locals).size());
DCHECK_EQ(descriptor->stack_count(), StateValuesAccess(stack).size()); DCHECK_EQ(descriptor->stack_count(), StateValuesAccess(stack).size());
ZoneVector<MachineType> types(zone); StateValueDescriptor* values_descriptor =
types.reserve(descriptor->GetSize()); descriptor->GetStateValueDescriptor();
entries += AddOperandToStateValueDescriptor(
size_t value_index = 0; values_descriptor, inputs, g, deduplicator, function,
inputs->push_back( MachineType::AnyTagged(), FrameStateInputKind::kStackSlot, zone);
OperandForDeopt(g, function, FrameStateInputKind::kStackSlot));
descriptor->SetType(value_index++, MachineType::AnyTagged());
for (StateValuesAccess::TypedNode input_node : for (StateValuesAccess::TypedNode input_node :
StateValuesAccess(parameters)) { StateValuesAccess(parameters)) {
inputs->push_back(OperandForDeopt(g, input_node.node, kind)); entries += AddOperandToStateValueDescriptor(values_descriptor, inputs, g,
descriptor->SetType(value_index++, input_node.type); deduplicator, input_node.node,
input_node.type, kind, zone);
} }
if (descriptor->HasContext()) { if (descriptor->HasContext()) {
inputs->push_back( entries += AddOperandToStateValueDescriptor(
OperandForDeopt(g, context, FrameStateInputKind::kStackSlot)); values_descriptor, inputs, g, deduplicator, context,
descriptor->SetType(value_index++, MachineType::AnyTagged()); MachineType::AnyTagged(), FrameStateInputKind::kStackSlot, zone);
} }
for (StateValuesAccess::TypedNode input_node : StateValuesAccess(locals)) { for (StateValuesAccess::TypedNode input_node : StateValuesAccess(locals)) {
inputs->push_back(OperandForDeopt(g, input_node.node, kind)); entries += AddOperandToStateValueDescriptor(values_descriptor, inputs, g,
descriptor->SetType(value_index++, input_node.type); deduplicator, input_node.node,
input_node.type, kind, zone);
} }
for (StateValuesAccess::TypedNode input_node : StateValuesAccess(stack)) { for (StateValuesAccess::TypedNode input_node : StateValuesAccess(stack)) {
inputs->push_back(OperandForDeopt(g, input_node.node, kind)); entries += AddOperandToStateValueDescriptor(values_descriptor, inputs, g,
descriptor->SetType(value_index++, input_node.type); deduplicator, input_node.node,
input_node.type, kind, zone);
} }
DCHECK(value_index == descriptor->GetSize()); DCHECK_EQ(initial_size + entries, inputs->size());
return entries;
} }
} // namespace } // namespace
...@@ -500,6 +578,8 @@ void InstructionSelector::InitializeCallBuffer(Node* call, CallBuffer* buffer, ...@@ -500,6 +578,8 @@ void InstructionSelector::InitializeCallBuffer(Node* call, CallBuffer* buffer,
// follows (n is the number of value inputs to the frame state): // follows (n is the number of value inputs to the frame state):
// arg 1 : deoptimization id. // arg 1 : deoptimization id.
// arg 2 - arg (n + 1) : value inputs to the frame state. // arg 2 - arg (n + 1) : value inputs to the frame state.
size_t frame_state_entries = 0;
USE(frame_state_entries); // frame_state_entries is only used for debug.
if (buffer->frame_state_descriptor != NULL) { if (buffer->frame_state_descriptor != NULL) {
InstructionSequence::StateId state_id = InstructionSequence::StateId state_id =
sequence()->AddFrameStateDescriptor(buffer->frame_state_descriptor); sequence()->AddFrameStateDescriptor(buffer->frame_state_descriptor);
...@@ -507,12 +587,17 @@ void InstructionSelector::InitializeCallBuffer(Node* call, CallBuffer* buffer, ...@@ -507,12 +587,17 @@ void InstructionSelector::InitializeCallBuffer(Node* call, CallBuffer* buffer,
Node* frame_state = Node* frame_state =
call->InputAt(static_cast<int>(buffer->descriptor->InputCount())); call->InputAt(static_cast<int>(buffer->descriptor->InputCount()));
AddFrameStateInputs(frame_state, &g, &buffer->instruction_args,
buffer->frame_state_descriptor, StateObjectDeduplicator deduplicator(instruction_zone());
FrameStateInputKind::kStackSlot, instruction_zone());
frame_state_entries =
1 + AddInputsToFrameStateDescriptor(
buffer->frame_state_descriptor, frame_state, &g, &deduplicator,
&buffer->instruction_args, FrameStateInputKind::kStackSlot,
instruction_zone());
DCHECK_EQ(1 + frame_state_entries, buffer->instruction_args.size());
} }
DCHECK(1 + buffer->frame_state_value_count() ==
buffer->instruction_args.size());
size_t input_count = static_cast<size_t>(buffer->input_count()); size_t input_count = static_cast<size_t>(buffer->input_count());
...@@ -549,7 +634,7 @@ void InstructionSelector::InitializeCallBuffer(Node* call, CallBuffer* buffer, ...@@ -549,7 +634,7 @@ void InstructionSelector::InitializeCallBuffer(Node* call, CallBuffer* buffer,
} }
} }
DCHECK_EQ(input_count, buffer->instruction_args.size() + pushed_count - DCHECK_EQ(input_count, buffer->instruction_args.size() + pushed_count -
buffer->frame_state_value_count()); frame_state_entries);
if (V8_TARGET_ARCH_STORES_RETURN_ADDRESS_ON_STACK && call_tail && if (V8_TARGET_ARCH_STORES_RETURN_ADDRESS_ON_STACK && call_tail &&
stack_param_delta != 0) { stack_param_delta != 0) {
// For tail calls that change the size of their parameter list and keep // For tail calls that change the size of their parameter list and keep
...@@ -752,6 +837,7 @@ void InstructionSelector::VisitNode(Node* node) { ...@@ -752,6 +837,7 @@ void InstructionSelector::VisitNode(Node* node) {
return VisitCall(node); return VisitCall(node);
case IrOpcode::kFrameState: case IrOpcode::kFrameState:
case IrOpcode::kStateValues: case IrOpcode::kStateValues:
case IrOpcode::kObjectState:
return; return;
case IrOpcode::kLoad: { case IrOpcode::kLoad: {
LoadRepresentation type = LoadRepresentationOf(node->op()); LoadRepresentation type = LoadRepresentationOf(node->op());
...@@ -1470,19 +1556,19 @@ void InstructionSelector::VisitDeoptimize(DeoptimizeKind kind, Node* value) { ...@@ -1470,19 +1556,19 @@ void InstructionSelector::VisitDeoptimize(DeoptimizeKind kind, Node* value) {
OperandGenerator g(this); OperandGenerator g(this);
FrameStateDescriptor* desc = GetFrameStateDescriptor(value); FrameStateDescriptor* desc = GetFrameStateDescriptor(value);
size_t arg_count = desc->GetTotalSize() + 1; // Include deopt id.
InstructionOperandVector args(instruction_zone()); InstructionOperandVector args(instruction_zone());
args.reserve(arg_count); args.reserve(desc->GetTotalSize() + 1); // Include deopt id.
InstructionSequence::StateId state_id = InstructionSequence::StateId state_id =
sequence()->AddFrameStateDescriptor(desc); sequence()->AddFrameStateDescriptor(desc);
args.push_back(g.TempImmediate(state_id.ToInt())); args.push_back(g.TempImmediate(state_id.ToInt()));
AddFrameStateInputs(value, &g, &args, desc, FrameStateInputKind::kAny, StateObjectDeduplicator deduplicator(instruction_zone());
instruction_zone());
DCHECK_EQ(args.size(), arg_count); AddInputsToFrameStateDescriptor(desc, value, &g, &deduplicator, &args,
FrameStateInputKind::kAny,
instruction_zone());
InstructionCode opcode = kArchDeoptimize; InstructionCode opcode = kArchDeoptimize;
switch (kind) { switch (kind) {
...@@ -1493,7 +1579,7 @@ void InstructionSelector::VisitDeoptimize(DeoptimizeKind kind, Node* value) { ...@@ -1493,7 +1579,7 @@ void InstructionSelector::VisitDeoptimize(DeoptimizeKind kind, Node* value) {
opcode |= MiscField::encode(Deoptimizer::SOFT); opcode |= MiscField::encode(Deoptimizer::SOFT);
break; break;
} }
Emit(opcode, 0, nullptr, arg_count, &args.front(), 0, nullptr); Emit(opcode, 0, nullptr, args.size(), &args.front(), 0, nullptr);
} }
......
...@@ -26,8 +26,6 @@ class Linkage; ...@@ -26,8 +26,6 @@ class Linkage;
class OperandGenerator; class OperandGenerator;
struct SwitchInfo; struct SwitchInfo;
typedef ZoneVector<InstructionOperand> InstructionOperandVector;
// This struct connects nodes of parameters which are going to be pushed on the // This struct connects nodes of parameters which are going to be pushed on the
// call stack with their parameter index in the call descriptor of the callee. // call stack with their parameter index in the call descriptor of the callee.
class PushParameter { class PushParameter {
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include "src/compiler/graph.h" #include "src/compiler/graph.h"
#include "src/compiler/instruction.h" #include "src/compiler/instruction.h"
#include "src/compiler/schedule.h" #include "src/compiler/schedule.h"
#include "src/compiler/state-values-utils.h"
namespace v8 { namespace v8 {
namespace internal { namespace internal {
...@@ -831,11 +832,9 @@ FrameStateDescriptor::FrameStateDescriptor( ...@@ -831,11 +832,9 @@ FrameStateDescriptor::FrameStateDescriptor(
parameters_count_(parameters_count), parameters_count_(parameters_count),
locals_count_(locals_count), locals_count_(locals_count),
stack_count_(stack_count), stack_count_(stack_count),
types_(zone), values_(zone),
shared_info_(shared_info), shared_info_(shared_info),
outer_state_(outer_state) { outer_state_(outer_state) {}
types_.resize(GetSize(), MachineType::None());
}
size_t FrameStateDescriptor::GetSize(OutputFrameStateCombine combine) const { size_t FrameStateDescriptor::GetSize(OutputFrameStateCombine combine) const {
...@@ -884,17 +883,6 @@ size_t FrameStateDescriptor::GetJSFrameCount() const { ...@@ -884,17 +883,6 @@ size_t FrameStateDescriptor::GetJSFrameCount() const {
} }
MachineType FrameStateDescriptor::GetType(size_t index) const {
return types_[index];
}
void FrameStateDescriptor::SetType(size_t index, MachineType type) {
DCHECK(index < GetSize());
types_[index] = type;
}
std::ostream& operator<<(std::ostream& os, const RpoNumber& rpo) { std::ostream& operator<<(std::ostream& os, const RpoNumber& rpo) {
return os << rpo.ToSize(); return os << rpo.ToSize();
} }
......
...@@ -23,8 +23,10 @@ namespace v8 { ...@@ -23,8 +23,10 @@ namespace v8 {
namespace internal { namespace internal {
namespace compiler { namespace compiler {
// Forward declarations.
class Schedule; class Schedule;
class InstructionOperand { class InstructionOperand {
public: public:
static const int kInvalidVirtualRegister = -1; static const int kInvalidVirtualRegister = -1;
...@@ -108,6 +110,9 @@ class InstructionOperand { ...@@ -108,6 +110,9 @@ class InstructionOperand {
}; };
typedef ZoneVector<InstructionOperand> InstructionOperandVector;
struct PrintableInstructionOperand { struct PrintableInstructionOperand {
const RegisterConfiguration* register_configuration_; const RegisterConfiguration* register_configuration_;
InstructionOperand op_; InstructionOperand op_;
...@@ -941,6 +946,59 @@ class Constant final { ...@@ -941,6 +946,59 @@ class Constant final {
}; };
std::ostream& operator<<(std::ostream& os, const Constant& constant);
// Forward declarations.
class FrameStateDescriptor;
enum class StateValueKind { kPlain, kNested, kDuplicate };
class StateValueDescriptor {
public:
explicit StateValueDescriptor(Zone* zone)
: kind_(StateValueKind::kPlain),
type_(MachineType::AnyTagged()),
id_(0),
fields_(zone) {}
static StateValueDescriptor Plain(Zone* zone, MachineType type) {
return StateValueDescriptor(StateValueKind::kPlain, zone, type, 0);
}
static StateValueDescriptor Recursive(Zone* zone, size_t id) {
return StateValueDescriptor(StateValueKind::kNested, zone,
MachineType::AnyTagged(), id);
}
static StateValueDescriptor Duplicate(Zone* zone, size_t id) {
return StateValueDescriptor(StateValueKind::kDuplicate, zone,
MachineType::AnyTagged(), id);
}
size_t size() { return fields_.size(); }
ZoneVector<StateValueDescriptor>& fields() { return fields_; }
int IsPlain() { return kind_ == StateValueKind::kPlain; }
int IsNested() { return kind_ == StateValueKind::kNested; }
int IsDuplicate() { return kind_ == StateValueKind::kDuplicate; }
MachineType type() const { return type_; }
MachineType GetOperandType(size_t index) const {
return fields_[index].type_;
}
size_t id() const { return id_; }
private:
StateValueDescriptor(StateValueKind kind, Zone* zone, MachineType type,
size_t id)
: kind_(kind), type_(type), id_(id), fields_(zone) {}
StateValueKind kind_;
MachineType type_;
size_t id_;
ZoneVector<StateValueDescriptor> fields_;
};
class FrameStateDescriptor : public ZoneObject { class FrameStateDescriptor : public ZoneObject {
public: public:
FrameStateDescriptor(Zone* zone, FrameStateType type, BailoutId bailout_id, FrameStateDescriptor(Zone* zone, FrameStateType type, BailoutId bailout_id,
...@@ -968,8 +1026,10 @@ class FrameStateDescriptor : public ZoneObject { ...@@ -968,8 +1026,10 @@ class FrameStateDescriptor : public ZoneObject {
size_t GetFrameCount() const; size_t GetFrameCount() const;
size_t GetJSFrameCount() const; size_t GetJSFrameCount() const;
MachineType GetType(size_t index) const; MachineType GetType(size_t index) const {
void SetType(size_t index, MachineType type); return values_.GetOperandType(index);
}
StateValueDescriptor* GetStateValueDescriptor() { return &values_; }
private: private:
FrameStateType type_; FrameStateType type_;
...@@ -978,12 +1038,13 @@ class FrameStateDescriptor : public ZoneObject { ...@@ -978,12 +1038,13 @@ class FrameStateDescriptor : public ZoneObject {
size_t parameters_count_; size_t parameters_count_;
size_t locals_count_; size_t locals_count_;
size_t stack_count_; size_t stack_count_;
ZoneVector<MachineType> types_; StateValueDescriptor values_;
MaybeHandle<SharedFunctionInfo> const shared_info_; MaybeHandle<SharedFunctionInfo> const shared_info_;
FrameStateDescriptor* outer_state_; FrameStateDescriptor* outer_state_;
}; };
std::ostream& operator<<(std::ostream& os, const Constant& constant);
typedef ZoneVector<FrameStateDescriptor*> DeoptimizationVector;
class PhiInstruction final : public ZoneObject { class PhiInstruction final : public ZoneObject {
...@@ -1100,9 +1161,10 @@ typedef std::map<int, Constant, std::less<int>, ...@@ -1100,9 +1161,10 @@ typedef std::map<int, Constant, std::less<int>,
typedef ZoneDeque<Instruction*> InstructionDeque; typedef ZoneDeque<Instruction*> InstructionDeque;
typedef ZoneDeque<ReferenceMap*> ReferenceMapDeque; typedef ZoneDeque<ReferenceMap*> ReferenceMapDeque;
typedef ZoneVector<FrameStateDescriptor*> DeoptimizationVector;
typedef ZoneVector<InstructionBlock*> InstructionBlocks; typedef ZoneVector<InstructionBlock*> InstructionBlocks;
// Forward declarations.
struct PrintableInstructionSequence; struct PrintableInstructionSequence;
......
...@@ -50,6 +50,7 @@ ...@@ -50,6 +50,7 @@
V(FrameState) \ V(FrameState) \
V(StateValues) \ V(StateValues) \
V(TypedStateValues) \ V(TypedStateValues) \
V(ObjectState) \
V(Call) \ V(Call) \
V(Parameter) \ V(Parameter) \
V(OsrValue) \ V(OsrValue) \
......
...@@ -636,6 +636,11 @@ Type* Typer::Visitor::TypeStateValues(Node* node) { ...@@ -636,6 +636,11 @@ Type* Typer::Visitor::TypeStateValues(Node* node) {
} }
Type* Typer::Visitor::TypeObjectState(Node* node) {
return Type::Internal(zone());
}
Type* Typer::Visitor::TypeTypedStateValues(Node* node) { Type* Typer::Visitor::TypeTypedStateValues(Node* node) {
return Type::Internal(zone()); return Type::Internal(zone());
} }
......
...@@ -436,6 +436,7 @@ void Verifier::Visitor::Check(Node* node) { ...@@ -436,6 +436,7 @@ void Verifier::Visitor::Check(Node* node) {
CHECK_EQ(6, input_count); CHECK_EQ(6, input_count);
break; break;
case IrOpcode::kStateValues: case IrOpcode::kStateValues:
case IrOpcode::kObjectState:
case IrOpcode::kTypedStateValues: case IrOpcode::kTypedStateValues:
// TODO(jarin): what are the constraints on these? // TODO(jarin): what are the constraints on these?
break; break;
......
...@@ -3486,6 +3486,37 @@ Handle<Object> TranslatedState::MaterializeAt(int frame_index, ...@@ -3486,6 +3486,37 @@ Handle<Object> TranslatedState::MaterializeAt(int frame_index,
object->set_length(*length); object->set_length(*length);
return object; return object;
} }
case FIXED_ARRAY_TYPE: {
Handle<Object> lengthObject = MaterializeAt(frame_index, value_index);
int32_t length = 0;
CHECK(lengthObject->ToInt32(&length));
Handle<FixedArray> object =
isolate_->factory()->NewFixedArray(length);
slot->value_ = object;
for (int i = 0; i < length; ++i) {
Handle<Object> value = MaterializeAt(frame_index, value_index);
object->set(i, *value);
}
return object;
}
case FIXED_DOUBLE_ARRAY_TYPE: {
Handle<Object> lengthObject = MaterializeAt(frame_index, value_index);
int32_t length = 0;
CHECK(lengthObject->ToInt32(&length));
Handle<FixedArrayBase> object =
isolate_->factory()->NewFixedDoubleArray(length);
slot->value_ = object;
if (length > 0) {
Handle<FixedDoubleArray> double_array =
Handle<FixedDoubleArray>::cast(object);
for (int i = 0; i < length; ++i) {
Handle<Object> value = MaterializeAt(frame_index, value_index);
CHECK(value->IsNumber());
double_array->set(i, value->Number());
}
}
return object;
}
default: default:
PrintF(stderr, "[couldn't handle instance type %d]\n", PrintF(stderr, "[couldn't handle instance type %d]\n",
map->instance_type()); map->instance_type());
......
// Copyright 2013 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Flags: --allow-natives-syntax --turbo-escape
// Test deoptimization with captured objects in local variables.
(function testDeoptLocal() {
"use strict";
function constructor1(a) {
return arguments;
}
function func(a) {
var o1 = constructor1(1,2,3);
if (a) { %DeoptimizeNow(); }
assertEquals(1, o1[0]);
assertEquals(2, o1[1]);
assertEquals(3, o1[2]);
}
func(false);
func(false);
%OptimizeFunctionOnNextCall(func);
func(true);
})();
// Copyright 2013 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Flags: --allow-natives-syntax --turbo-escape
// Test deoptimization with captured objects in local variables.
(function testDeoptLocal() {
"use strict";
function constructor1(a) {
return arguments;
}
function func() {
var o1 = constructor1(1,2,3);
var o2 = constructor1(4,o1);
%DeoptimizeNow();
assertEquals(1, o1[0]);
assertEquals(2, o1[1]);
assertEquals(3, o1[2]);
assertEquals(4, o2[0]);
assertEquals(o1, o2[1]);
}
func();
func();
%OptimizeFunctionOnNextCall(func);
func();
})();
// Copyright 2013 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Flags: --allow-natives-syntax --turbo-escape
// Test deoptimization with captured objects in local variables.
(function testDeoptLocal() {
"use strict";
function constructor1(a) {
return arguments;
}
function func() {
var o1 = constructor1(1,2,3);
var o2 = constructor1(4,o1);
o1[0] = o1;
%DeoptimizeNow();
assertEquals(o1, o1[0]);
assertEquals(2, o1[1]);
assertEquals(3, o1[2]);
assertEquals(4, o2[0]);
assertEquals(o1, o2[1]);
}
func();
func();
%OptimizeFunctionOnNextCall(func);
func();
})();
// Copyright 2013 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Flags: --allow-natives-syntax --turbo-escape
// Test deoptimization with captured objects in local variables.
(function testDeoptLocal() {
"use strict";
function constructor1() {
this.x=1;
this.y=2;
this.z=3;
}
function constructor2(x) {
this.a=x;
this.b=4;
}
function func() {
var o1 = new constructor1();
var o2 = new constructor2(o1);
o1.x = o1;
%DeoptimizeNow();
assertEquals(o1, o1.x);
assertEquals(2, o1.y);
assertEquals(3, o1.z);
assertEquals(o1, o2.a);
assertEquals(4, o2.b);
}
func();
func();
%OptimizeFunctionOnNextCall(func);
func();
})();
// Copyright 2016 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Flags: --allow-natives-syntax --turbo-escape
function f() {
var x = new Array(2);
x[0] = 23.1234;
x[1] = 25.1234;
%DeoptimizeNow();
return x[0];
}
assertEquals(f(), 23.1234);
assertEquals(f(), 23.1234);
%OptimizeFunctionOnNextCall(f);
assertEquals(f(), 23.1234);
...@@ -156,19 +156,6 @@ ...@@ -156,19 +156,6 @@
# TODO(titzer): too slow in --turbo mode due to O(n^2) graph verification. # TODO(titzer): too slow in --turbo mode due to O(n^2) graph verification.
'regress/regress-1122': [PASS, NO_VARIANTS], 'regress/regress-1122': [PASS, NO_VARIANTS],
# TODO(sigurds): Tests that may fail because of missing deopt support.
'compiler/escape-analysis-1': [PASS, NO_VARIANTS],
'compiler/escape-analysis-2': [PASS, NO_VARIANTS],
'compiler/escape-analysis-3': [PASS, NO_VARIANTS],
'compiler/escape-analysis-4': [PASS, NO_VARIANTS],
'compiler/escape-analysis-5': [PASS, NO_VARIANTS],
'compiler/escape-analysis-6': [PASS, NO_VARIANTS],
'compiler/escape-analysis-7': [PASS, NO_VARIANTS],
'compiler/escape-analysis-9': [PASS, NO_VARIANTS],
'compiler/escape-analysis-10': [PASS, NO_VARIANTS],
# TODO(sigurds): Tests that fail because of incomplete use handling (i.e. select).
'compiler/escape-analysis-8': [PASS, NO_VARIANTS],
# Assumptions about optimization need investigation in TurboFan. # Assumptions about optimization need investigation in TurboFan.
'regress-sync-optimized-lists': [PASS, NO_VARIANTS], 'regress-sync-optimized-lists': [PASS, NO_VARIANTS],
'regress/regress-store-uncacheable': [PASS, NO_VARIANTS], 'regress/regress-store-uncacheable': [PASS, NO_VARIANTS],
......
...@@ -300,6 +300,90 @@ TEST_F(EscapeAnalysisTest, DanglingLoadOrder) { ...@@ -300,6 +300,90 @@ TEST_F(EscapeAnalysisTest, DanglingLoadOrder) {
ASSERT_EQ(object1, NodeProperties::GetValueInput(result, 0)); ASSERT_EQ(object1, NodeProperties::GetValueInput(result, 0));
} }
TEST_F(EscapeAnalysisTest, DeoptReplacement) {
Node* object1 = Constant(1);
BeginRegion();
Node* allocation = Allocate(Constant(kPointerSize));
Store(AccessAtIndex(0), allocation, object1);
Node* finish = FinishRegion(allocation);
Node* effect1 = Store(AccessAtIndex(0), allocation, object1, finish);
Branch();
Node* ifFalse = IfFalse();
Node* state_values1 = graph()->NewNode(common()->StateValues(1), finish);
Node* state_values2 = graph()->NewNode(common()->StateValues(0));
Node* state_values3 = graph()->NewNode(common()->StateValues(0));
Node* frame_state = graph()->NewNode(
common()->FrameState(BailoutId::None(), OutputFrameStateCombine::Ignore(),
nullptr),
state_values1, state_values2, state_values3, UndefinedConstant(),
graph()->start(), graph()->start());
Node* deopt = graph()->NewNode(common()->Deoptimize(DeoptimizeKind::kEager),
frame_state, effect1, ifFalse);
Node* ifTrue = IfTrue();
Node* load = Load(AccessAtIndex(0), finish, effect1, ifTrue);
Node* result = Return(load, effect1, ifTrue);
EndGraph();
graph()->end()->AppendInput(zone(), deopt);
Analysis();
ExpectVirtual(allocation);
ExpectReplacement(load, object1);
Transformation();
ASSERT_EQ(object1, NodeProperties::GetValueInput(result, 0));
Node* object_state = NodeProperties::GetValueInput(state_values1, 0);
ASSERT_EQ(object_state->opcode(), IrOpcode::kObjectState);
ASSERT_EQ(1, object_state->op()->ValueInputCount());
ASSERT_EQ(object1, NodeProperties::GetValueInput(object_state, 0));
}
TEST_F(EscapeAnalysisTest, DeoptReplacementIdentity) {
Node* object1 = Constant(1);
BeginRegion();
Node* allocation = Allocate(Constant(kPointerSize * 2));
Store(AccessAtIndex(0), allocation, object1);
Store(AccessAtIndex(kPointerSize), allocation, allocation);
Node* finish = FinishRegion(allocation);
Node* effect1 = Store(AccessAtIndex(0), allocation, object1, finish);
Branch();
Node* ifFalse = IfFalse();
Node* state_values1 = graph()->NewNode(common()->StateValues(1), finish);
Node* state_values2 = graph()->NewNode(common()->StateValues(1), finish);
Node* state_values3 = graph()->NewNode(common()->StateValues(0));
Node* frame_state = graph()->NewNode(
common()->FrameState(BailoutId::None(), OutputFrameStateCombine::Ignore(),
nullptr),
state_values1, state_values2, state_values3, UndefinedConstant(),
graph()->start(), graph()->start());
Node* deopt = graph()->NewNode(common()->Deoptimize(DeoptimizeKind::kEager),
frame_state, effect1, ifFalse);
Node* ifTrue = IfTrue();
Node* load = Load(AccessAtIndex(0), finish, effect1, ifTrue);
Node* result = Return(load, effect1, ifTrue);
EndGraph();
graph()->end()->AppendInput(zone(), deopt);
Analysis();
ExpectVirtual(allocation);
ExpectReplacement(load, object1);
Transformation();
ASSERT_EQ(object1, NodeProperties::GetValueInput(result, 0));
Node* object_state = NodeProperties::GetValueInput(state_values1, 0);
ASSERT_EQ(object_state->opcode(), IrOpcode::kObjectState);
ASSERT_EQ(2, object_state->op()->ValueInputCount());
ASSERT_EQ(object1, NodeProperties::GetValueInput(object_state, 0));
ASSERT_EQ(object_state, NodeProperties::GetValueInput(object_state, 1));
Node* object_state2 = NodeProperties::GetValueInput(state_values1, 0);
ASSERT_EQ(object_state, object_state2);
}
} // namespace compiler } // namespace compiler
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment