Commit 09e4a11b authored by jarin's avatar jarin Committed by Commit bot

[turbofan] Improve memory consumption for state values descriptors.

Review-Url: https://codereview.chromium.org/2546113002
Cr-Commit-Position: refs/heads/master@{#41469}
parent c17ea79e
......@@ -58,6 +58,7 @@ CodeGenerator::CodeGenerator(
jump_tables_(nullptr),
ools_(nullptr),
osr_pc_offset_(-1),
optimized_out_literal_id_(-1),
source_position_table_builder_(code->zone(),
info->SourcePositionRecordingMode()),
protected_instructions_(protected_instructions) {
......@@ -640,15 +641,6 @@ void CodeGenerator::RecordCallPosition(Instruction* instr) {
deopt_state_id = BuildTranslation(instr, -1, frame_state_offset,
OutputFrameStateCombine::Ignore());
}
#if DEBUG
// Make sure all the values live in stack slots or they are immediates.
// (The values should not live in register because registers are clobbered
// by calls.)
for (size_t i = 0; i < descriptor->GetSize(); i++) {
InstructionOperand* op = instr->InputAt(frame_state_offset + 1 + i);
CHECK(op->IsStackSlot() || op->IsFPStackSlot() || op->IsImmediate());
}
#endif
safepoints()->RecordLazyDeoptimizationIndex(deopt_state_id);
}
}
......@@ -678,19 +670,26 @@ DeoptimizeReason CodeGenerator::GetDeoptimizationReason(
}
void CodeGenerator::TranslateStateValueDescriptor(
StateValueDescriptor* desc, Translation* translation,
InstructionOperandIterator* iter) {
StateValueDescriptor* desc, StateValueList* nested,
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);
translation->BeginCapturedObject(static_cast<int>(nested->size()));
for (auto field : *nested) {
TranslateStateValueDescriptor(field.desc, field.nested, translation,
iter);
}
} else if (desc->IsDuplicate()) {
translation->DuplicateObject(static_cast<int>(desc->id()));
} else {
DCHECK(desc->IsPlain());
} else if (desc->IsPlain()) {
AddTranslationForOperand(translation, iter->instruction(), iter->Advance(),
desc->type());
} else {
DCHECK(desc->IsOptimizedOut());
if (optimized_out_literal_id_ == -1) {
optimized_out_literal_id_ =
DefineDeoptimizationLiteral(isolate()->factory()->optimized_out());
}
translation->StoreLiteral(optimized_out_literal_id_);
}
}
......@@ -698,44 +697,39 @@ void CodeGenerator::TranslateStateValueDescriptor(
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;
size_t index = 0;
StateValueList* values = desc->GetStateValueDescriptors();
for (StateValueList::iterator it = values->begin(); it != values->end();
++it, ++index) {
if (combine.kind() == 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;
}
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);
TranslateStateValueDescriptor((*it).desc, (*it).nested, translation, iter);
}
DCHECK_EQ(desc->GetSize(OutputFrameStateCombine::Ignore()), index);
if (combine.kind() == OutputFrameStateCombine::kPushOutput) {
DCHECK(combine.GetPushCount() <= iter->instruction()->OutputCount());
for (size_t output = 0; output < combine.GetPushCount(); output++) {
// Materialize the result of the call instruction in this slot.
AddTranslationForOperand(translation, iter->instruction(),
iter->instruction()->OutputAt(output),
MachineType::AnyTagged());
}
}
}
......
......@@ -218,6 +218,7 @@ class CodeGenerator final : public GapResolver::Assembler {
FrameStateDescriptor* descriptor, InstructionOperandIterator* iter,
Translation* translation, OutputFrameStateCombine state_combine);
void TranslateStateValueDescriptor(StateValueDescriptor* desc,
StateValueList* nested,
Translation* translation,
InstructionOperandIterator* iter);
void TranslateFrameStateDescriptorOperands(FrameStateDescriptor* desc,
......@@ -284,6 +285,7 @@ class CodeGenerator final : public GapResolver::Assembler {
JumpTable* jump_tables_;
OutOfLineCode* ools_;
int osr_pc_offset_;
int optimized_out_literal_id_;
SourcePositionTableBuilder source_position_table_builder_;
ZoneVector<trap_handler::ProtectedInstructionData>* protected_instructions_;
};
......
......@@ -414,11 +414,8 @@ void InstructionSelector::MarkAsRepresentation(MachineRepresentation rep,
sequence()->MarkAsRepresentation(rep, GetVirtualRegister(node));
}
namespace {
enum class FrameStateInputKind { kAny, kStackSlot };
InstructionOperand OperandForDeopt(OperandGenerator* g, Node* input,
FrameStateInputKind kind,
MachineRepresentation rep) {
......@@ -452,6 +449,7 @@ InstructionOperand OperandForDeopt(OperandGenerator* g, Node* input,
return InstructionOperand();
}
} // namespace
class StateObjectDeduplicator {
public:
......@@ -477,14 +475,11 @@ class StateObjectDeduplicator {
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) {
size_t InstructionSelector::AddOperandToStateValueDescriptor(
StateValueList* values, InstructionOperandVector* inputs,
OperandGenerator* g, StateObjectDeduplicator* deduplicator, Node* input,
MachineType type, FrameStateInputKind kind, Zone* zone) {
switch (input->opcode()) {
case IrOpcode::kObjectState: {
UNREACHABLE();
......@@ -495,29 +490,36 @@ size_t AddOperandToStateValueDescriptor(StateValueDescriptor* descriptor,
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();
StateValueList* nested = values->PushRecursiveField(zone, id);
int const input_count = input->op()->ValueInputCount();
ZoneVector<MachineType> const* types = MachineTypesOf(input->op());
for (int i = 0; i < input_count; ++i) {
entries += AddOperandToStateValueDescriptor(
new_desc, inputs, g, deduplicator, input->InputAt(i),
types->at(i), kind, zone);
nested, inputs, g, deduplicator, input->InputAt(i), types->at(i),
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));
values->PushDuplicate(id);
return 0;
}
}
default: {
Heap* const heap = isolate()->heap();
if (input->opcode() == IrOpcode::kHeapConstant) {
Handle<HeapObject> constant = OpParameter<Handle<HeapObject>>(input);
Heap::RootListIndex root_index;
if (heap->IsRootHandle(constant, &root_index) &&
root_index == Heap::kOptimizedOutRootIndex) {
values->PushOptimizedOut();
return 0;
}
}
inputs->push_back(OperandForDeopt(g, input, kind, type.representation()));
descriptor->fields().push_back(StateValueDescriptor::Plain(zone, type));
values->PushPlain(type);
return 1;
}
}
......@@ -525,11 +527,10 @@ size_t AddOperandToStateValueDescriptor(StateValueDescriptor* descriptor,
// 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) {
size_t InstructionSelector::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;
......@@ -553,8 +554,7 @@ size_t AddInputsToFrameStateDescriptor(FrameStateDescriptor* descriptor,
DCHECK_EQ(descriptor->locals_count(), StateValuesAccess(locals).size());
DCHECK_EQ(descriptor->stack_count(), StateValuesAccess(stack).size());
StateValueDescriptor* values_descriptor =
descriptor->GetStateValueDescriptor();
StateValueList* values_descriptor = descriptor->GetStateValueDescriptors();
entries += AddOperandToStateValueDescriptor(
values_descriptor, inputs, g, deduplicator, function,
MachineType::AnyTagged(), FrameStateInputKind::kStackSlot, zone);
......@@ -583,8 +583,6 @@ size_t AddInputsToFrameStateDescriptor(FrameStateDescriptor* descriptor,
return entries;
}
} // namespace
// An internal helper class for generating the operands to calls.
// TODO(bmeurer): Get rid of the CallBuffer business and make
......
......@@ -26,6 +26,7 @@ class FlagsContinuation;
class Linkage;
class OperandGenerator;
struct SwitchInfo;
class StateObjectDeduplicator;
// 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.
......@@ -42,6 +43,8 @@ class PushParameter {
MachineType type_;
};
enum class FrameStateInputKind { kAny, kStackSlot };
// Instruction selection generates an InstructionSequence for a given Schedule.
class V8_EXPORT_PRIVATE InstructionSelector final {
public:
......@@ -286,6 +289,17 @@ class V8_EXPORT_PRIVATE InstructionSelector final {
int GetTempsCountForTailCallFromJSFunction();
FrameStateDescriptor* GetFrameStateDescriptor(Node* node);
size_t AddInputsToFrameStateDescriptor(FrameStateDescriptor* descriptor,
Node* state, OperandGenerator* g,
StateObjectDeduplicator* deduplicator,
InstructionOperandVector* inputs,
FrameStateInputKind kind, Zone* zone);
size_t AddOperandToStateValueDescriptor(StateValueList* values,
InstructionOperandVector* inputs,
OperandGenerator* g,
StateObjectDeduplicator* deduplicator,
Node* input, MachineType type,
FrameStateInputKind kind, Zone* zone);
// ===========================================================================
// ============= Architecture-specific graph covering methods. ===============
......
......@@ -1104,52 +1104,123 @@ std::ostream& operator<<(std::ostream& os, const Constant& constant);
// Forward declarations.
class FrameStateDescriptor;
enum class StateValueKind { kPlain, kNested, kDuplicate };
enum class StateValueKind : uint8_t {
kPlain,
kOptimizedOut,
kNested,
kDuplicate
};
class StateValueDescriptor {
public:
explicit StateValueDescriptor(Zone* zone)
StateValueDescriptor()
: kind_(StateValueKind::kPlain),
type_(MachineType::AnyTagged()),
id_(0),
fields_(zone) {}
id_(0) {}
static StateValueDescriptor Plain(Zone* zone, MachineType type) {
return StateValueDescriptor(StateValueKind::kPlain, zone, type, 0);
static StateValueDescriptor Plain(MachineType type) {
return StateValueDescriptor(StateValueKind::kPlain, type, 0);
}
static StateValueDescriptor Recursive(Zone* zone, size_t id) {
return StateValueDescriptor(StateValueKind::kNested, zone,
static StateValueDescriptor OptimizedOut() {
return StateValueDescriptor(StateValueKind::kOptimizedOut,
MachineType::AnyTagged(), 0);
}
static StateValueDescriptor Recursive(size_t id) {
return StateValueDescriptor(StateValueKind::kNested,
MachineType::AnyTagged(), id);
}
static StateValueDescriptor Duplicate(Zone* zone, size_t id) {
return StateValueDescriptor(StateValueKind::kDuplicate, zone,
static StateValueDescriptor Duplicate(size_t id) {
return StateValueDescriptor(StateValueKind::kDuplicate,
MachineType::AnyTagged(), id);
}
size_t size() { return fields_.size(); }
ZoneVector<StateValueDescriptor>& fields() { return fields_; }
int IsPlain() { return kind_ == StateValueKind::kPlain; }
int IsOptimizedOut() { return kind_ == StateValueKind::kOptimizedOut; }
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) {}
StateValueDescriptor(StateValueKind kind, MachineType type, size_t id)
: kind_(kind), type_(type), id_(id) {}
StateValueKind kind_;
MachineType type_;
size_t id_;
ZoneVector<StateValueDescriptor> fields_;
};
class StateValueList {
public:
explicit StateValueList(Zone* zone) : fields_(zone), nested_(zone) {}
size_t size() { return fields_.size(); }
struct Value {
StateValueDescriptor* desc;
StateValueList* nested;
Value(StateValueDescriptor* desc, StateValueList* nested)
: desc(desc), nested(nested) {}
};
class iterator {
public:
// Bare minimum of operators needed for range iteration.
bool operator!=(const iterator& other) const {
return field_iterator != other.field_iterator;
}
bool operator==(const iterator& other) const {
return field_iterator == other.field_iterator;
}
iterator& operator++() {
if (field_iterator->IsNested()) {
nested_iterator++;
}
++field_iterator;
return *this;
}
Value operator*() {
StateValueDescriptor* desc = &(*field_iterator);
StateValueList* nested = desc->IsNested() ? *nested_iterator : nullptr;
return Value(desc, nested);
}
private:
friend class StateValueList;
iterator(ZoneVector<StateValueDescriptor>::iterator it,
ZoneVector<StateValueList*>::iterator nested)
: field_iterator(it), nested_iterator(nested) {}
ZoneVector<StateValueDescriptor>::iterator field_iterator;
ZoneVector<StateValueList*>::iterator nested_iterator;
};
StateValueList* PushRecursiveField(Zone* zone, size_t id) {
fields_.push_back(StateValueDescriptor::Recursive(id));
StateValueList* nested =
new (zone->New(sizeof(StateValueList))) StateValueList(zone);
nested_.push_back(nested);
return nested;
}
void PushDuplicate(size_t id) {
fields_.push_back(StateValueDescriptor::Duplicate(id));
}
void PushPlain(MachineType type) {
fields_.push_back(StateValueDescriptor::Plain(type));
}
void PushOptimizedOut() {
fields_.push_back(StateValueDescriptor::OptimizedOut());
}
iterator begin() { return iterator(fields_.begin(), nested_.begin()); }
iterator end() { return iterator(fields_.end(), nested_.end()); }
private:
ZoneVector<StateValueDescriptor> fields_;
ZoneVector<StateValueList*> nested_;
};
class FrameStateDescriptor : public ZoneObject {
public:
......@@ -1178,10 +1249,7 @@ class FrameStateDescriptor : public ZoneObject {
size_t GetFrameCount() const;
size_t GetJSFrameCount() const;
MachineType GetType(size_t index) const {
return values_.GetOperandType(index);
}
StateValueDescriptor* GetStateValueDescriptor() { return &values_; }
StateValueList* GetStateValueDescriptors() { return &values_; }
static const int kImpossibleValue = 0xdead;
......@@ -1192,7 +1260,7 @@ class FrameStateDescriptor : public ZoneObject {
size_t parameters_count_;
size_t locals_count_;
size_t stack_count_;
StateValueDescriptor values_;
StateValueList values_;
MaybeHandle<SharedFunctionInfo> const shared_info_;
FrameStateDescriptor* outer_state_;
};
......
......@@ -477,15 +477,6 @@ TARGET_TEST_F(InstructionSelectorTest, CallStubWithDeopt) {
// We inserted 0 here.
EXPECT_EQ(0.5, s.ToFloat64(call_instr->InputAt(5)));
EXPECT_TRUE(s.ToHeapObject(call_instr->InputAt(6))->IsUndefined(isolate()));
EXPECT_EQ(MachineType::AnyTagged(),
desc_before->GetType(0)); // function is always
// tagged/any.
EXPECT_EQ(MachineType::Int32(), desc_before->GetType(1));
EXPECT_EQ(MachineType::AnyTagged(),
desc_before->GetType(2)); // context is always
// tagged/any.
EXPECT_EQ(MachineType::Float64(), desc_before->GetType(3));
EXPECT_EQ(MachineType::AnyTagged(), desc_before->GetType(4));
// Function.
EXPECT_EQ(s.ToVreg(function_node), s.ToVreg(call_instr->InputAt(7)));
......@@ -585,31 +576,20 @@ TARGET_TEST_F(InstructionSelectorTest, CallStubWithDeoptRecursiveFrameState) {
EXPECT_EQ(1u, desc_before_outer->locals_count());
EXPECT_EQ(1u, desc_before_outer->stack_count());
// Values from parent environment.
EXPECT_EQ(MachineType::AnyTagged(), desc_before->GetType(0));
EXPECT_EQ(63, s.ToInt32(call_instr->InputAt(3)));
EXPECT_EQ(MachineType::Int32(), desc_before_outer->GetType(1));
// Context:
EXPECT_EQ(66, s.ToInt32(call_instr->InputAt(4)));
EXPECT_EQ(MachineType::AnyTagged(), desc_before_outer->GetType(2));
EXPECT_EQ(64, s.ToInt32(call_instr->InputAt(5)));
EXPECT_EQ(MachineType::Int32(), desc_before_outer->GetType(3));
EXPECT_EQ(65, s.ToInt32(call_instr->InputAt(6)));
EXPECT_EQ(MachineType::Int32(), desc_before_outer->GetType(4));
// Values from the nested frame.
EXPECT_EQ(1u, desc_before->parameters_count());
EXPECT_EQ(1u, desc_before->locals_count());
EXPECT_EQ(2u, desc_before->stack_count());
EXPECT_EQ(MachineType::AnyTagged(), desc_before->GetType(0));
EXPECT_EQ(43, s.ToInt32(call_instr->InputAt(8)));
EXPECT_EQ(MachineType::Int32(), desc_before->GetType(1));
EXPECT_EQ(46, s.ToInt32(call_instr->InputAt(9)));
EXPECT_EQ(MachineType::AnyTagged(), desc_before->GetType(2));
EXPECT_EQ(0.25, s.ToFloat64(call_instr->InputAt(10)));
EXPECT_EQ(MachineType::Float64(), desc_before->GetType(3));
EXPECT_EQ(44, s.ToInt32(call_instr->InputAt(11)));
EXPECT_EQ(MachineType::Int32(), desc_before->GetType(4));
EXPECT_EQ(45, s.ToInt32(call_instr->InputAt(12)));
EXPECT_EQ(MachineType::Int32(), desc_before->GetType(5));
// Function.
EXPECT_EQ(s.ToVreg(function_node), s.ToVreg(call_instr->InputAt(13)));
......
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