[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}
This commit is contained in:
parent
06738d6410
commit
3b473d7aad
@ -216,8 +216,10 @@ Handle<Code> CodeGenerator::GenerateCode() {
|
||||
|
||||
|
||||
bool CodeGenerator::IsNextInAssemblyOrder(RpoNumber block) const {
|
||||
return code()->InstructionBlockAt(current_block_)->ao_number().IsNext(
|
||||
code()->InstructionBlockAt(block)->ao_number());
|
||||
return code()
|
||||
->InstructionBlockAt(current_block_)
|
||||
->ao_number()
|
||||
.IsNext(code()->InstructionBlockAt(block)->ao_number());
|
||||
}
|
||||
|
||||
|
||||
@ -489,64 +491,84 @@ FrameStateDescriptor* CodeGenerator::GetFrameStateDescriptor(
|
||||
}
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
struct OperandAndType {
|
||||
InstructionOperand* const operand;
|
||||
MachineType const type;
|
||||
};
|
||||
|
||||
|
||||
OperandAndType TypedOperandForFrameState(FrameStateDescriptor* descriptor,
|
||||
Instruction* instr,
|
||||
size_t frame_state_offset,
|
||||
size_t index,
|
||||
OutputFrameStateCombine combine) {
|
||||
DCHECK(index < descriptor->GetSize(combine));
|
||||
switch (combine.kind()) {
|
||||
case OutputFrameStateCombine::kPushOutput: {
|
||||
DCHECK(combine.GetPushCount() <= instr->OutputCount());
|
||||
size_t size_without_output =
|
||||
descriptor->GetSize(OutputFrameStateCombine::Ignore());
|
||||
// If the index is past the existing stack items, return the output.
|
||||
if (index >= size_without_output) {
|
||||
return {instr->OutputAt(index - size_without_output),
|
||||
MachineType::AnyTagged()};
|
||||
}
|
||||
break;
|
||||
void CodeGenerator::TranslateStateValueDescriptor(
|
||||
StateValueDescriptor* desc, Translation* translation,
|
||||
InstructionOperandIterator* iter) {
|
||||
if (desc->IsNested()) {
|
||||
translation->BeginCapturedObject(static_cast<int>(desc->size()));
|
||||
for (size_t index = 0; index < desc->fields().size(); index++) {
|
||||
TranslateStateValueDescriptor(&desc->fields()[index], translation, iter);
|
||||
}
|
||||
case OutputFrameStateCombine::kPokeAt:
|
||||
size_t index_from_top =
|
||||
descriptor->GetSize(combine) - 1 - combine.GetOffsetToPokeAt();
|
||||
if (index >= index_from_top &&
|
||||
index < index_from_top + instr->OutputCount()) {
|
||||
return {instr->OutputAt(index - index_from_top),
|
||||
MachineType::AnyTagged()};
|
||||
}
|
||||
break;
|
||||
} else if (desc->IsDuplicate()) {
|
||||
translation->DuplicateObject(static_cast<int>(desc->id()));
|
||||
} else {
|
||||
DCHECK(desc->IsPlain());
|
||||
AddTranslationForOperand(translation, iter->instruction(), iter->Advance(),
|
||||
desc->type());
|
||||
}
|
||||
return {instr->InputAt(frame_state_offset + index),
|
||||
descriptor->GetType(index)};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void CodeGenerator::TranslateFrameStateDescriptorOperands(
|
||||
FrameStateDescriptor* desc, InstructionOperandIterator* iter,
|
||||
OutputFrameStateCombine combine, Translation* translation) {
|
||||
for (size_t index = 0; index < desc->GetSize(combine); index++) {
|
||||
switch (combine.kind()) {
|
||||
case OutputFrameStateCombine::kPushOutput: {
|
||||
DCHECK(combine.GetPushCount() <= iter->instruction()->OutputCount());
|
||||
size_t size_without_output =
|
||||
desc->GetSize(OutputFrameStateCombine::Ignore());
|
||||
// If the index is past the existing stack items in values_.
|
||||
if (index >= size_without_output) {
|
||||
// Materialize the result of the call instruction in this slot.
|
||||
AddTranslationForOperand(
|
||||
translation, iter->instruction(),
|
||||
iter->instruction()->OutputAt(index - size_without_output),
|
||||
MachineType::AnyTagged());
|
||||
continue;
|
||||
}
|
||||
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;
|
||||
}
|
||||
StateValueDescriptor* value_desc = desc->GetStateValueDescriptor();
|
||||
TranslateStateValueDescriptor(&value_desc->fields()[index], translation,
|
||||
iter);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CodeGenerator::BuildTranslationForFrameStateDescriptor(
|
||||
FrameStateDescriptor* descriptor, Instruction* instr,
|
||||
Translation* translation, size_t frame_state_offset,
|
||||
OutputFrameStateCombine state_combine) {
|
||||
FrameStateDescriptor* descriptor, InstructionOperandIterator* iter,
|
||||
Translation* translation, OutputFrameStateCombine state_combine) {
|
||||
// Outer-most state must be added to translation first.
|
||||
if (descriptor->outer_state() != nullptr) {
|
||||
BuildTranslationForFrameStateDescriptor(descriptor->outer_state(), instr,
|
||||
translation, frame_state_offset,
|
||||
BuildTranslationForFrameStateDescriptor(descriptor->outer_state(), iter,
|
||||
translation,
|
||||
OutputFrameStateCombine::Ignore());
|
||||
}
|
||||
frame_state_offset += descriptor->outer_state()->GetTotalSize();
|
||||
|
||||
Handle<SharedFunctionInfo> 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();
|
||||
}
|
||||
int shared_info_id = DefineDeoptimizationLiteral(shared_info);
|
||||
@ -575,11 +597,8 @@ void CodeGenerator::BuildTranslationForFrameStateDescriptor(
|
||||
break;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < descriptor->GetSize(state_combine); i++) {
|
||||
OperandAndType op = TypedOperandForFrameState(
|
||||
descriptor, instr, frame_state_offset, i, state_combine);
|
||||
AddTranslationForOperand(translation, instr, op.operand, op.type);
|
||||
}
|
||||
TranslateFrameStateDescriptorOperands(descriptor, iter, state_combine,
|
||||
translation);
|
||||
}
|
||||
|
||||
|
||||
@ -593,8 +612,9 @@ int CodeGenerator::BuildTranslation(Instruction* instr, int pc_offset,
|
||||
Translation translation(
|
||||
&translations_, static_cast<int>(descriptor->GetFrameCount()),
|
||||
static_cast<int>(descriptor->GetJSFrameCount()), zone());
|
||||
BuildTranslationForFrameStateDescriptor(descriptor, instr, &translation,
|
||||
frame_state_offset, state_combine);
|
||||
InstructionOperandIterator iter(instr, frame_state_offset);
|
||||
BuildTranslationForFrameStateDescriptor(descriptor, &iter, &translation,
|
||||
state_combine);
|
||||
|
||||
int deoptimization_id = static_cast<int>(deoptimization_states_.size());
|
||||
|
||||
|
@ -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.
|
||||
class CodeGenerator final : public GapResolver::Assembler {
|
||||
public:
|
||||
@ -136,9 +150,15 @@ class CodeGenerator final : public GapResolver::Assembler {
|
||||
size_t frame_access_state_offset,
|
||||
OutputFrameStateCombine state_combine);
|
||||
void BuildTranslationForFrameStateDescriptor(
|
||||
FrameStateDescriptor* descriptor, Instruction* instr,
|
||||
Translation* translation, size_t frame_access_state_offset,
|
||||
OutputFrameStateCombine state_combine);
|
||||
FrameStateDescriptor* descriptor, InstructionOperandIterator* iter,
|
||||
Translation* translation, 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,
|
||||
InstructionOperand* op, MachineType type);
|
||||
void AddNopForSmiCodeInlining();
|
||||
|
@ -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 ZoneVector<MachineType>* types) {
|
||||
return new (zone()) Operator1<const ZoneVector<MachineType>*>( // --
|
||||
|
@ -166,6 +166,7 @@ class CommonOperatorBuilder final : public ZoneObject {
|
||||
const Operator* BeginRegion();
|
||||
const Operator* FinishRegion();
|
||||
const Operator* StateValues(int arguments);
|
||||
const Operator* ObjectState(int pointer_slots, int id);
|
||||
const Operator* TypedStateValues(const ZoneVector<MachineType>* types);
|
||||
const Operator* FrameState(BailoutId bailout_id,
|
||||
OutputFrameStateCombine state_combine,
|
||||
|
@ -36,10 +36,13 @@ Reduction EscapeAnalysisReducer::Reduce(Node* node) {
|
||||
return ReduceReferenceEqual(node);
|
||||
case IrOpcode::kObjectIsSmi:
|
||||
return ReduceObjectIsSmi(node);
|
||||
case IrOpcode::kStateValues:
|
||||
case IrOpcode::kFrameState:
|
||||
return ReplaceWithDeoptDummy(node);
|
||||
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;
|
||||
}
|
||||
return NoChange();
|
||||
@ -117,23 +120,14 @@ Reduction EscapeAnalysisReducer::ReduceReferenceEqual(Node* node) {
|
||||
Node* left = NodeProperties::GetValueInput(node, 0);
|
||||
Node* right = NodeProperties::GetValueInput(node, 1);
|
||||
if (escape_analysis()->IsVirtual(left)) {
|
||||
if (escape_analysis()->IsVirtual(right)) {
|
||||
if (Node* rep = escape_analysis()->GetReplacement(left)) {
|
||||
left = rep;
|
||||
}
|
||||
if (Node* rep = escape_analysis()->GetReplacement(right)) {
|
||||
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);
|
||||
if (escape_analysis()->IsVirtual(right) &&
|
||||
escape_analysis()->CompareVirtualObjects(left, right)) {
|
||||
ReplaceWithValue(node, jsgraph()->TrueConstant());
|
||||
if (FLAG_trace_turbo_escape) {
|
||||
PrintF("Replaced ref eq #%d with true\n", node->id());
|
||||
}
|
||||
}
|
||||
// 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());
|
||||
if (FLAG_trace_turbo_escape) {
|
||||
PrintF("Replaced ref eq #%d with false\n", node->id());
|
||||
@ -164,28 +158,132 @@ Reduction EscapeAnalysisReducer::ReduceObjectIsSmi(Node* node) {
|
||||
}
|
||||
|
||||
|
||||
// TODO(sigurds): This is a temporary solution until escape analysis
|
||||
// supports deoptimization.
|
||||
Reduction EscapeAnalysisReducer::ReplaceWithDeoptDummy(Node* node) {
|
||||
DCHECK(node->opcode() == IrOpcode::kStateValues ||
|
||||
node->opcode() == IrOpcode::kFrameState);
|
||||
Reduction r = NoChange();
|
||||
for (int i = 0; i < node->op()->ValueInputCount(); ++i) {
|
||||
Node* input = NodeProperties::GetValueInput(node, i);
|
||||
if (input->opcode() == IrOpcode::kFinishRegion ||
|
||||
input->opcode() == IrOpcode::kAllocate ||
|
||||
input->opcode() == IrOpcode::kPhi) {
|
||||
if (escape_analysis()->IsVirtual(input)) {
|
||||
NodeProperties::ReplaceValueInput(node, jsgraph()->UndefinedConstant(),
|
||||
i);
|
||||
if (FLAG_trace_turbo_escape) {
|
||||
PrintF("Replaced state value (#%d) input with dummy\n", node->id());
|
||||
}
|
||||
r = Changed(node);
|
||||
Reduction EscapeAnalysisReducer::ReduceFrameStateUses(Node* node) {
|
||||
DCHECK_GE(node->op()->EffectInputCount(), 1);
|
||||
bool changed = false;
|
||||
for (int i = 0; i < node->InputCount(); ++i) {
|
||||
Node* input = node->InputAt(i);
|
||||
if (input->opcode() == IrOpcode::kFrameState) {
|
||||
if (Node* ret = ReduceFrameState(input, node, false)) {
|
||||
node->ReplaceInput(i, ret);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return r;
|
||||
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) {
|
||||
Node* input = NodeProperties::GetValueInput(node, i);
|
||||
Node* ret =
|
||||
input->opcode() == IrOpcode::kStateValues
|
||||
? ReduceStateValueInputs(input, effect, node->UseCount() > 1)
|
||||
: ReduceStateValueInput(node, i, effect, node->UseCount() > 1);
|
||||
if (ret) {
|
||||
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) {
|
||||
PrintF("No object state replacement available.\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return clone;
|
||||
}
|
||||
|
||||
|
||||
|
@ -35,8 +35,11 @@ class EscapeAnalysisReducer final : public AdvancedReducer {
|
||||
Reduction ReduceFinishRegion(Node* node);
|
||||
Reduction ReduceReferenceEqual(Node* node);
|
||||
Reduction ReduceObjectIsSmi(Node* node);
|
||||
|
||||
Reduction ReplaceWithDeoptDummy(Node* node);
|
||||
Reduction ReduceFrameStateUses(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_; }
|
||||
EscapeAnalysis* escape_analysis() const { return escape_analysis_; }
|
||||
|
@ -21,24 +21,29 @@ namespace v8 {
|
||||
namespace internal {
|
||||
namespace compiler {
|
||||
|
||||
|
||||
// ------------------------------VirtualObject----------------------------------
|
||||
|
||||
|
||||
class VirtualObject : public ZoneObject {
|
||||
public:
|
||||
enum Status { kUntracked = 0, kTracked = 1 };
|
||||
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)
|
||||
: id_(other.id_),
|
||||
status_(other.status_),
|
||||
fields_(other.fields_),
|
||||
phi_(other.phi_) {}
|
||||
phi_(other.phi_),
|
||||
object_state_(other.object_state_) {}
|
||||
|
||||
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);
|
||||
phi_.resize(field_number, false);
|
||||
}
|
||||
@ -92,6 +97,8 @@ class VirtualObject : public ZoneObject {
|
||||
return changed;
|
||||
}
|
||||
bool UpdateFrom(const VirtualObject& other);
|
||||
void SetObjectState(Node* node) { object_state_ = node; }
|
||||
Node* GetObjectState() { return object_state_; }
|
||||
|
||||
NodeId id() { return id_; }
|
||||
void id(NodeId id) { id_ = id; }
|
||||
@ -101,6 +108,7 @@ class VirtualObject : public ZoneObject {
|
||||
Status status_;
|
||||
ZoneVector<Node*> fields_;
|
||||
ZoneVector<bool> phi_;
|
||||
Node* object_state_;
|
||||
};
|
||||
|
||||
|
||||
@ -121,9 +129,6 @@ bool VirtualObject::UpdateFrom(const VirtualObject& other) {
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------VirtualState-----------------------------------
|
||||
|
||||
|
||||
class VirtualState : public ZoneObject {
|
||||
public:
|
||||
VirtualState(Zone* zone, size_t size);
|
||||
@ -224,15 +229,12 @@ Node* MergeCache::GetFields(size_t pos) {
|
||||
|
||||
|
||||
VirtualState::VirtualState(Zone* zone, size_t size)
|
||||
: info_(zone), last_changed_(nullptr) {
|
||||
info_.resize(size);
|
||||
}
|
||||
: info_(size, nullptr, zone), last_changed_(nullptr) {}
|
||||
|
||||
|
||||
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_) {
|
||||
info_.resize(state.info_.size());
|
||||
for (size_t i = 0; i < state.info_.size(); ++i) {
|
||||
if (state.info_[i] && state.info_[i]->id() == i) {
|
||||
info_[i] = new (state.info_.get_allocator().zone())
|
||||
@ -428,8 +430,10 @@ bool VirtualState::MergeFrom(MergeCache* cache, Zone* zone, Graph* graph,
|
||||
}
|
||||
}
|
||||
if (rep->op()->ValueInputCount() != value_input_count) {
|
||||
PrintF(" Widening Phi #%d of arity %d to %d", rep->id(),
|
||||
rep->op()->ValueInputCount(), value_input_count);
|
||||
if (FLAG_trace_turbo_escape) {
|
||||
PrintF(" Widening Phi #%d of arity %d to %d", rep->id(),
|
||||
rep->op()->ValueInputCount(), value_input_count);
|
||||
}
|
||||
NodeProperties::ChangeOp(
|
||||
rep, common->Phi(MachineRepresentation::kTagged,
|
||||
value_input_count));
|
||||
@ -440,7 +444,6 @@ bool VirtualState::MergeFrom(MergeCache* cache, Zone* zone, Graph* graph,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
SetVirtualObject(id, nullptr);
|
||||
}
|
||||
@ -457,9 +460,6 @@ bool VirtualState::MergeFrom(MergeCache* cache, Zone* zone, Graph* graph,
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------EscapeStatusAnalysis---------------------------
|
||||
|
||||
|
||||
EscapeStatusAnalysis::EscapeStatusAnalysis(EscapeAnalysis* object_analysis,
|
||||
Graph* graph, Zone* zone)
|
||||
: object_analysis_(object_analysis),
|
||||
@ -571,6 +571,10 @@ void EscapeStatusAnalysis::Process(Node* node) {
|
||||
case IrOpcode::kPhi:
|
||||
if (!HasEntry(node)) {
|
||||
info_[node->id()] = kVirtual;
|
||||
if (!IsAllocationPhi(node)) {
|
||||
SetEscaped(node);
|
||||
RevisitUses(node);
|
||||
}
|
||||
}
|
||||
CheckUsesForEscape(node);
|
||||
default:
|
||||
@ -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) {
|
||||
DCHECK_EQ(node->opcode(), IrOpcode::kStoreField);
|
||||
Node* to = NodeProperties::GetValueInput(node, 0);
|
||||
@ -618,6 +633,10 @@ void EscapeStatusAnalysis::ProcessAllocate(Node* node) {
|
||||
node->op()->mnemonic());
|
||||
}
|
||||
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)) {
|
||||
RevisitUses(node);
|
||||
if (FLAG_trace_turbo_escape) {
|
||||
@ -724,9 +743,6 @@ void EscapeStatusAnalysis::DebugPrint() {
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------EscapeAnalysis----------------------------------
|
||||
|
||||
|
||||
EscapeAnalysis::EscapeAnalysis(Graph* graph, CommonOperatorBuilder* common,
|
||||
Zone* zone)
|
||||
: graph_(graph),
|
||||
@ -1005,6 +1021,10 @@ void EscapeAnalysis::ProcessAllocation(Node* node) {
|
||||
if (virtual_states_[node->id()]->GetVirtualObject(node)) return;
|
||||
|
||||
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()) {
|
||||
virtual_states_[node->id()]->SetVirtualObject(
|
||||
node->id(), new (zone()) VirtualObject(node->id(), zone(),
|
||||
@ -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) {
|
||||
DCHECK(OpParameter<FieldAccess>(node).offset % kPointerSize == 0);
|
||||
return OpParameter<FieldAccess>(node).offset / kPointerSize;
|
||||
@ -1215,6 +1246,10 @@ void EscapeAnalysis::ProcessLoadElement(Node* node) {
|
||||
VirtualState* state = virtual_states_[node->id()];
|
||||
Node* index_node = node->InputAt(1);
|
||||
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);
|
||||
if (index.HasValue()) {
|
||||
int offset = index.Value() + access.header_size / kPointerSize;
|
||||
@ -1273,6 +1308,10 @@ void EscapeAnalysis::ProcessStoreElement(Node* node) {
|
||||
Node* to = NodeProperties::GetValueInput(node, 0);
|
||||
Node* index_node = node->InputAt(1);
|
||||
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);
|
||||
Node* val = NodeProperties::GetValueInput(node, 2);
|
||||
if (index.HasValue()) {
|
||||
@ -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) {
|
||||
PrintF(" Object #%d with %zu fields\n", id, object->field_count());
|
||||
for (size_t i = 0; i < object->field_count(); ++i) {
|
||||
|
@ -59,6 +59,7 @@ class EscapeStatusAnalysis {
|
||||
bool HasEntry(Node* node);
|
||||
void Resize();
|
||||
size_t size();
|
||||
bool IsAllocationPhi(Node* node);
|
||||
|
||||
Graph* graph() const { return graph_; }
|
||||
Zone* zone() const { return zone_; }
|
||||
@ -92,6 +93,8 @@ class EscapeAnalysis {
|
||||
Node* GetReplacement(Node* node);
|
||||
bool IsVirtual(Node* node);
|
||||
bool IsEscaped(Node* node);
|
||||
bool CompareVirtualObjects(Node* left, Node* right);
|
||||
Node* GetOrCreateObjectState(Node* effect, Node* node);
|
||||
|
||||
private:
|
||||
void RunObjectAnalysis();
|
||||
@ -110,7 +113,6 @@ class EscapeAnalysis {
|
||||
VirtualState* states);
|
||||
|
||||
void ForwardVirtualState(Node* node);
|
||||
|
||||
bool IsEffectBranchPoint(Node* node);
|
||||
bool IsDanglingEffectNode(Node* node);
|
||||
int OffsetFromAccess(Node* node);
|
||||
|
@ -2,8 +2,9 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "src/base/functional.h"
|
||||
#include "src/compiler/frame-states.h"
|
||||
|
||||
#include "src/base/functional.h"
|
||||
#include "src/handles-inl.h"
|
||||
|
||||
namespace v8 {
|
||||
|
@ -307,6 +307,9 @@ InstructionOperand OperandForDeopt(OperandGenerator* g, Node* input,
|
||||
case IrOpcode::kFloat64Constant:
|
||||
case IrOpcode::kHeapConstant:
|
||||
return g->UseImmediate(input);
|
||||
case IrOpcode::kObjectState:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
default:
|
||||
switch (kind) {
|
||||
case FrameStateInputKind::kStackSlot:
|
||||
@ -314,21 +317,94 @@ InstructionOperand OperandForDeopt(OperandGenerator* g, Node* input,
|
||||
case FrameStateInputKind::kAny:
|
||||
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,
|
||||
InstructionOperandVector* inputs,
|
||||
FrameStateDescriptor* descriptor,
|
||||
FrameStateInputKind kind, Zone* zone) {
|
||||
// Returns the number of instruction operands added to inputs.
|
||||
size_t AddInputsToFrameStateDescriptor(FrameStateDescriptor* descriptor,
|
||||
Node* state, OperandGenerator* g,
|
||||
StateObjectDeduplicator* deduplicator,
|
||||
InstructionOperandVector* inputs,
|
||||
FrameStateInputKind kind, Zone* zone) {
|
||||
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()) {
|
||||
AddFrameStateInputs(state->InputAt(kFrameStateOuterStateInput), g, inputs,
|
||||
descriptor->outer_state(), kind, zone);
|
||||
entries += AddInputsToFrameStateDescriptor(
|
||||
descriptor->outer_state(), state->InputAt(kFrameStateOuterStateInput),
|
||||
g, deduplicator, inputs, kind, zone);
|
||||
}
|
||||
|
||||
Node* parameters = state->InputAt(kFrameStateParametersInput);
|
||||
@ -342,32 +418,34 @@ void AddFrameStateInputs(Node* state, OperandGenerator* g,
|
||||
DCHECK_EQ(descriptor->locals_count(), StateValuesAccess(locals).size());
|
||||
DCHECK_EQ(descriptor->stack_count(), StateValuesAccess(stack).size());
|
||||
|
||||
ZoneVector<MachineType> types(zone);
|
||||
types.reserve(descriptor->GetSize());
|
||||
|
||||
size_t value_index = 0;
|
||||
inputs->push_back(
|
||||
OperandForDeopt(g, function, FrameStateInputKind::kStackSlot));
|
||||
descriptor->SetType(value_index++, MachineType::AnyTagged());
|
||||
StateValueDescriptor* values_descriptor =
|
||||
descriptor->GetStateValueDescriptor();
|
||||
entries += AddOperandToStateValueDescriptor(
|
||||
values_descriptor, inputs, g, deduplicator, function,
|
||||
MachineType::AnyTagged(), FrameStateInputKind::kStackSlot, zone);
|
||||
for (StateValuesAccess::TypedNode input_node :
|
||||
StateValuesAccess(parameters)) {
|
||||
inputs->push_back(OperandForDeopt(g, input_node.node, kind));
|
||||
descriptor->SetType(value_index++, input_node.type);
|
||||
entries += AddOperandToStateValueDescriptor(values_descriptor, inputs, g,
|
||||
deduplicator, input_node.node,
|
||||
input_node.type, kind, zone);
|
||||
}
|
||||
if (descriptor->HasContext()) {
|
||||
inputs->push_back(
|
||||
OperandForDeopt(g, context, FrameStateInputKind::kStackSlot));
|
||||
descriptor->SetType(value_index++, MachineType::AnyTagged());
|
||||
entries += AddOperandToStateValueDescriptor(
|
||||
values_descriptor, inputs, g, deduplicator, context,
|
||||
MachineType::AnyTagged(), FrameStateInputKind::kStackSlot, zone);
|
||||
}
|
||||
for (StateValuesAccess::TypedNode input_node : StateValuesAccess(locals)) {
|
||||
inputs->push_back(OperandForDeopt(g, input_node.node, kind));
|
||||
descriptor->SetType(value_index++, input_node.type);
|
||||
entries += AddOperandToStateValueDescriptor(values_descriptor, inputs, g,
|
||||
deduplicator, input_node.node,
|
||||
input_node.type, kind, zone);
|
||||
}
|
||||
for (StateValuesAccess::TypedNode input_node : StateValuesAccess(stack)) {
|
||||
inputs->push_back(OperandForDeopt(g, input_node.node, kind));
|
||||
descriptor->SetType(value_index++, input_node.type);
|
||||
entries += AddOperandToStateValueDescriptor(values_descriptor, inputs, g,
|
||||
deduplicator, input_node.node,
|
||||
input_node.type, kind, zone);
|
||||
}
|
||||
DCHECK(value_index == descriptor->GetSize());
|
||||
DCHECK_EQ(initial_size + entries, inputs->size());
|
||||
return entries;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@ -500,6 +578,8 @@ void InstructionSelector::InitializeCallBuffer(Node* call, CallBuffer* buffer,
|
||||
// follows (n is the number of value inputs to the frame state):
|
||||
// arg 1 : deoptimization id.
|
||||
// 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) {
|
||||
InstructionSequence::StateId state_id =
|
||||
sequence()->AddFrameStateDescriptor(buffer->frame_state_descriptor);
|
||||
@ -507,12 +587,17 @@ void InstructionSelector::InitializeCallBuffer(Node* call, CallBuffer* buffer,
|
||||
|
||||
Node* frame_state =
|
||||
call->InputAt(static_cast<int>(buffer->descriptor->InputCount()));
|
||||
AddFrameStateInputs(frame_state, &g, &buffer->instruction_args,
|
||||
buffer->frame_state_descriptor,
|
||||
FrameStateInputKind::kStackSlot, instruction_zone());
|
||||
|
||||
StateObjectDeduplicator deduplicator(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());
|
||||
|
||||
@ -549,7 +634,7 @@ void InstructionSelector::InitializeCallBuffer(Node* call, CallBuffer* buffer,
|
||||
}
|
||||
}
|
||||
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 &&
|
||||
stack_param_delta != 0) {
|
||||
// For tail calls that change the size of their parameter list and keep
|
||||
@ -752,6 +837,7 @@ void InstructionSelector::VisitNode(Node* node) {
|
||||
return VisitCall(node);
|
||||
case IrOpcode::kFrameState:
|
||||
case IrOpcode::kStateValues:
|
||||
case IrOpcode::kObjectState:
|
||||
return;
|
||||
case IrOpcode::kLoad: {
|
||||
LoadRepresentation type = LoadRepresentationOf(node->op());
|
||||
@ -1470,19 +1556,19 @@ void InstructionSelector::VisitDeoptimize(DeoptimizeKind kind, Node* value) {
|
||||
OperandGenerator g(this);
|
||||
|
||||
FrameStateDescriptor* desc = GetFrameStateDescriptor(value);
|
||||
size_t arg_count = desc->GetTotalSize() + 1; // Include deopt id.
|
||||
|
||||
InstructionOperandVector args(instruction_zone());
|
||||
args.reserve(arg_count);
|
||||
args.reserve(desc->GetTotalSize() + 1); // Include deopt id.
|
||||
|
||||
InstructionSequence::StateId state_id =
|
||||
sequence()->AddFrameStateDescriptor(desc);
|
||||
args.push_back(g.TempImmediate(state_id.ToInt()));
|
||||
|
||||
AddFrameStateInputs(value, &g, &args, desc, FrameStateInputKind::kAny,
|
||||
instruction_zone());
|
||||
StateObjectDeduplicator deduplicator(instruction_zone());
|
||||
|
||||
DCHECK_EQ(args.size(), arg_count);
|
||||
AddInputsToFrameStateDescriptor(desc, value, &g, &deduplicator, &args,
|
||||
FrameStateInputKind::kAny,
|
||||
instruction_zone());
|
||||
|
||||
InstructionCode opcode = kArchDeoptimize;
|
||||
switch (kind) {
|
||||
@ -1493,7 +1579,7 @@ void InstructionSelector::VisitDeoptimize(DeoptimizeKind kind, Node* value) {
|
||||
opcode |= MiscField::encode(Deoptimizer::SOFT);
|
||||
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;
|
||||
class OperandGenerator;
|
||||
struct SwitchInfo;
|
||||
|
||||
typedef ZoneVector<InstructionOperand> InstructionOperandVector;
|
||||
|
||||
// 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.
|
||||
class PushParameter {
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include "src/compiler/graph.h"
|
||||
#include "src/compiler/instruction.h"
|
||||
#include "src/compiler/schedule.h"
|
||||
#include "src/compiler/state-values-utils.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
@ -831,11 +832,9 @@ FrameStateDescriptor::FrameStateDescriptor(
|
||||
parameters_count_(parameters_count),
|
||||
locals_count_(locals_count),
|
||||
stack_count_(stack_count),
|
||||
types_(zone),
|
||||
values_(zone),
|
||||
shared_info_(shared_info),
|
||||
outer_state_(outer_state) {
|
||||
types_.resize(GetSize(), MachineType::None());
|
||||
}
|
||||
outer_state_(outer_state) {}
|
||||
|
||||
|
||||
size_t FrameStateDescriptor::GetSize(OutputFrameStateCombine combine) 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) {
|
||||
return os << rpo.ToSize();
|
||||
}
|
||||
|
@ -23,8 +23,10 @@ namespace v8 {
|
||||
namespace internal {
|
||||
namespace compiler {
|
||||
|
||||
// Forward declarations.
|
||||
class Schedule;
|
||||
|
||||
|
||||
class InstructionOperand {
|
||||
public:
|
||||
static const int kInvalidVirtualRegister = -1;
|
||||
@ -108,6 +110,9 @@ class InstructionOperand {
|
||||
};
|
||||
|
||||
|
||||
typedef ZoneVector<InstructionOperand> InstructionOperandVector;
|
||||
|
||||
|
||||
struct PrintableInstructionOperand {
|
||||
const RegisterConfiguration* register_configuration_;
|
||||
InstructionOperand op_;
|
||||
@ -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 {
|
||||
public:
|
||||
FrameStateDescriptor(Zone* zone, FrameStateType type, BailoutId bailout_id,
|
||||
@ -968,8 +1026,10 @@ class FrameStateDescriptor : public ZoneObject {
|
||||
size_t GetFrameCount() const;
|
||||
size_t GetJSFrameCount() const;
|
||||
|
||||
MachineType GetType(size_t index) const;
|
||||
void SetType(size_t index, MachineType type);
|
||||
MachineType GetType(size_t index) const {
|
||||
return values_.GetOperandType(index);
|
||||
}
|
||||
StateValueDescriptor* GetStateValueDescriptor() { return &values_; }
|
||||
|
||||
private:
|
||||
FrameStateType type_;
|
||||
@ -978,12 +1038,13 @@ class FrameStateDescriptor : public ZoneObject {
|
||||
size_t parameters_count_;
|
||||
size_t locals_count_;
|
||||
size_t stack_count_;
|
||||
ZoneVector<MachineType> types_;
|
||||
StateValueDescriptor values_;
|
||||
MaybeHandle<SharedFunctionInfo> const shared_info_;
|
||||
FrameStateDescriptor* outer_state_;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const Constant& constant);
|
||||
|
||||
typedef ZoneVector<FrameStateDescriptor*> DeoptimizationVector;
|
||||
|
||||
|
||||
class PhiInstruction final : public ZoneObject {
|
||||
@ -1100,9 +1161,10 @@ typedef std::map<int, Constant, std::less<int>,
|
||||
|
||||
typedef ZoneDeque<Instruction*> InstructionDeque;
|
||||
typedef ZoneDeque<ReferenceMap*> ReferenceMapDeque;
|
||||
typedef ZoneVector<FrameStateDescriptor*> DeoptimizationVector;
|
||||
typedef ZoneVector<InstructionBlock*> InstructionBlocks;
|
||||
|
||||
|
||||
// Forward declarations.
|
||||
struct PrintableInstructionSequence;
|
||||
|
||||
|
||||
|
@ -50,6 +50,7 @@
|
||||
V(FrameState) \
|
||||
V(StateValues) \
|
||||
V(TypedStateValues) \
|
||||
V(ObjectState) \
|
||||
V(Call) \
|
||||
V(Parameter) \
|
||||
V(OsrValue) \
|
||||
|
@ -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) {
|
||||
return Type::Internal(zone());
|
||||
}
|
||||
|
@ -436,6 +436,7 @@ void Verifier::Visitor::Check(Node* node) {
|
||||
CHECK_EQ(6, input_count);
|
||||
break;
|
||||
case IrOpcode::kStateValues:
|
||||
case IrOpcode::kObjectState:
|
||||
case IrOpcode::kTypedStateValues:
|
||||
// TODO(jarin): what are the constraints on these?
|
||||
break;
|
||||
|
@ -3486,6 +3486,37 @@ Handle<Object> TranslatedState::MaterializeAt(int frame_index,
|
||||
object->set_length(*length);
|
||||
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:
|
||||
PrintF(stderr, "[couldn't handle instance type %d]\n",
|
||||
map->instance_type());
|
||||
|
47
test/mjsunit/compiler/escape-analysis-deopt-1.js
Normal file
47
test/mjsunit/compiler/escape-analysis-deopt-1.js
Normal file
@ -0,0 +1,47 @@
|
||||
// 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);
|
||||
})();
|
50
test/mjsunit/compiler/escape-analysis-deopt-2.js
Normal file
50
test/mjsunit/compiler/escape-analysis-deopt-2.js
Normal file
@ -0,0 +1,50 @@
|
||||
// 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();
|
||||
})();
|
51
test/mjsunit/compiler/escape-analysis-deopt-3.js
Normal file
51
test/mjsunit/compiler/escape-analysis-deopt-3.js
Normal file
@ -0,0 +1,51 @@
|
||||
// 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();
|
||||
})();
|
57
test/mjsunit/compiler/escape-analysis-deopt-4.js
Normal file
57
test/mjsunit/compiler/escape-analysis-deopt-4.js
Normal file
@ -0,0 +1,57 @@
|
||||
// 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();
|
||||
})();
|
41
test/mjsunit/compiler/escape-analysis-deopt-5.js
Normal file
41
test/mjsunit/compiler/escape-analysis-deopt-5.js
Normal file
@ -0,0 +1,41 @@
|
||||
// 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 @@
|
||||
# TODO(titzer): too slow in --turbo mode due to O(n^2) graph verification.
|
||||
'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.
|
||||
'regress-sync-optimized-lists': [PASS, NO_VARIANTS],
|
||||
'regress/regress-store-uncacheable': [PASS, NO_VARIANTS],
|
||||
|
@ -300,6 +300,90 @@ TEST_F(EscapeAnalysisTest, DanglingLoadOrder) {
|
||||
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 internal
|
||||
} // namespace v8
|
||||
|
Loading…
Reference in New Issue
Block a user