Commit e61ff991 authored by tebbi's avatar tebbi Committed by Commit bot

[turbofan] compute arguments length in deoptimizer

Following the design of https://codereview.chromium.org/2692753004, also compute the arguments length in the deoptimizer sucht that it does not have to be computed in optimized code.

R=bmeurer@chromium.org

Review-Url: https://codereview.chromium.org/2729163002
Cr-Commit-Position: refs/heads/master@{#43587}
parent b7944a5c
......@@ -702,6 +702,10 @@ void CodeGenerator::TranslateStateValueDescriptor(
if (translation != nullptr) {
translation->ArgumentsElements(desc->is_rest());
}
} else if (desc->IsArgumentsLength()) {
if (translation != nullptr) {
translation->ArgumentsLength(desc->is_rest());
}
} else if (desc->IsDuplicate()) {
if (translation != nullptr) {
translation->DuplicateObject(static_cast<int>(desc->id()));
......
......@@ -1239,8 +1239,16 @@ const Operator* CommonOperatorBuilder::ArgumentsElementsState(bool is_rest) {
0, 0, 0, 1, 0, 0, is_rest); // counts
}
const Operator* CommonOperatorBuilder::ArgumentsLengthState(bool is_rest) {
return new (zone()) Operator1<bool>( // --
IrOpcode::kArgumentsLengthState, Operator::kPure, // opcode
"ArgumentsLengthState", // name
0, 0, 0, 1, 0, 0, is_rest); // counts
}
bool IsRestOf(Operator const* op) {
DCHECK(op->opcode() == IrOpcode::kArgumentsElementsState);
DCHECK(op->opcode() == IrOpcode::kArgumentsElementsState ||
op->opcode() == IrOpcode::kArgumentsLengthState);
return OpParameter<bool>(op);
}
......
......@@ -297,9 +297,9 @@ SparseInputMask SparseInputMaskOf(Operator const*);
ZoneVector<MachineType> const* MachineTypesOf(Operator const*)
WARN_UNUSED_RESULT;
// The ArgumentsElementsState can either describe an unmapped arguments backing
// store or the backing store of the rest parameters. IsRestOf(op) is true in
// the second case.
// The ArgumentsElementsState and ArgumentsLengthState can either describe an
// unmapped arguments backing store or the backing store of the rest parameters.
// IsRestOf(op) is true in the second case.
bool IsRestOf(Operator const*);
// Interface for building common operators that can be used at any level of IR,
......@@ -368,6 +368,7 @@ class V8_EXPORT_PRIVATE CommonOperatorBuilder final
const Operator* TypedStateValues(const ZoneVector<MachineType>* types,
SparseInputMask bitmask);
const Operator* ArgumentsElementsState(bool is_rest);
const Operator* ArgumentsLengthState(bool is_rest);
const Operator* ObjectState(int pointer_slots);
const Operator* TypedObjectState(const ZoneVector<MachineType>* types);
const Operator* FrameState(BailoutId bailout_id,
......
......@@ -411,6 +411,28 @@ void EscapeAnalysisReducer::Finalize() {
Node* arguments_length = NodeProperties::GetValueInput(node, 1);
if (arguments_length->opcode() != IrOpcode::kArgumentsLength) continue;
Node* arguments_length_state = nullptr;
for (Edge edge : arguments_length->use_edges()) {
Node* use = edge.from();
switch (use->opcode()) {
case IrOpcode::kObjectState:
case IrOpcode::kTypedObjectState:
case IrOpcode::kStateValues:
case IrOpcode::kTypedStateValues:
if (!arguments_length_state) {
arguments_length_state = jsgraph()->graph()->NewNode(
jsgraph()->common()->ArgumentsLengthState(
IsRestLengthOf(arguments_length->op())));
NodeProperties::SetType(arguments_length_state,
Type::OtherInternal());
}
edge.UpdateTo(arguments_length_state);
break;
default:
break;
}
}
bool escaping_use = false;
ZoneVector<Node*> loads(zone());
for (Edge edge : node->use_edges()) {
......
......@@ -454,6 +454,7 @@ InstructionOperand OperandForDeopt(Isolate* isolate, OperandGenerator* g,
return g->UseImmediate(input);
}
case IrOpcode::kArgumentsElementsState:
case IrOpcode::kArgumentsLengthState:
case IrOpcode::kObjectState:
case IrOpcode::kTypedObjectState:
UNREACHABLE();
......@@ -513,6 +514,10 @@ size_t InstructionSelector::AddOperandToStateValueDescriptor(
values->PushArgumentsElements(IsRestOf(input->op()));
return 0;
}
case IrOpcode::kArgumentsLengthState: {
values->PushArgumentsLength(IsRestOf(input->op()));
return 0;
}
case IrOpcode::kObjectState: {
UNREACHABLE();
return 0;
......
......@@ -1126,6 +1126,7 @@ class FrameStateDescriptor;
enum class StateValueKind : uint8_t {
kArgumentsElements,
kArgumentsLength,
kPlain,
kOptimizedOut,
kNested,
......@@ -1143,6 +1144,12 @@ class StateValueDescriptor {
descr.is_rest_ = is_rest;
return descr;
}
static StateValueDescriptor ArgumentsLength(bool is_rest) {
StateValueDescriptor descr(StateValueKind::kArgumentsLength,
MachineType::AnyTagged());
descr.is_rest_ = is_rest;
return descr;
}
static StateValueDescriptor Plain(MachineType type) {
return StateValueDescriptor(StateValueKind::kPlain, type);
}
......@@ -1166,6 +1173,9 @@ class StateValueDescriptor {
bool IsArgumentsElements() const {
return kind_ == StateValueKind::kArgumentsElements;
}
bool IsArgumentsLength() const {
return kind_ == StateValueKind::kArgumentsLength;
}
bool IsPlain() const { return kind_ == StateValueKind::kPlain; }
bool IsOptimizedOut() const { return kind_ == StateValueKind::kOptimizedOut; }
bool IsNested() const { return kind_ == StateValueKind::kNested; }
......@@ -1177,7 +1187,8 @@ class StateValueDescriptor {
return id_;
}
int is_rest() const {
DCHECK(kind_ == StateValueKind::kArgumentsElements);
DCHECK(kind_ == StateValueKind::kArgumentsElements ||
kind_ == StateValueKind::kArgumentsLength);
return is_rest_;
}
......@@ -1252,6 +1263,9 @@ class StateValueList {
void PushArgumentsElements(bool is_rest) {
fields_.push_back(StateValueDescriptor::ArgumentsElements(is_rest));
}
void PushArgumentsLength(bool is_rest) {
fields_.push_back(StateValueDescriptor::ArgumentsLength(is_rest));
}
void PushDuplicate(size_t id) {
fields_.push_back(StateValueDescriptor::Duplicate(id));
}
......
......@@ -60,6 +60,7 @@
V(StateValues) \
V(TypedStateValues) \
V(ArgumentsElementsState) \
V(ArgumentsLengthState) \
V(ObjectState) \
V(TypedObjectState) \
V(Call) \
......
......@@ -2725,6 +2725,7 @@ class RepresentationSelector {
case IrOpcode::kProjection:
case IrOpcode::kOsrValue:
case IrOpcode::kArgumentsElementsState:
case IrOpcode::kArgumentsLengthState:
// All JavaScript operators except JSToNumber have uniform handling.
#define OPCODE_CASE(name) case IrOpcode::k##name:
JS_SIMPLE_BINOP_LIST(OPCODE_CASE)
......
......@@ -848,6 +848,10 @@ Type* Typer::Visitor::TypeArgumentsElementsState(Node* node) {
return Type::Internal();
}
Type* Typer::Visitor::TypeArgumentsLengthState(Node* node) {
return Type::Internal();
}
Type* Typer::Visitor::TypeObjectState(Node* node) { return Type::Internal(); }
Type* Typer::Visitor::TypeTypedObjectState(Node* node) {
......
......@@ -498,6 +498,7 @@ void Verifier::Visitor::Check(Node* node) {
case IrOpcode::kStateValues:
case IrOpcode::kTypedStateValues:
case IrOpcode::kArgumentsElementsState:
case IrOpcode::kArgumentsLengthState:
case IrOpcode::kObjectState:
case IrOpcode::kTypedObjectState:
// TODO(jarin): what are the constraints on these?
......
......@@ -2407,6 +2407,11 @@ void Translation::ArgumentsElements(bool is_rest) {
buffer_->Add(is_rest);
}
void Translation::ArgumentsLength(bool is_rest) {
buffer_->Add(ARGUMENTS_LENGTH);
buffer_->Add(is_rest);
}
void Translation::BeginCapturedObject(int length) {
buffer_->Add(CAPTURED_OBJECT);
buffer_->Add(length);
......@@ -2540,6 +2545,7 @@ int Translation::NumberOfOperandsFor(Opcode opcode) {
case CONSTRUCT_STUB_FRAME:
return 3;
case ARGUMENTS_ELEMENTS:
case ARGUMENTS_LENGTH:
return 1;
}
FATAL("Unexpected translation type");
......@@ -3309,6 +3315,7 @@ TranslatedFrame TranslatedState::CreateNextTranslatedFrame(
case Translation::DUPLICATED_OBJECT:
case Translation::ARGUMENTS_OBJECT:
case Translation::ARGUMENTS_ELEMENTS:
case Translation::ARGUMENTS_LENGTH:
case Translation::CAPTURED_OBJECT:
case Translation::REGISTER:
case Translation::INT32_REGISTER:
......@@ -3344,38 +3351,48 @@ void TranslatedFrame::AdvanceIterator(
}
}
// Creates translated values for an arguments backing store, or the backing
// store for the rest parameters if {is_rest} is true. The TranslatedValue
// objects for the fields are not read from the TranslationIterator, but instead
// created on-the-fly based on dynamic information in the optimized frame.
void TranslatedState::CreateArgumentsElementsTranslatedValues(
int frame_index, Address input_frame_pointer, bool is_rest) {
TranslatedFrame& frame = frames_[frame_index];
Address TranslatedState::ComputeArgumentsPosition(Address input_frame_pointer,
bool is_rest, int* length) {
Address parent_frame_pointer = *reinterpret_cast<Address*>(
input_frame_pointer + StandardFrameConstants::kCallerFPOffset);
intptr_t parent_frame_type = Memory::intptr_at(
parent_frame_pointer + CommonFrameConstants::kContextOrFrameTypeOffset);
int length;
Address arguments_frame;
if (parent_frame_type ==
StackFrame::TypeToMarker(StackFrame::ARGUMENTS_ADAPTOR)) {
length = Smi::cast(*reinterpret_cast<Object**>(
parent_frame_pointer +
ArgumentsAdaptorFrameConstants::kLengthOffset))
->value();
if (length)
*length = Smi::cast(*reinterpret_cast<Object**>(
parent_frame_pointer +
ArgumentsAdaptorFrameConstants::kLengthOffset))
->value();
arguments_frame = parent_frame_pointer;
} else {
length = formal_parameter_count_;
if (length) *length = formal_parameter_count_;
arguments_frame = input_frame_pointer;
}
if (is_rest) {
// If the actual number of arguments is less than the number of formal
// parameters, we have zero rest parameters.
length = std::max(0, length - formal_parameter_count_);
if (length) *length = std::max(0, *length - formal_parameter_count_);
}
return arguments_frame;
}
// Creates translated values for an arguments backing store, or the backing
// store for the rest parameters if {is_rest} is true. The TranslatedValue
// objects for the fields are not read from the TranslationIterator, but instead
// created on-the-fly based on dynamic information in the optimized frame.
void TranslatedState::CreateArgumentsElementsTranslatedValues(
int frame_index, Address input_frame_pointer, bool is_rest) {
TranslatedFrame& frame = frames_[frame_index];
int length;
Address arguments_frame =
ComputeArgumentsPosition(input_frame_pointer, is_rest, &length);
int object_index = static_cast<int>(object_positions_.size());
int value_index = static_cast<int>(frame.values_.size());
object_positions_.push_back({frame_index, value_index});
......@@ -3459,6 +3476,14 @@ int TranslatedState::CreateNextTranslatedValue(
return 0;
}
case Translation::ARGUMENTS_LENGTH: {
bool is_rest = iterator->Next();
int length;
ComputeArgumentsPosition(fp, is_rest, &length);
frame.Add(TranslatedValue::NewInt32(this, length));
return 0;
}
case Translation::CAPTURED_OBJECT: {
int field_count = iterator->Next();
int object_index = static_cast<int>(object_positions_.size());
......
......@@ -318,6 +318,8 @@ class TranslatedState {
int CreateNextTranslatedValue(int frame_index, TranslationIterator* iterator,
FixedArray* literal_array, Address fp,
RegisterValues* registers, FILE* trace_file);
Address ComputeArgumentsPosition(Address input_frame_pointer, bool is_rest,
int* length);
void CreateArgumentsElementsTranslatedValues(int frame_index,
Address input_frame_pointer,
bool is_rest);
......@@ -935,6 +937,7 @@ class TranslationIterator BASE_EMBEDDED {
V(DUPLICATED_OBJECT) \
V(ARGUMENTS_OBJECT) \
V(ARGUMENTS_ELEMENTS) \
V(ARGUMENTS_LENGTH) \
V(CAPTURED_OBJECT) \
V(REGISTER) \
V(INT32_REGISTER) \
......@@ -984,6 +987,7 @@ class Translation BASE_EMBEDDED {
void BeginSetterStubFrame(int literal_id);
void BeginArgumentsObject(int args_length);
void ArgumentsElements(bool is_rest);
void ArgumentsLength(bool is_rest);
void BeginCapturedObject(int length);
void DuplicateObject(int object_index);
void StoreRegister(Register reg);
......
......@@ -14479,7 +14479,8 @@ void DeoptimizationInputData::DeoptimizationInputDataPrint(
break;
}
case Translation::ARGUMENTS_ELEMENTS: {
case Translation::ARGUMENTS_ELEMENTS:
case Translation::ARGUMENTS_LENGTH: {
bool is_rest = iterator.Next();
os << "{is_rest=" << (is_rest ? "true" : "false") << "}";
break;
......
......@@ -96,8 +96,8 @@
var rest = arguments;
for (var i = 0; i < rest.length; ++i) {
var j = i;
if (rest.length % 15 == 0 && i == 10) j += rest.length;
sum += rest[j] || rest[j-rest.length];
if (rest.length % 15 == 0 && i == 10) j += 10000;
sum += rest[j] || i+1;
}
return sum;
};
......@@ -113,11 +113,10 @@
(function ArgumentsAccessSloppy () {
function sum2(a,b,c) {
var sum = 0;
var rest = arguments;
for (var i = 0; i < rest.length; ++i) {
for (var i = 0; i < arguments.length; ++i) {
var j = i;
if (rest.length % 15 == 0 && i == 10) j += rest.length;
sum += rest[j] || rest[j-rest.length];
if (arguments.length % 15 == 0 && i == 10) j += 10000;
sum += arguments[j] || i+1;
}
return sum;
};
......@@ -135,8 +134,8 @@
var sum = 0;
for (var i = 0; i < rest.length; ++i) {
var j = i;
if (rest.length % 15 == 0 && i == 10) j += rest.length;
sum += rest[j] || rest[j-rest.length];
if (rest.length % 15 == 0 && i == 10) j += 10000;
sum += rest[j] || i+1;
}
return sum;
};
......@@ -154,8 +153,8 @@
var sum = 0;
for (var i = 0; i < rest.length; ++i) {
var j = i;
if (rest.length % 15 == 0 && i == 10) j += rest.length;
sum += rest[j] || rest[j-rest.length];
if (rest.length % 15 == 0 && i == 10) j += 10000;
sum += rest[j] || i+2;
}
return sum;
};
......@@ -167,3 +166,18 @@
assertEquals(i*(i+1)/2-1, sum4(...args));
}
})();
(function ReadArguments () {
function read() {
if (arguments.length % 10 == 5) %DeoptimizeNow();
return arguments[arguments.length-1];
};
var args = []
for (var i = 1; i < 30; ++i) {
args.push(i);
if (i%10 == 0) %OptimizeFunctionOnNextCall(read);
assertEquals(i, read(...args));
}
})();
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