[Interpreter] Optimize equality check with null/undefined with a check on the map.
Equality with null/undefined is equivalent to a check on the undetectable bit on the map of the object. This would be more efficient than performing the entire comparison operation. This cl introduces: 1. A new bytecode called TestUndetectable that checks if the object is null/undefined. 2. Updates peeophole optimizer to emit TestUndetectable when a LdaNull/Undefined precedes equality check. 4. TestUndetectable is transformed to ObjectIsUndetectable operator when building turbofan graph. BUG=v8:4280 Review-Url: https://codereview.chromium.org/2547043002 Cr-Commit-Position: refs/heads/master@{#41514}
This commit is contained in:
parent
d50108adc8
commit
9119d16904
@ -10,6 +10,7 @@
|
||||
#include "src/compiler/compiler-source-position-table.h"
|
||||
#include "src/compiler/linkage.h"
|
||||
#include "src/compiler/operator-properties.h"
|
||||
#include "src/compiler/simplified-operator.h"
|
||||
#include "src/interpreter/bytecodes.h"
|
||||
#include "src/objects-inl.h"
|
||||
|
||||
@ -1643,6 +1644,13 @@ void BytecodeGraphBuilder::VisitTestInstanceOf() {
|
||||
BuildCompareOp(javascript()->InstanceOf());
|
||||
}
|
||||
|
||||
void BytecodeGraphBuilder::VisitTestUndetectable() {
|
||||
Node* object =
|
||||
environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
|
||||
Node* node = NewNode(jsgraph()->simplified()->ObjectIsUndetectable(), object);
|
||||
environment()->BindAccumulator(node);
|
||||
}
|
||||
|
||||
void BytecodeGraphBuilder::BuildCastOperator(const Operator* js_op) {
|
||||
PrepareEagerCheckpoint();
|
||||
Node* value = NewNode(js_op, environment()->LookupAccumulator());
|
||||
|
@ -588,6 +588,7 @@ BytecodeGenerator::BytecodeGenerator(CompilationInfo* info)
|
||||
const AstRawString* prototype_string = ast_value_factory->prototype_string();
|
||||
ast_value_factory->Internalize(info->isolate());
|
||||
prototype_string_ = prototype_string->string();
|
||||
undefined_string_ = ast_value_factory->undefined_string();
|
||||
}
|
||||
|
||||
Handle<BytecodeArray> BytecodeGenerator::FinalizeBytecode(Isolate* isolate) {
|
||||
@ -1831,8 +1832,15 @@ void BytecodeGenerator::BuildVariableLoad(Variable* variable,
|
||||
break;
|
||||
}
|
||||
case VariableLocation::UNALLOCATED: {
|
||||
builder()->LoadGlobal(variable->name(), feedback_index(slot),
|
||||
typeof_mode);
|
||||
// The global identifier "undefined" is immutable. Everything
|
||||
// else could be reassigned. For performance, we do a pointer comparison
|
||||
// rather than checking if the raw_name is really "undefined".
|
||||
if (variable->raw_name() == undefined_string()) {
|
||||
builder()->LoadUndefined();
|
||||
} else {
|
||||
builder()->LoadGlobal(variable->name(), feedback_index(slot),
|
||||
typeof_mode);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case VariableLocation::CONTEXT: {
|
||||
|
@ -195,6 +195,7 @@ class BytecodeGenerator final : public AstVisitor<BytecodeGenerator> {
|
||||
Handle<Name> home_object_symbol() const { return home_object_symbol_; }
|
||||
Handle<Name> prototype_string() const { return prototype_string_; }
|
||||
Handle<FixedArray> empty_fixed_array() const { return empty_fixed_array_; }
|
||||
const AstRawString* undefined_string() const { return undefined_string_; }
|
||||
|
||||
Zone* zone_;
|
||||
BytecodeArrayBuilder* builder_;
|
||||
@ -218,6 +219,7 @@ class BytecodeGenerator final : public AstVisitor<BytecodeGenerator> {
|
||||
Handle<Name> home_object_symbol_;
|
||||
Handle<Name> prototype_string_;
|
||||
Handle<FixedArray> empty_fixed_array_;
|
||||
const AstRawString* undefined_string_;
|
||||
};
|
||||
|
||||
} // namespace interpreter
|
||||
|
@ -138,6 +138,17 @@ void TransformLdaZeroBinaryOpToBinaryOpWithZero(Bytecode new_bytecode,
|
||||
}
|
||||
}
|
||||
|
||||
void TransformEqualityWithNullOrUndefinedToTestUndetectable(
|
||||
BytecodeNode* const last, BytecodeNode* const current) {
|
||||
DCHECK((last->bytecode() == Bytecode::kLdaNull) ||
|
||||
(last->bytecode() == Bytecode::kLdaUndefined));
|
||||
DCHECK_EQ(current->bytecode(), Bytecode::kTestEqual);
|
||||
current->set_bytecode(Bytecode::kTestUndetectable, current->operand(0));
|
||||
if (last->source_info().is_valid()) {
|
||||
current->set_source_info(last->source_info());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void BytecodePeepholeOptimizer::DefaultAction(
|
||||
@ -251,6 +262,16 @@ void BytecodePeepholeOptimizer::
|
||||
}
|
||||
}
|
||||
|
||||
void BytecodePeepholeOptimizer::
|
||||
TransformEqualityWithNullOrUndefinedToTestUndetectableAction(
|
||||
BytecodeNode* const node, const PeepholeActionAndData* action_data) {
|
||||
DCHECK(LastIsValid());
|
||||
DCHECK(!Bytecodes::IsJump(node->bytecode()));
|
||||
// Fused last and current into current.
|
||||
TransformEqualityWithNullOrUndefinedToTestUndetectable(last(), node);
|
||||
SetLast(node);
|
||||
}
|
||||
|
||||
void BytecodePeepholeOptimizer::DefaultJumpAction(
|
||||
BytecodeNode* const node, const PeepholeActionAndData* action_data) {
|
||||
DCHECK(LastIsValid());
|
||||
|
@ -11,16 +11,17 @@ namespace v8 {
|
||||
namespace internal {
|
||||
namespace interpreter {
|
||||
|
||||
#define PEEPHOLE_NON_JUMP_ACTION_LIST(V) \
|
||||
V(DefaultAction) \
|
||||
V(UpdateLastAction) \
|
||||
V(UpdateLastIfSourceInfoPresentAction) \
|
||||
V(ElideCurrentAction) \
|
||||
V(ElideCurrentIfOperand0MatchesAction) \
|
||||
V(ElideLastAction) \
|
||||
V(ChangeBytecodeAction) \
|
||||
V(TransformLdaSmiBinaryOpToBinaryOpWithSmiAction) \
|
||||
V(TransformLdaZeroBinaryOpToBinaryOpWithZeroAction)
|
||||
#define PEEPHOLE_NON_JUMP_ACTION_LIST(V) \
|
||||
V(DefaultAction) \
|
||||
V(UpdateLastAction) \
|
||||
V(UpdateLastIfSourceInfoPresentAction) \
|
||||
V(ElideCurrentAction) \
|
||||
V(ElideCurrentIfOperand0MatchesAction) \
|
||||
V(ElideLastAction) \
|
||||
V(ChangeBytecodeAction) \
|
||||
V(TransformLdaSmiBinaryOpToBinaryOpWithSmiAction) \
|
||||
V(TransformLdaZeroBinaryOpToBinaryOpWithZeroAction) \
|
||||
V(TransformEqualityWithNullOrUndefinedToTestUndetectableAction)
|
||||
|
||||
#define PEEPHOLE_JUMP_ACTION_LIST(V) \
|
||||
V(DefaultJumpAction) \
|
||||
|
@ -183,6 +183,9 @@ namespace interpreter {
|
||||
V(TestInstanceOf, AccumulatorUse::kReadWrite, OperandType::kReg) \
|
||||
V(TestIn, AccumulatorUse::kReadWrite, OperandType::kReg) \
|
||||
\
|
||||
/* TestEqual with Null or Undefined */ \
|
||||
V(TestUndetectable, AccumulatorUse::kWrite, OperandType::kReg) \
|
||||
\
|
||||
/* Cast operators */ \
|
||||
V(ToName, AccumulatorUse::kRead, OperandType::kRegOut) \
|
||||
V(ToNumber, AccumulatorUse::kRead, OperandType::kRegOut) \
|
||||
@ -483,6 +486,7 @@ class V8_EXPORT_PRIVATE Bytecodes final {
|
||||
case Bytecode::kTestGreaterThanOrEqual:
|
||||
case Bytecode::kTestInstanceOf:
|
||||
case Bytecode::kTestIn:
|
||||
case Bytecode::kTestUndetectable:
|
||||
case Bytecode::kForInContinue:
|
||||
return true;
|
||||
default:
|
||||
|
@ -1918,6 +1918,38 @@ void Interpreter::DoTestInstanceOf(InterpreterAssembler* assembler) {
|
||||
DoCompareOp(Token::INSTANCEOF, assembler);
|
||||
}
|
||||
|
||||
// TestUndetectable <src>
|
||||
//
|
||||
// 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);
|
||||
Node* object = __ LoadRegister(reg_index);
|
||||
|
||||
Label not_equal(assembler), end(assembler);
|
||||
// If the object is an Smi then return false.
|
||||
__ GotoIf(__ TaggedIsSmi(object), ¬_equal);
|
||||
|
||||
// If it is a HeapObject, load the map and check for undetectable bit.
|
||||
Node* map = __ LoadMap(object);
|
||||
Node* map_bitfield = __ LoadMapBitField(map);
|
||||
Node* map_undetectable =
|
||||
__ Word32And(map_bitfield, __ Int32Constant(1 << Map::kIsUndetectable));
|
||||
__ GotoIf(__ Word32Equal(map_undetectable, __ Int32Constant(0)), ¬_equal);
|
||||
|
||||
__ SetAccumulator(__ BooleanConstant(true));
|
||||
__ Goto(&end);
|
||||
|
||||
__ Bind(¬_equal);
|
||||
{
|
||||
__ SetAccumulator(__ BooleanConstant(false));
|
||||
__ Goto(&end);
|
||||
}
|
||||
|
||||
__ Bind(&end);
|
||||
__ Dispatch();
|
||||
}
|
||||
|
||||
// Jump <imm>
|
||||
//
|
||||
// Jump by number of bytes represented by the immediate operand |imm|.
|
||||
|
@ -192,6 +192,19 @@ 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};
|
||||
}
|
||||
}
|
||||
|
||||
// If there is no last bytecode to optimize against, store the incoming
|
||||
// bytecode or for jumps emit incoming bytecode immediately.
|
||||
if (last == Bytecode::kIllegal) {
|
||||
|
@ -11,7 +11,7 @@ snippet: "
|
||||
"
|
||||
frame size: 15
|
||||
parameter count: 1
|
||||
bytecode array length: 268
|
||||
bytecode array length: 266
|
||||
bytecodes: [
|
||||
/* 30 E> */ B(StackCheck),
|
||||
B(LdaZero),
|
||||
@ -71,11 +71,10 @@ bytecodes: [
|
||||
B(Star), R(11),
|
||||
B(LdaZero),
|
||||
B(TestEqualStrict), R(4), U8(15),
|
||||
B(JumpIfTrue), U8(113),
|
||||
B(JumpIfTrue), U8(111),
|
||||
B(LdaNamedProperty), R(2), U8(7), U8(16),
|
||||
B(Star), R(6),
|
||||
B(LdaNull),
|
||||
B(TestEqual), R(6), U8(18),
|
||||
B(TestUndetectable), R(6),
|
||||
B(JumpIfFalse), U8(4),
|
||||
B(Jump), U8(99),
|
||||
B(LdaSmi), U8(1),
|
||||
@ -144,7 +143,7 @@ constant pool: [
|
||||
handlers: [
|
||||
[7, 120, 126],
|
||||
[10, 84, 86],
|
||||
[195, 205, 207],
|
||||
[193, 203, 205],
|
||||
]
|
||||
|
||||
---
|
||||
@ -154,7 +153,7 @@ snippet: "
|
||||
"
|
||||
frame size: 16
|
||||
parameter count: 1
|
||||
bytecode array length: 279
|
||||
bytecode array length: 277
|
||||
bytecodes: [
|
||||
/* 30 E> */ B(StackCheck),
|
||||
/* 42 S> */ B(LdaConstant), U8(0),
|
||||
@ -215,11 +214,10 @@ bytecodes: [
|
||||
B(Star), R(12),
|
||||
B(LdaZero),
|
||||
B(TestEqualStrict), R(5), U8(15),
|
||||
B(JumpIfTrue), U8(113),
|
||||
B(JumpIfTrue), U8(111),
|
||||
B(LdaNamedProperty), R(3), U8(7), U8(16),
|
||||
B(Star), R(7),
|
||||
B(LdaNull),
|
||||
B(TestEqual), R(7), U8(18),
|
||||
B(TestUndetectable), R(7),
|
||||
B(JumpIfFalse), U8(4),
|
||||
B(Jump), U8(99),
|
||||
B(LdaSmi), U8(1),
|
||||
@ -293,7 +291,7 @@ constant pool: [
|
||||
handlers: [
|
||||
[11, 120, 126],
|
||||
[14, 84, 86],
|
||||
[196, 206, 208],
|
||||
[194, 204, 206],
|
||||
]
|
||||
|
||||
---
|
||||
@ -305,7 +303,7 @@ snippet: "
|
||||
"
|
||||
frame size: 15
|
||||
parameter count: 1
|
||||
bytecode array length: 286
|
||||
bytecode array length: 284
|
||||
bytecodes: [
|
||||
/* 30 E> */ B(StackCheck),
|
||||
B(LdaZero),
|
||||
@ -373,11 +371,10 @@ bytecodes: [
|
||||
B(Star), R(11),
|
||||
B(LdaZero),
|
||||
B(TestEqualStrict), R(4), U8(17),
|
||||
B(JumpIfTrue), U8(113),
|
||||
B(JumpIfTrue), U8(111),
|
||||
B(LdaNamedProperty), R(2), U8(7), U8(18),
|
||||
B(Star), R(6),
|
||||
B(LdaNull),
|
||||
B(TestEqual), R(6), U8(20),
|
||||
B(TestUndetectable), R(6),
|
||||
B(JumpIfFalse), U8(4),
|
||||
B(Jump), U8(99),
|
||||
B(LdaSmi), U8(1),
|
||||
@ -446,7 +443,7 @@ constant pool: [
|
||||
handlers: [
|
||||
[7, 138, 144],
|
||||
[10, 102, 104],
|
||||
[213, 223, 225],
|
||||
[211, 221, 223],
|
||||
]
|
||||
|
||||
---
|
||||
@ -456,7 +453,7 @@ snippet: "
|
||||
"
|
||||
frame size: 14
|
||||
parameter count: 1
|
||||
bytecode array length: 293
|
||||
bytecode array length: 291
|
||||
bytecodes: [
|
||||
/* 30 E> */ B(StackCheck),
|
||||
/* 42 S> */ B(CreateObjectLiteral), U8(0), U8(0), U8(1), R(8),
|
||||
@ -521,11 +518,10 @@ bytecodes: [
|
||||
B(Star), R(10),
|
||||
B(LdaZero),
|
||||
B(TestEqualStrict), R(3), U8(19),
|
||||
B(JumpIfTrue), U8(113),
|
||||
B(JumpIfTrue), U8(111),
|
||||
B(LdaNamedProperty), R(1), U8(9), U8(20),
|
||||
B(Star), R(5),
|
||||
B(LdaNull),
|
||||
B(TestEqual), R(5), U8(22),
|
||||
B(TestUndetectable), R(5),
|
||||
B(JumpIfFalse), U8(4),
|
||||
B(Jump), U8(99),
|
||||
B(LdaSmi), U8(1),
|
||||
@ -601,6 +597,6 @@ constant pool: [
|
||||
handlers: [
|
||||
[15, 134, 140],
|
||||
[18, 98, 100],
|
||||
[210, 220, 222],
|
||||
[208, 218, 220],
|
||||
]
|
||||
|
||||
|
@ -0,0 +1,123 @@
|
||||
#
|
||||
# Autogenerated by generate-bytecode-expectations.
|
||||
#
|
||||
|
||||
---
|
||||
wrap: yes
|
||||
|
||||
---
|
||||
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(TestUndetectable), R(0),
|
||||
B(JumpIfFalse), U8(6),
|
||||
/* 88 S> */ B(LdaSmi), U8(20),
|
||||
B(Star), R(1),
|
||||
/* 97 S> */ B(Ldar), R(1),
|
||||
/* 107 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(TestUndetectable), R(0),
|
||||
B(JumpIfFalse), U8(6),
|
||||
/* 93 S> */ B(LdaSmi), U8(20),
|
||||
B(Star), R(1),
|
||||
/* 102 S> */ B(Ldar), R(1),
|
||||
/* 112 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(TestUndetectable), R(0),
|
||||
B(JumpIfTrue), U8(6),
|
||||
/* 88 S> */ B(LdaSmi), U8(20),
|
||||
B(Star), R(1),
|
||||
/* 97 S> */ B(Ldar), R(1),
|
||||
/* 107 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(TestUndetectable), R(0),
|
||||
B(JumpIfTrue), U8(6),
|
||||
/* 93 S> */ B(LdaSmi), U8(20),
|
||||
B(Star), R(1),
|
||||
/* 102 S> */ B(Ldar), R(1),
|
||||
/* 112 S> */ B(Return),
|
||||
]
|
||||
constant pool: [
|
||||
FIXED_ARRAY_TYPE,
|
||||
]
|
||||
handlers: [
|
||||
]
|
||||
|
@ -277,7 +277,7 @@ snippet: "
|
||||
"
|
||||
frame size: 17
|
||||
parameter count: 1
|
||||
bytecode array length: 771
|
||||
bytecode array length: 769
|
||||
bytecodes: [
|
||||
B(Ldar), R(new_target),
|
||||
B(JumpIfUndefined), U8(28),
|
||||
@ -475,8 +475,7 @@ bytecodes: [
|
||||
B(StaContextSlot), R(1), U8(11), U8(0),
|
||||
B(LdaContextSlot), R(1), U8(11), U8(0),
|
||||
B(Star), R(10),
|
||||
B(LdaNull),
|
||||
B(TestEqual), R(10), U8(18),
|
||||
B(TestUndetectable), R(10),
|
||||
B(JumpIfFalse), U8(4),
|
||||
B(JumpConstant), U8(16),
|
||||
B(LdaContextSlot), R(1), U8(9), U8(0),
|
||||
@ -620,13 +619,13 @@ constant pool: [
|
||||
ONE_BYTE_INTERNALIZED_STRING_TYPE [""],
|
||||
FIXED_ARRAY_TYPE,
|
||||
Smi [133],
|
||||
Smi [161],
|
||||
Smi [581],
|
||||
Smi [159],
|
||||
Smi [579],
|
||||
]
|
||||
handlers: [
|
||||
[46, 690, 696],
|
||||
[46, 688, 694],
|
||||
[143, 438, 444],
|
||||
[146, 394, 396],
|
||||
[542, 558, 560],
|
||||
[540, 556, 558],
|
||||
]
|
||||
|
||||
|
@ -1742,6 +1742,31 @@ TEST(RemoveRedundantLdar) {
|
||||
LoadGolden("RemoveRedundantLdar.golden")));
|
||||
}
|
||||
|
||||
TEST(GenerateTestUndetectable) {
|
||||
InitializedIgnitionHandleScope scope;
|
||||
BytecodeExpectationsPrinter printer(CcTest::isolate());
|
||||
const char* snippets[] = {
|
||||
"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),
|
||||
LoadGolden("GenerateTestUndetectable.golden")));
|
||||
}
|
||||
|
||||
TEST(AssignmentsInBinaryExpression) {
|
||||
InitializedIgnitionHandleScope scope;
|
||||
BytecodeExpectationsPrinter printer(CcTest::isolate());
|
||||
|
@ -1075,7 +1075,7 @@ TEST(CodeSerializerLargeCodeObject) {
|
||||
Vector<const uint8_t> source =
|
||||
ConstructSource(STATIC_CHAR_VECTOR("var j=1; if (j == 0) {"),
|
||||
STATIC_CHAR_VECTOR("for (let i of Object.prototype);"),
|
||||
STATIC_CHAR_VECTOR("} j=7; j"), 1000);
|
||||
STATIC_CHAR_VECTOR("} j=7; j"), 1050);
|
||||
Handle<String> source_str =
|
||||
isolate->factory()->NewStringFromOneByte(source).ToHandleChecked();
|
||||
|
||||
|
@ -195,6 +195,12 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
|
||||
.CompareOperation(Token::Value::INSTANCEOF, reg, 8)
|
||||
.CompareOperation(Token::Value::IN, reg, 9);
|
||||
|
||||
// Emit peephole optimizations of equality with Null or Undefined.
|
||||
builder.LoadUndefined()
|
||||
.CompareOperation(Token::Value::EQ, reg, 1)
|
||||
.LoadNull()
|
||||
.CompareOperation(Token::Value::EQ, reg, 1);
|
||||
|
||||
// Emit conversion operator invocations.
|
||||
builder.ConvertAccumulatorToNumber(reg)
|
||||
.ConvertAccumulatorToObject(reg)
|
||||
@ -398,6 +404,7 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
|
||||
scorecard[Bytecodes::ToByte(Bytecode::kBitwiseOrSmi)] = 1;
|
||||
scorecard[Bytecodes::ToByte(Bytecode::kShiftLeftSmi)] = 1;
|
||||
scorecard[Bytecodes::ToByte(Bytecode::kShiftRightSmi)] = 1;
|
||||
scorecard[Bytecodes::ToByte(Bytecode::kTestUndetectable)] = 1;
|
||||
}
|
||||
|
||||
// Check return occurs at the end and only once in the BytecodeArray.
|
||||
|
@ -403,6 +403,25 @@ TEST_F(BytecodePeepholeOptimizerTest, MergeLdaZeroWithBinaryOp) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(BytecodePeepholeOptimizerTest, MergeLdaNullOrUndefinedWithCompareOp) {
|
||||
Bytecode first_bytecodes[] = {Bytecode::kLdaUndefined, Bytecode::kLdaNull};
|
||||
|
||||
for (auto first_bytecode : first_bytecodes) {
|
||||
uint32_t reg_operand = Register(0).ToOperand();
|
||||
uint32_t idx_operand = 1;
|
||||
BytecodeNode first(first_bytecode);
|
||||
BytecodeNode second(Bytecode::kTestEqual, reg_operand, idx_operand);
|
||||
optimizer()->Write(&first);
|
||||
optimizer()->Write(&second);
|
||||
Flush();
|
||||
CHECK_EQ(write_count(), 1);
|
||||
CHECK_EQ(last_written().bytecode(), Bytecode::kTestUndetectable);
|
||||
CHECK_EQ(last_written().operand_count(), 1);
|
||||
CHECK_EQ(last_written().operand(0), reg_operand);
|
||||
Reset();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace interpreter
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
Loading…
Reference in New Issue
Block a user