Commit 74ccda64 authored by mythria's avatar mythria Committed by Commit bot

[Interpreter] Transform StrictEquality with null/undefined to special bytecodes.

Transform LdaNull/LdaUndefined followed by StrictEquality to TestNull/TestUndefined.
This would avoid a call to the compare IC. In the bytecode-graph builder these are
mapped to StrictEqual javascript operator. When reducing this operator, we already
optimize the cases for null/undefined.

BUG=v8:4280

Review-Url: https://codereview.chromium.org/2554723004
Cr-Commit-Position: refs/heads/master@{#41768}
parent 692ba84f
......@@ -1678,6 +1678,22 @@ void BytecodeGraphBuilder::VisitTestUndetectable() {
environment()->BindAccumulator(node);
}
void BytecodeGraphBuilder::VisitTestNull() {
Node* object =
environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
Node* result = NewNode(javascript()->StrictEqual(CompareOperationHint::kAny),
object, jsgraph()->NullConstant());
environment()->BindAccumulator(result);
}
void BytecodeGraphBuilder::VisitTestUndefined() {
Node* object =
environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
Node* result = NewNode(javascript()->StrictEqual(CompareOperationHint::kAny),
object, jsgraph()->UndefinedConstant());
environment()->BindAccumulator(result);
}
void BytecodeGraphBuilder::BuildCastOperator(const Operator* js_op) {
PrepareEagerCheckpoint();
Node* value = NewNode(js_op, environment()->LookupAccumulator());
......
......@@ -141,13 +141,14 @@ BytecodeNode TransformLdaZeroBinaryOpToBinaryOpWithZero(
return node;
}
BytecodeNode TransformEqualityWithNullOrUndefinedToTestUndetectable(
BytecodeNode* const last, BytecodeNode* const current) {
BytecodeNode TransformEqualityWithNullOrUndefined(Bytecode new_bytecode,
BytecodeNode* const last,
BytecodeNode* const current) {
DCHECK((last->bytecode() == Bytecode::kLdaNull) ||
(last->bytecode() == Bytecode::kLdaUndefined));
DCHECK_EQ(current->bytecode(), Bytecode::kTestEqual);
BytecodeNode node(BytecodeNode::TestUndetectable(current->source_info(),
current->operand(0)));
DCHECK((current->bytecode() == Bytecode::kTestEqual) ||
(current->bytecode() == Bytecode::kTestEqualStrict));
BytecodeNode node(new_bytecode, current->operand(0), current->source_info());
if (last->source_info().is_valid()) {
node.set_source_info(last->source_info());
}
......@@ -267,14 +268,13 @@ void BytecodePeepholeOptimizer::
}
}
void BytecodePeepholeOptimizer::
TransformEqualityWithNullOrUndefinedToTestUndetectableAction(
BytecodeNode* const node, const PeepholeActionAndData* action_data) {
void BytecodePeepholeOptimizer::TransformEqualityWithNullOrUndefinedAction(
BytecodeNode* const node, const PeepholeActionAndData* action_data) {
DCHECK(LastIsValid());
DCHECK(!Bytecodes::IsJump(node->bytecode()));
// Fused last and current into current.
BytecodeNode new_node(
TransformEqualityWithNullOrUndefinedToTestUndetectable(last(), node));
BytecodeNode new_node(TransformEqualityWithNullOrUndefined(
action_data->bytecode, last(), node));
SetLast(&new_node);
}
......
......@@ -21,7 +21,7 @@ namespace interpreter {
V(ChangeBytecodeAction) \
V(TransformLdaSmiBinaryOpToBinaryOpWithSmiAction) \
V(TransformLdaZeroBinaryOpToBinaryOpWithZeroAction) \
V(TransformEqualityWithNullOrUndefinedToTestUndetectableAction)
V(TransformEqualityWithNullOrUndefinedAction)
#define PEEPHOLE_JUMP_ACTION_LIST(V) \
V(DefaultJumpAction) \
......
......@@ -187,6 +187,8 @@ namespace interpreter {
\
/* TestEqual with Null or Undefined */ \
V(TestUndetectable, AccumulatorUse::kWrite, OperandType::kReg) \
V(TestNull, AccumulatorUse::kWrite, OperandType::kReg) \
V(TestUndefined, AccumulatorUse::kWrite, OperandType::kReg) \
\
/* Cast operators */ \
V(ToName, AccumulatorUse::kRead, OperandType::kRegOut) \
......@@ -484,6 +486,8 @@ class V8_EXPORT_PRIVATE Bytecodes final {
case Bytecode::kTestIn:
case Bytecode::kTestUndetectable:
case Bytecode::kForInContinue:
case Bytecode::kTestUndefined:
case Bytecode::kTestNull:
return true;
default:
return false;
......
......@@ -1918,7 +1918,7 @@ void Interpreter::DoTestInstanceOf(InterpreterAssembler* assembler) {
// TestUndetectable <src>
//
// Test if the value in the <src> register equals to Null/Undefined. This is
// Test if the value in the <src> register equals to null/undefined. This is
// done by checking undetectable bit on the map of the object.
void Interpreter::DoTestUndetectable(InterpreterAssembler* assembler) {
Node* reg_index = __ BytecodeOperandReg(0);
......@@ -1948,6 +1948,53 @@ void Interpreter::DoTestUndetectable(InterpreterAssembler* assembler) {
__ Dispatch();
}
// TestNull <src>
//
// Test if the value in the <src> register is strictly equal to null.
void Interpreter::DoTestNull(InterpreterAssembler* assembler) {
Node* reg_index = __ BytecodeOperandReg(0);
Node* object = __ LoadRegister(reg_index);
Node* null_value = __ HeapConstant(isolate_->factory()->null_value());
Label equal(assembler), end(assembler);
__ GotoIf(__ WordEqual(object, null_value), &equal);
__ SetAccumulator(__ BooleanConstant(false));
__ Goto(&end);
__ Bind(&equal);
{
__ SetAccumulator(__ BooleanConstant(true));
__ Goto(&end);
}
__ Bind(&end);
__ Dispatch();
}
// TestUndefined <src>
//
// Test if the value in the <src> register is strictly equal to undefined.
void Interpreter::DoTestUndefined(InterpreterAssembler* assembler) {
Node* reg_index = __ BytecodeOperandReg(0);
Node* object = __ LoadRegister(reg_index);
Node* undefined_value =
__ HeapConstant(isolate_->factory()->undefined_value());
Label equal(assembler), end(assembler);
__ GotoIf(__ WordEqual(object, undefined_value), &equal);
__ SetAccumulator(__ BooleanConstant(false));
__ Goto(&end);
__ Bind(&equal);
{
__ SetAccumulator(__ BooleanConstant(true));
__ Goto(&end);
}
__ Bind(&end);
__ Dispatch();
}
// Jump <imm>
//
// Jump by number of bytes represented by the immediate operand |imm|.
......
......@@ -195,13 +195,22 @@ PeepholeActionAndData PeepholeActionTableWriter::LookupActionAndData(
// Fuse LdaNull/LdaUndefined followed by a equality comparison with test
// undetectable. Testing undetectable is a simple check on the map which is
// more efficient than the full comparison operation.
// Note: StrictEquals cannot use this, they need to compare it with the
// Null/undefined map.
if (last == Bytecode::kLdaNull || last == Bytecode::kLdaUndefined) {
if (current == Bytecode::kTestEqual) {
return {PeepholeAction::
kTransformEqualityWithNullOrUndefinedToTestUndetectableAction,
Bytecode::kIllegal};
return {PeepholeAction::kTransformEqualityWithNullOrUndefinedAction,
Bytecode::kTestUndetectable};
}
}
// Fuse LdaNull/LdaUndefined followed by a strict equals with
// TestNull/TestUndefined.
if (current == Bytecode::kTestEqualStrict) {
if (last == Bytecode::kLdaNull) {
return {PeepholeAction::kTransformEqualityWithNullOrUndefinedAction,
Bytecode::kTestNull};
} else if (last == Bytecode::kLdaUndefined) {
return {PeepholeAction::kTransformEqualityWithNullOrUndefinedAction,
Bytecode::kTestUndefined};
}
}
......
......@@ -121,3 +121,119 @@ constant pool: [
handlers: [
]
---
snippet: "
var obj_a = {val:1};
var b = 10;
if (obj_a === null) { b = 20;}
return b;
"
frame size: 3
parameter count: 1
bytecode array length: 24
bytecodes: [
/* 30 E> */ B(StackCheck),
/* 46 S> */ B(CreateObjectLiteral), U8(0), U8(0), U8(1), R(2),
B(Mov), R(2), R(0),
/* 63 S> */ B(LdaSmi), U8(10),
B(Star), R(1),
/* 67 S> */ B(TestNull), R(0),
B(JumpIfFalse), U8(6),
/* 89 S> */ B(LdaSmi), U8(20),
B(Star), R(1),
/* 98 S> */ B(Ldar), R(1),
/* 108 S> */ B(Return),
]
constant pool: [
FIXED_ARRAY_TYPE,
]
handlers: [
]
---
snippet: "
var obj_a = {val:1};
var b = 10;
if (obj_a === undefined) { b = 20;}
return b;
"
frame size: 3
parameter count: 1
bytecode array length: 24
bytecodes: [
/* 30 E> */ B(StackCheck),
/* 46 S> */ B(CreateObjectLiteral), U8(0), U8(0), U8(1), R(2),
B(Mov), R(2), R(0),
/* 63 S> */ B(LdaSmi), U8(10),
B(Star), R(1),
/* 67 S> */ B(TestUndefined), R(0),
B(JumpIfFalse), U8(6),
/* 94 S> */ B(LdaSmi), U8(20),
B(Star), R(1),
/* 103 S> */ B(Ldar), R(1),
/* 113 S> */ B(Return),
]
constant pool: [
FIXED_ARRAY_TYPE,
]
handlers: [
]
---
snippet: "
var obj_a = {val:1};
var b = 10;
if (obj_a !== null) { b = 20;}
return b;
"
frame size: 3
parameter count: 1
bytecode array length: 24
bytecodes: [
/* 30 E> */ B(StackCheck),
/* 46 S> */ B(CreateObjectLiteral), U8(0), U8(0), U8(1), R(2),
B(Mov), R(2), R(0),
/* 63 S> */ B(LdaSmi), U8(10),
B(Star), R(1),
/* 67 S> */ B(TestNull), R(0),
B(JumpIfTrue), U8(6),
/* 89 S> */ B(LdaSmi), U8(20),
B(Star), R(1),
/* 98 S> */ B(Ldar), R(1),
/* 108 S> */ B(Return),
]
constant pool: [
FIXED_ARRAY_TYPE,
]
handlers: [
]
---
snippet: "
var obj_a = {val:1};
var b = 10;
if (obj_a !== undefined) { b = 20;}
return b;
"
frame size: 3
parameter count: 1
bytecode array length: 24
bytecodes: [
/* 30 E> */ B(StackCheck),
/* 46 S> */ B(CreateObjectLiteral), U8(0), U8(0), U8(1), R(2),
B(Mov), R(2), R(0),
/* 63 S> */ B(LdaSmi), U8(10),
B(Star), R(1),
/* 67 S> */ B(TestUndefined), R(0),
B(JumpIfTrue), U8(6),
/* 94 S> */ B(LdaSmi), U8(20),
B(Star), R(1),
/* 103 S> */ B(Ldar), R(1),
/* 113 S> */ B(Return),
]
constant pool: [
FIXED_ARRAY_TYPE,
]
handlers: [
]
......@@ -1753,17 +1753,40 @@ TEST(GenerateTestUndetectable) {
"var b = 10;\n"
"if (obj_a == null) { b = 20;}\n"
"return b;\n",
"var obj_a = {val:1};\n"
"var b = 10;\n"
"if (obj_a == undefined) { b = 20;}\n"
"return b;\n",
"var obj_a = {val:1};\n"
"var b = 10;\n"
"if (obj_a != null) { b = 20;}\n"
"return b;\n",
"var obj_a = {val:1};\n"
"var b = 10;\n"
"if (obj_a != undefined) { b = 20;}\n"
"return b;\n",
"var obj_a = {val:1};\n"
"var b = 10;\n"
"if (obj_a === null) { b = 20;}\n"
"return b;\n",
"var obj_a = {val:1};\n"
"var b = 10;\n"
"if (obj_a === undefined) { b = 20;}\n"
"return b;\n",
"var obj_a = {val:1};\n"
"var b = 10;\n"
"if (obj_a !== null) { b = 20;}\n"
"return b;\n",
"var obj_a = {val:1};\n"
"var b = 10;\n"
"if (obj_a !== undefined) { b = 20;}\n"
"return b;\n"};
CHECK(CompareTexts(BuildActual(printer, snippets),
......
......@@ -202,7 +202,11 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
builder.LoadUndefined()
.CompareOperation(Token::Value::EQ, reg, 1)
.LoadNull()
.CompareOperation(Token::Value::EQ, reg, 1);
.CompareOperation(Token::Value::EQ, reg, 1)
.LoadUndefined()
.CompareOperation(Token::Value::EQ_STRICT, reg, 1)
.LoadNull()
.CompareOperation(Token::Value::EQ_STRICT, reg, 1);
// Emit conversion operator invocations.
builder.ConvertAccumulatorToNumber(reg)
......@@ -416,6 +420,8 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
scorecard[Bytecodes::ToByte(Bytecode::kShiftLeftSmi)] = 1;
scorecard[Bytecodes::ToByte(Bytecode::kShiftRightSmi)] = 1;
scorecard[Bytecodes::ToByte(Bytecode::kTestUndetectable)] = 1;
scorecard[Bytecodes::ToByte(Bytecode::kTestUndefined)] = 1;
scorecard[Bytecodes::ToByte(Bytecode::kTestNull)] = 1;
}
// Check return occurs at the end and only once in the BytecodeArray.
......
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