[interpreter] Record type feedback in the handlers for Inc and Dec.

BUG=v8:5273
R=rmcilroy@chromium.org

Review-Url: https://codereview.chromium.org/2250513005
Cr-Commit-Position: refs/heads/master@{#38751}
This commit is contained in:
epertoso 2016-08-19 05:58:43 -07:00 committed by Commit bot
parent 870763f5ce
commit 6949acab5c
7 changed files with 314 additions and 11 deletions

View File

@ -2382,7 +2382,9 @@ compiler::Node* BitwiseXorStub::Generate(CodeStubAssembler* assembler,
// static
compiler::Node* IncStub::Generate(CodeStubAssembler* assembler,
compiler::Node* value,
compiler::Node* context) {
compiler::Node* context,
compiler::Node* type_feedback_vector,
compiler::Node* slot_id) {
typedef CodeStubAssembler::Label Label;
typedef compiler::Node Node;
typedef CodeStubAssembler::Variable Variable;
@ -2394,8 +2396,12 @@ compiler::Node* IncStub::Generate(CodeStubAssembler* assembler,
// We might need to try again due to ToNumber conversion.
Variable value_var(assembler, MachineRepresentation::kTagged);
Variable result_var(assembler, MachineRepresentation::kTagged);
Label start(assembler, &value_var);
Variable var_type_feedback(assembler, MachineRepresentation::kWord32);
Variable* loop_vars[] = {&value_var, &var_type_feedback};
Label start(assembler, 2, loop_vars);
value_var.Bind(value);
var_type_feedback.Bind(
assembler->Int32Constant(BinaryOperationFeedback::kNone));
assembler->Goto(&start);
assembler->Bind(&start);
{
@ -2416,6 +2422,9 @@ compiler::Node* IncStub::Generate(CodeStubAssembler* assembler,
assembler->Branch(overflow, &if_overflow, &if_notoverflow);
assembler->Bind(&if_notoverflow);
var_type_feedback.Bind(assembler->Word32Or(
var_type_feedback.value(),
assembler->Int32Constant(BinaryOperationFeedback::kSignedSmall)));
result_var.Bind(assembler->Projection(0, pair));
assembler->Goto(&end);
@ -2448,6 +2457,8 @@ compiler::Node* IncStub::Generate(CodeStubAssembler* assembler,
// Convert to a Number first and try again.
Callable callable =
CodeFactory::NonNumberToNumber(assembler->isolate());
var_type_feedback.Bind(
assembler->Int32Constant(BinaryOperationFeedback::kAny));
value_var.Bind(assembler->CallStub(callable, context, value));
assembler->Goto(&start);
}
@ -2459,18 +2470,25 @@ compiler::Node* IncStub::Generate(CodeStubAssembler* assembler,
Node* finc_value = var_finc_value.value();
Node* one = assembler->Float64Constant(1.0);
Node* finc_result = assembler->Float64Add(finc_value, one);
var_type_feedback.Bind(assembler->Word32Or(
var_type_feedback.value(),
assembler->Int32Constant(BinaryOperationFeedback::kNumber)));
result_var.Bind(assembler->ChangeFloat64ToTagged(finc_result));
assembler->Goto(&end);
}
assembler->Bind(&end);
assembler->UpdateFeedback(var_type_feedback.value(), type_feedback_vector,
slot_id);
return result_var.value();
}
// static
compiler::Node* DecStub::Generate(CodeStubAssembler* assembler,
compiler::Node* value,
compiler::Node* context) {
compiler::Node* context,
compiler::Node* type_feedback_vector,
compiler::Node* slot_id) {
typedef CodeStubAssembler::Label Label;
typedef compiler::Node Node;
typedef CodeStubAssembler::Variable Variable;
@ -2482,7 +2500,11 @@ compiler::Node* DecStub::Generate(CodeStubAssembler* assembler,
// We might need to try again due to ToNumber conversion.
Variable value_var(assembler, MachineRepresentation::kTagged);
Variable result_var(assembler, MachineRepresentation::kTagged);
Label start(assembler, &value_var);
Variable var_type_feedback(assembler, MachineRepresentation::kWord32);
Variable* loop_vars[] = {&value_var, &var_type_feedback};
Label start(assembler, 2, loop_vars);
var_type_feedback.Bind(
assembler->Int32Constant(BinaryOperationFeedback::kNone));
value_var.Bind(value);
assembler->Goto(&start);
assembler->Bind(&start);
@ -2504,6 +2526,9 @@ compiler::Node* DecStub::Generate(CodeStubAssembler* assembler,
assembler->Branch(overflow, &if_overflow, &if_notoverflow);
assembler->Bind(&if_notoverflow);
var_type_feedback.Bind(assembler->Word32Or(
var_type_feedback.value(),
assembler->Int32Constant(BinaryOperationFeedback::kSignedSmall)));
result_var.Bind(assembler->Projection(0, pair));
assembler->Goto(&end);
@ -2536,6 +2561,8 @@ compiler::Node* DecStub::Generate(CodeStubAssembler* assembler,
// Convert to a Number first and try again.
Callable callable =
CodeFactory::NonNumberToNumber(assembler->isolate());
var_type_feedback.Bind(
assembler->Int32Constant(BinaryOperationFeedback::kAny));
value_var.Bind(assembler->CallStub(callable, context, value));
assembler->Goto(&start);
}
@ -2547,11 +2574,16 @@ compiler::Node* DecStub::Generate(CodeStubAssembler* assembler,
Node* fdec_value = var_fdec_value.value();
Node* one = assembler->Float64Constant(1.0);
Node* fdec_result = assembler->Float64Sub(fdec_value, one);
var_type_feedback.Bind(assembler->Word32Or(
var_type_feedback.value(),
assembler->Int32Constant(BinaryOperationFeedback::kNumber)));
result_var.Bind(assembler->ChangeFloat64ToTagged(fdec_result));
assembler->Goto(&end);
}
assembler->Bind(&end);
assembler->UpdateFeedback(var_type_feedback.value(), type_feedback_vector,
slot_id);
return result_var.value();
}

View File

@ -462,6 +462,19 @@ class CodeStub BASE_EMBEDDED {
} \
DEFINE_CODE_STUB(NAME, SUPER)
#define DEFINE_TURBOFAN_UNARY_OP_CODE_STUB_WITH_FEEDBACK(NAME, SUPER) \
public: \
static compiler::Node* Generate( \
CodeStubAssembler* assembler, compiler::Node* value, \
compiler::Node* context, compiler::Node* type_feedback_vector, \
compiler::Node* slot_id); \
void GenerateAssembly(CodeStubAssembler* assembler) const override { \
assembler->Return( \
Generate(assembler, assembler->Parameter(0), assembler->Parameter(1), \
assembler->Parameter(2), assembler->Parameter(3))); \
} \
DEFINE_CODE_STUB(NAME, SUPER)
#define DEFINE_HANDLER_CODE_STUB(NAME, SUPER) \
public: \
Handle<Code> GenerateCode() override; \
@ -895,7 +908,7 @@ class IncStub final : public TurboFanCodeStub {
explicit IncStub(Isolate* isolate) : TurboFanCodeStub(isolate) {}
DEFINE_CALL_INTERFACE_DESCRIPTOR(CountOp);
DEFINE_TURBOFAN_UNARY_OP_CODE_STUB(Inc, TurboFanCodeStub);
DEFINE_TURBOFAN_UNARY_OP_CODE_STUB_WITH_FEEDBACK(Inc, TurboFanCodeStub);
};
class DecStub final : public TurboFanCodeStub {
@ -903,7 +916,7 @@ class DecStub final : public TurboFanCodeStub {
explicit DecStub(Isolate* isolate) : TurboFanCodeStub(isolate) {}
DEFINE_CALL_INTERFACE_DESCRIPTOR(CountOp);
DEFINE_TURBOFAN_UNARY_OP_CODE_STUB(Dec, TurboFanCodeStub);
DEFINE_TURBOFAN_UNARY_OP_CODE_STUB_WITH_FEEDBACK(Dec, TurboFanCodeStub);
};
class InstanceOfStub final : public TurboFanCodeStub {

View File

@ -1215,6 +1215,18 @@ BinaryOperationHint BytecodeGraphBuilder::GetBinaryOperationHint() {
return hint;
}
BinaryOperationHint BytecodeGraphBuilder::GetBinaryOperationHintForIncDec() {
FeedbackVectorSlot slot =
feedback_vector()->ToSlot(bytecode_iterator().GetIndexOperand(0));
DCHECK_EQ(FeedbackVectorSlotKind::GENERAL, feedback_vector()->GetKind(slot));
Object* feedback = feedback_vector()->Get(slot);
BinaryOperationHint hint = BinaryOperationHint::kAny;
if (feedback->IsSmi()) {
hint = BinaryOperationHintFromFeedback((Smi::cast(feedback))->value());
}
return hint;
}
void BytecodeGraphBuilder::VisitAdd() {
BuildBinaryOp(javascript()->Add(GetBinaryOperationHint()));
}
@ -1302,15 +1314,17 @@ void BytecodeGraphBuilder::VisitInc() {
FrameStateBeforeAndAfter states(this);
// Note: Use subtract -1 here instead of add 1 to ensure we always convert to
// a number, not a string.
const Operator* js_op = javascript()->Subtract(BinaryOperationHint::kAny);
const Operator* js_op =
javascript()->Subtract(GetBinaryOperationHintForIncDec());
Node* node = NewNode(js_op, environment()->LookupAccumulator(),
jsgraph()->Constant(-1.0));
jsgraph()->Constant(-1));
environment()->BindAccumulator(node, &states);
}
void BytecodeGraphBuilder::VisitDec() {
FrameStateBeforeAndAfter states(this);
const Operator* js_op = javascript()->Subtract(BinaryOperationHint::kAny);
const Operator* js_op =
javascript()->Subtract(GetBinaryOperationHintForIncDec());
Node* node = NewNode(js_op, environment()->LookupAccumulator(),
jsgraph()->OneConstant());
environment()->BindAccumulator(node, &states);

View File

@ -139,6 +139,10 @@ class BytecodeGraphBuilder {
// type feedback.
BinaryOperationHint GetBinaryOperationHint();
// Helper function to create an binary operation hint from the recorded
// type feedback in Inc/Dec handlers.
BinaryOperationHint GetBinaryOperationHintForIncDec();
// Control flow plumbing.
void BuildJump();
void BuildConditionalJump(Node* condition);

View File

@ -1119,6 +1119,18 @@ void Interpreter::DoUnaryOp(InterpreterAssembler* assembler) {
__ Dispatch();
}
template <class Generator>
void Interpreter::DoUnaryOpWithFeedback(InterpreterAssembler* assembler) {
Node* value = __ GetAccumulator();
Node* context = __ GetContext();
Node* slot_index = __ BytecodeOperandIdx(0);
Node* type_feedback_vector = __ LoadTypeFeedbackVector();
Node* result = Generator::Generate(assembler, value, context,
type_feedback_vector, slot_index);
__ SetAccumulator(result);
__ Dispatch();
}
// ToName
//
// Cast the object referenced by the accumulator to a name.
@ -1150,14 +1162,14 @@ void Interpreter::DoToObject(InterpreterAssembler* assembler) {
//
// Increments value in the accumulator by one.
void Interpreter::DoInc(InterpreterAssembler* assembler) {
DoUnaryOp<IncStub>(assembler);
DoUnaryOpWithFeedback<IncStub>(assembler);
}
// Dec
//
// Decrements value in the accumulator by one.
void Interpreter::DoDec(InterpreterAssembler* assembler) {
DoUnaryOp<DecStub>(assembler);
DoUnaryOpWithFeedback<DecStub>(assembler);
}
// LogicalNot

View File

@ -97,6 +97,11 @@ class Interpreter {
template <class Generator>
void DoUnaryOp(InterpreterAssembler* assembler);
// Generates code to perform the unary operation via |Generator| while
// gatering type feedback.
template <class Generator>
void DoUnaryOpWithFeedback(InterpreterAssembler* assembler);
// Generates code to perform the comparison operation associated with
// |compare_op|.
void DoCompareOp(Token::Value compare_op, InterpreterAssembler* assembler);

View File

@ -509,6 +509,229 @@ TEST(InterpreterParameter8) {
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(36));
}
TEST(InterpreterBinaryOpTypeFeedback) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
i::Zone zone(isolate->allocator());
struct BinaryOpExpectation {
Token::Value op;
Handle<Object> arg1;
Handle<Object> arg2;
Handle<Object> result;
int32_t feedback;
};
BinaryOpExpectation const kTestCases[] = {
// ADD
{Token::Value::ADD, Handle<Smi>(Smi::FromInt(2), isolate),
Handle<Smi>(Smi::FromInt(3), isolate),
Handle<Smi>(Smi::FromInt(5), isolate),
BinaryOperationFeedback::kSignedSmall},
{Token::Value::ADD, Handle<Smi>(Smi::FromInt(Smi::kMaxValue), isolate),
Handle<Smi>(Smi::FromInt(1), isolate),
isolate->factory()->NewHeapNumber(Smi::kMaxValue + 1.0),
BinaryOperationFeedback::kNumber},
{Token::Value::ADD, isolate->factory()->NewHeapNumber(3.1415),
Handle<Smi>(Smi::FromInt(3), isolate),
isolate->factory()->NewHeapNumber(3.1415 + 3),
BinaryOperationFeedback::kNumber},
{Token::Value::ADD, isolate->factory()->NewHeapNumber(3.1415),
isolate->factory()->NewHeapNumber(1.4142),
isolate->factory()->NewHeapNumber(3.1415 + 1.4142),
BinaryOperationFeedback::kNumber},
{Token::Value::ADD, Handle<Smi>(Smi::FromInt(2), isolate),
isolate->factory()->NewStringFromAsciiChecked("2"),
isolate->factory()->NewStringFromAsciiChecked("22"),
BinaryOperationFeedback::kAny},
// SUB
{Token::Value::SUB, Handle<Smi>(Smi::FromInt(2), isolate),
Handle<Smi>(Smi::FromInt(3), isolate),
Handle<Smi>(Smi::FromInt(-1), isolate),
BinaryOperationFeedback::kSignedSmall},
{Token::Value::SUB, Handle<Smi>(Smi::FromInt(Smi::kMinValue), isolate),
Handle<Smi>(Smi::FromInt(1), isolate),
isolate->factory()->NewHeapNumber(Smi::kMinValue - 1.0),
BinaryOperationFeedback::kNumber},
{Token::Value::SUB, isolate->factory()->NewHeapNumber(3.1415),
Handle<Smi>(Smi::FromInt(3), isolate),
isolate->factory()->NewHeapNumber(3.1415 - 3),
BinaryOperationFeedback::kNumber},
{Token::Value::SUB, isolate->factory()->NewHeapNumber(3.1415),
isolate->factory()->NewHeapNumber(1.4142),
isolate->factory()->NewHeapNumber(3.1415 - 1.4142),
BinaryOperationFeedback::kNumber},
{Token::Value::SUB, Handle<Smi>(Smi::FromInt(2), isolate),
isolate->factory()->NewStringFromAsciiChecked("1"),
Handle<Smi>(Smi::FromInt(1), isolate), BinaryOperationFeedback::kAny},
// MUL
{Token::Value::MUL, Handle<Smi>(Smi::FromInt(2), isolate),
Handle<Smi>(Smi::FromInt(3), isolate),
Handle<Smi>(Smi::FromInt(6), isolate),
BinaryOperationFeedback::kSignedSmall},
{Token::Value::MUL, Handle<Smi>(Smi::FromInt(Smi::kMinValue), isolate),
Handle<Smi>(Smi::FromInt(2), isolate),
isolate->factory()->NewHeapNumber(Smi::kMinValue * 2.0),
BinaryOperationFeedback::kNumber},
{Token::Value::MUL, isolate->factory()->NewHeapNumber(3.1415),
Handle<Smi>(Smi::FromInt(3), isolate),
isolate->factory()->NewHeapNumber(3 * 3.1415),
BinaryOperationFeedback::kNumber},
{Token::Value::MUL, isolate->factory()->NewHeapNumber(3.1415),
isolate->factory()->NewHeapNumber(1.4142),
isolate->factory()->NewHeapNumber(3.1415 * 1.4142),
BinaryOperationFeedback::kNumber},
{Token::Value::MUL, Handle<Smi>(Smi::FromInt(2), isolate),
isolate->factory()->NewStringFromAsciiChecked("1"),
Handle<Smi>(Smi::FromInt(2), isolate), BinaryOperationFeedback::kAny},
// DIV
{Token::Value::DIV, Handle<Smi>(Smi::FromInt(6), isolate),
Handle<Smi>(Smi::FromInt(3), isolate),
Handle<Smi>(Smi::FromInt(2), isolate),
BinaryOperationFeedback::kSignedSmall},
{Token::Value::DIV, Handle<Smi>(Smi::FromInt(3), isolate),
Handle<Smi>(Smi::FromInt(2), isolate),
isolate->factory()->NewHeapNumber(3.0 / 2.0),
BinaryOperationFeedback::kNumber},
{Token::Value::DIV, isolate->factory()->NewHeapNumber(3.1415),
Handle<Smi>(Smi::FromInt(3), isolate),
isolate->factory()->NewHeapNumber(3.1415 / 3),
BinaryOperationFeedback::kNumber},
{Token::Value::DIV, isolate->factory()->NewHeapNumber(3.1415),
isolate->factory()->NewHeapNumber(
-std::numeric_limits<double>::infinity()),
isolate->factory()->NewHeapNumber(-0.0),
BinaryOperationFeedback::kNumber},
{Token::Value::DIV, Handle<Smi>(Smi::FromInt(2), isolate),
isolate->factory()->NewStringFromAsciiChecked("1"),
Handle<Smi>(Smi::FromInt(2), isolate), BinaryOperationFeedback::kAny},
// MOD
{Token::Value::MOD, Handle<Smi>(Smi::FromInt(5), isolate),
Handle<Smi>(Smi::FromInt(3), isolate),
Handle<Smi>(Smi::FromInt(2), isolate),
BinaryOperationFeedback::kSignedSmall},
{Token::Value::MOD, Handle<Smi>(Smi::FromInt(-4), isolate),
Handle<Smi>(Smi::FromInt(2), isolate),
isolate->factory()->NewHeapNumber(-0.0),
BinaryOperationFeedback::kNumber},
{Token::Value::MOD, isolate->factory()->NewHeapNumber(3.1415),
Handle<Smi>(Smi::FromInt(3), isolate),
isolate->factory()->NewHeapNumber(fmod(3.1415, 3.0)),
BinaryOperationFeedback::kNumber},
{Token::Value::MOD, isolate->factory()->NewHeapNumber(-3.1415),
isolate->factory()->NewHeapNumber(-1.4142),
isolate->factory()->NewHeapNumber(fmod(-3.1415, -1.4142)),
BinaryOperationFeedback::kNumber},
{Token::Value::MOD, Handle<Smi>(Smi::FromInt(3), isolate),
isolate->factory()->NewStringFromAsciiChecked("-2"),
Handle<Smi>(Smi::FromInt(1), isolate), BinaryOperationFeedback::kAny}};
for (const BinaryOpExpectation& test_case : kTestCases) {
BytecodeArrayBuilder builder(isolate, handles.main_zone(), 1, 0, 1);
i::FeedbackVectorSpec feedback_spec(&zone);
i::FeedbackVectorSlot slot0 = feedback_spec.AddGeneralSlot();
Handle<i::TypeFeedbackVector> vector =
i::NewTypeFeedbackVector(isolate, &feedback_spec);
Register reg(0);
builder.LoadLiteral(test_case.arg1)
.StoreAccumulatorInRegister(reg)
.LoadLiteral(test_case.arg2)
.BinaryOperation(test_case.op, reg, vector->GetIndex(slot0))
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(isolate, bytecode_array, vector);
auto callable = tester.GetCallable<>();
Handle<Object> return_val = callable().ToHandleChecked();
Object* feedback0 = vector->Get(slot0);
CHECK(feedback0->IsSmi());
CHECK_EQ(test_case.feedback, static_cast<Smi*>(feedback0)->value());
CHECK(Object::Equals(test_case.result, return_val).ToChecked());
}
}
TEST(InterpreterUnaryOpFeedback) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
i::Zone zone(isolate->allocator());
Handle<Smi> smi_one = Handle<Smi>(Smi::FromInt(1), isolate);
Handle<Smi> smi_max = Handle<Smi>(Smi::FromInt(Smi::kMaxValue), isolate);
Handle<Smi> smi_min = Handle<Smi>(Smi::FromInt(Smi::kMinValue), isolate);
Handle<HeapNumber> number = isolate->factory()->NewHeapNumber(2.1);
Handle<String> str = isolate->factory()->NewStringFromAsciiChecked("42");
struct TestCase {
Token::Value op;
Handle<Smi> smi_feedback_value;
Handle<Smi> smi_to_number_feedback_value;
Handle<HeapNumber> number_feedback_value;
Handle<Object> any_feedback_value;
};
TestCase const kTestCases[] = {
{Token::Value::ADD, smi_one, smi_max, number, str},
{Token::Value::SUB, smi_one, smi_min, number, str}};
for (TestCase const& test_case : kTestCases) {
BytecodeArrayBuilder builder(isolate, handles.main_zone(), 4, 0, 0);
i::FeedbackVectorSpec feedback_spec(&zone);
i::FeedbackVectorSlot slot0 = feedback_spec.AddGeneralSlot();
i::FeedbackVectorSlot slot1 = feedback_spec.AddGeneralSlot();
i::FeedbackVectorSlot slot2 = feedback_spec.AddGeneralSlot();
i::FeedbackVectorSlot slot3 = feedback_spec.AddGeneralSlot();
Handle<i::TypeFeedbackVector> vector =
i::NewTypeFeedbackVector(isolate, &feedback_spec);
builder.LoadAccumulatorWithRegister(builder.Parameter(0))
.CountOperation(test_case.op, vector->GetIndex(slot0))
.LoadAccumulatorWithRegister(builder.Parameter(1))
.CountOperation(test_case.op, vector->GetIndex(slot1))
.LoadAccumulatorWithRegister(builder.Parameter(2))
.CountOperation(test_case.op, vector->GetIndex(slot2))
.LoadAccumulatorWithRegister(builder.Parameter(3))
.CountOperation(test_case.op, vector->GetIndex(slot3))
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(isolate, bytecode_array, vector);
typedef Handle<Object> H;
auto callable = tester.GetCallable<H, H, H, H>();
Handle<Object> return_val =
callable(test_case.smi_feedback_value,
test_case.smi_to_number_feedback_value,
test_case.number_feedback_value, test_case.any_feedback_value)
.ToHandleChecked();
USE(return_val);
Object* feedback0 = vector->Get(slot0);
CHECK(feedback0->IsSmi());
CHECK_EQ(BinaryOperationFeedback::kSignedSmall,
static_cast<Smi*>(feedback0)->value());
Object* feedback1 = vector->Get(slot1);
CHECK(feedback1->IsSmi());
CHECK_EQ(BinaryOperationFeedback::kNumber,
static_cast<Smi*>(feedback1)->value());
Object* feedback2 = vector->Get(slot2);
CHECK(feedback2->IsSmi());
CHECK_EQ(BinaryOperationFeedback::kNumber,
static_cast<Smi*>(feedback2)->value());
Object* feedback3 = vector->Get(slot3);
CHECK(feedback3->IsSmi());
CHECK_EQ(BinaryOperationFeedback::kAny,
static_cast<Smi*>(feedback3)->value());
}
}
TEST(InterpreterBitwiseTypeFeedback) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();