Commit 23f7d34d authored by rmcilroy's avatar rmcilroy Committed by Commit bot

[Interpreter] Add support for property store operations.

Adds support for property store operations via Store/KeyedStore ICs. Adds the
following bytecodes:
 - StoreIC
 - KeyedStoreIC

The --vector_store flag is now required for --ignition.

BUG=v8:4280
LOG=N

Review URL: https://codereview.chromium.org/1319833004

Cr-Commit-Position: refs/heads/master@{#30660}
parent 164f92d2
......@@ -237,19 +237,37 @@ Node* InterpreterAssembler::LoadTypeFeedbackVector() {
Node* InterpreterAssembler::CallIC(CallInterfaceDescriptor descriptor,
Node* target, Node* arg1, Node* arg2,
Node* arg3, Node* arg4) {
Node* target, Node** args) {
CallDescriptor* call_descriptor = Linkage::GetStubCallDescriptor(
isolate(), zone(), descriptor, 0, CallDescriptor::kNoFlags);
return raw_assembler_->CallN(call_descriptor, target, args);
}
Node* InterpreterAssembler::CallIC(CallInterfaceDescriptor descriptor,
Node* target, Node* arg1, Node* arg2,
Node* arg3, Node* arg4) {
Node** args = zone()->NewArray<Node*>(5);
args[0] = arg1;
args[1] = arg2;
args[2] = arg3;
args[3] = arg4;
args[4] = ContextTaggedPointer();
return CallIC(descriptor, target, args);
}
return raw_assembler_->CallN(call_descriptor, target, args);
Node* InterpreterAssembler::CallIC(CallInterfaceDescriptor descriptor,
Node* target, Node* arg1, Node* arg2,
Node* arg3, Node* arg4, Node* arg5) {
Node** args = zone()->NewArray<Node*>(6);
args[0] = arg1;
args[1] = arg2;
args[2] = arg3;
args[3] = arg4;
args[4] = arg5;
args[5] = ContextTaggedPointer();
return CallIC(descriptor, target, args);
}
......
......@@ -83,6 +83,8 @@ class InterpreterAssembler {
// Call an IC code stub.
Node* CallIC(CallInterfaceDescriptor descriptor, Node* target, Node* arg1,
Node* arg2, Node* arg3, Node* arg4);
Node* CallIC(CallInterfaceDescriptor descriptor, Node* target, Node* arg1,
Node* arg2, Node* arg3, Node* arg4, Node* arg5);
// Call JS builtin.
Node* CallJSBuiltin(int context_index, Node* receiver);
......@@ -121,6 +123,7 @@ class InterpreterAssembler {
Node* BytecodeOperand(int operand_index);
Node* BytecodeOperandSignExtended(int operand_index);
Node* CallIC(CallInterfaceDescriptor descriptor, Node* target, Node** args);
Node* CallJSBuiltin(int context_index, Node* receiver, Node** js_args,
int js_arg_count);
......
......@@ -287,6 +287,7 @@ DEFINE_BOOL(string_slices, true, "use string slices")
// Flags for Ignition.
DEFINE_BOOL(ignition, false, "use ignition interpreter")
DEFINE_IMPLICATION(ignition, vector_stores)
DEFINE_STRING(ignition_filter, "~~", "filter for ignition interpreter")
DEFINE_BOOL(print_bytecode, false,
"print bytecode generated by ignition interpreter")
......
......@@ -154,7 +154,7 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::StoreAccumulatorInRegister(
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadNamedProperty(
Register object, int feedback_slot, LanguageMode language_mode) {
if (is_strong(language_mode)) {
if (!is_sloppy(language_mode)) {
UNIMPLEMENTED();
}
......@@ -170,7 +170,7 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::LoadNamedProperty(
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadKeyedProperty(
Register object, int feedback_slot, LanguageMode language_mode) {
if (is_strong(language_mode)) {
if (!is_sloppy(language_mode)) {
UNIMPLEMENTED();
}
......@@ -184,6 +184,40 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::LoadKeyedProperty(
}
BytecodeArrayBuilder& BytecodeArrayBuilder::StoreNamedProperty(
Register object, Register name, int feedback_slot,
LanguageMode language_mode) {
if (!is_sloppy(language_mode)) {
UNIMPLEMENTED();
}
if (FitsInByteOperand(feedback_slot)) {
Output(Bytecode::kStoreIC, object.ToOperand(), name.ToOperand(),
static_cast<uint8_t>(feedback_slot));
} else {
UNIMPLEMENTED();
}
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::StoreKeyedProperty(
Register object, Register key, int feedback_slot,
LanguageMode language_mode) {
if (!is_sloppy(language_mode)) {
UNIMPLEMENTED();
}
if (FitsInByteOperand(feedback_slot)) {
Output(Bytecode::kKeyedStoreIC, object.ToOperand(), key.ToOperand(),
static_cast<uint8_t>(feedback_slot));
} else {
UNIMPLEMENTED();
}
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::Return() {
Output(Bytecode::kReturn);
return *this;
......
......@@ -59,6 +59,14 @@ class BytecodeArrayBuilder {
BytecodeArrayBuilder& LoadKeyedProperty(Register object, int feedback_slot,
LanguageMode language_mode);
// Store properties. The value to be stored should be in the accumulator.
BytecodeArrayBuilder& StoreNamedProperty(Register object, Register name,
int feedback_slot,
LanguageMode language_mode);
BytecodeArrayBuilder& StoreKeyedProperty(Register object, Register key,
int feedback_slot,
LanguageMode language_mode);
// Operators.
BytecodeArrayBuilder& BinaryOperation(Token::Value binop, Register reg);
......
......@@ -270,14 +270,49 @@ void BytecodeGenerator::VisitVariableProxy(VariableProxy* proxy) {
void BytecodeGenerator::VisitAssignment(Assignment* expr) {
DCHECK(expr->target()->IsValidReferenceExpression());
TemporaryRegisterScope temporary_register_scope(&builder_);
Register object, key;
// Left-hand side can only be a property, a global or a variable slot.
Property* property = expr->target()->AsProperty();
LhsKind assign_type = Property::GetAssignType(property);
DCHECK(!expr->is_compound());
Visit(expr->value());
// Evaluate LHS expression.
switch (assign_type) {
case VARIABLE:
// Nothing to do to evaluate variable assignment LHS.
break;
case NAMED_PROPERTY:
object = temporary_register_scope.NewRegister();
key = temporary_register_scope.NewRegister();
Visit(property->obj());
builder().StoreAccumulatorInRegister(object);
builder().LoadLiteral(property->key()->AsLiteral()->AsPropertyName());
builder().StoreAccumulatorInRegister(key);
break;
case KEYED_PROPERTY:
object = temporary_register_scope.NewRegister();
key = temporary_register_scope.NewRegister();
Visit(property->obj());
builder().StoreAccumulatorInRegister(object);
Visit(property->key());
builder().StoreAccumulatorInRegister(key);
break;
case NAMED_SUPER_PROPERTY:
case KEYED_SUPER_PROPERTY:
UNIMPLEMENTED();
}
// Evaluate the value and potentially handle compound assignments by loading
// the left-hand side value and performing a binary operation.
if (expr->is_compound()) {
UNIMPLEMENTED();
} else {
Visit(expr->value());
}
// Store the value.
FeedbackVectorICSlot slot = expr->AssignmentSlot();
switch (assign_type) {
case VARIABLE: {
Variable* variable = expr->target()->AsVariableProxy()->var();
......@@ -287,7 +322,13 @@ void BytecodeGenerator::VisitAssignment(Assignment* expr) {
break;
}
case NAMED_PROPERTY:
builder().StoreNamedProperty(object, key, feedback_index(slot),
language_mode());
break;
case KEYED_PROPERTY:
builder().StoreKeyedProperty(object, key, feedback_index(slot),
language_mode());
break;
case NAMED_SUPER_PROPERTY:
case KEYED_SUPER_PROPERTY:
UNIMPLEMENTED();
......
......@@ -23,34 +23,38 @@ namespace interpreter {
V(Reg)
// The list of bytecodes which are interpreted by the interpreter.
#define BYTECODE_LIST(V) \
\
/* Loading the accumulator */ \
V(LdaZero, OperandType::kNone) \
V(LdaSmi8, OperandType::kImm8) \
V(LdaConstant, OperandType::kIdx) \
V(LdaUndefined, OperandType::kNone) \
V(LdaNull, OperandType::kNone) \
V(LdaTheHole, OperandType::kNone) \
V(LdaTrue, OperandType::kNone) \
V(LdaFalse, OperandType::kNone) \
\
/* Register-accumulator transfers */ \
V(Ldar, OperandType::kReg) \
V(Star, OperandType::kReg) \
\
/* LoadIC operations */ \
V(LoadIC, OperandType::kReg, OperandType::kIdx) \
V(KeyedLoadIC, OperandType::kReg, OperandType::kIdx) \
\
/* Binary Operators */ \
V(Add, OperandType::kReg) \
V(Sub, OperandType::kReg) \
V(Mul, OperandType::kReg) \
V(Div, OperandType::kReg) \
V(Mod, OperandType::kReg) \
\
/* Control Flow */ \
#define BYTECODE_LIST(V) \
\
/* Loading the accumulator */ \
V(LdaZero, OperandType::kNone) \
V(LdaSmi8, OperandType::kImm8) \
V(LdaConstant, OperandType::kIdx) \
V(LdaUndefined, OperandType::kNone) \
V(LdaNull, OperandType::kNone) \
V(LdaTheHole, OperandType::kNone) \
V(LdaTrue, OperandType::kNone) \
V(LdaFalse, OperandType::kNone) \
\
/* Register-accumulator transfers */ \
V(Ldar, OperandType::kReg) \
V(Star, OperandType::kReg) \
\
/* LoadIC operations */ \
V(LoadIC, OperandType::kReg, OperandType::kIdx) \
V(KeyedLoadIC, OperandType::kReg, OperandType::kIdx) \
\
/* StoreIC operations */ \
V(StoreIC, OperandType::kReg, OperandType::kReg, OperandType::kIdx) \
V(KeyedStoreIC, OperandType::kReg, OperandType::kReg, OperandType::kIdx) \
\
/* Binary Operators */ \
V(Add, OperandType::kReg) \
V(Sub, OperandType::kReg) \
V(Mul, OperandType::kReg) \
V(Div, OperandType::kReg) \
V(Mod, OperandType::kReg) \
\
/* Control Flow */ \
V(Return, OperandType::kNone)
......@@ -87,13 +91,18 @@ class Register {
static const int kMaxRegisterIndex = 127;
static const int kMinRegisterIndex = -128;
Register() : index_(kIllegalIndex) {}
explicit Register(int index) : index_(index) {
DCHECK_LE(index_, kMaxRegisterIndex);
DCHECK_GE(index_, kMinRegisterIndex);
}
int index() const { return index_; }
bool is_parameter() const { return index_ < 0; }
int index() const {
DCHECK(index_ != kIllegalIndex);
return index_;
}
bool is_parameter() const { return index() < 0; }
static Register FromParameterIndex(int index, int parameter_count);
int ToParameterIndex(int parameter_count) const;
......@@ -103,6 +112,8 @@ class Register {
uint8_t ToOperand() const;
private:
static const int kIllegalIndex = kMaxInt;
void* operator new(size_t size);
void operator delete(void* p);
......
......@@ -220,7 +220,7 @@ void Interpreter::DoLoadIC(compiler::InterpreterAssembler* assembler) {
// KeyedLoadIC <object> <slot>
//
// Calls the LoadIC at FeedBackVector slot <slot> for <object> and the key
// Calls the KeyedLoadIC at FeedBackVector slot <slot> for <object> and the key
// in the accumulator.
void Interpreter::DoKeyedLoadIC(compiler::InterpreterAssembler* assembler) {
Callable ic =
......@@ -229,6 +229,46 @@ void Interpreter::DoKeyedLoadIC(compiler::InterpreterAssembler* assembler) {
}
void Interpreter::DoPropertyStoreIC(Callable ic,
compiler::InterpreterAssembler* assembler) {
Node* code_target = __ HeapConstant(ic.code());
Node* object_reg_index = __ BytecodeOperandReg(0);
Node* object = __ LoadRegister(object_reg_index);
Node* name_reg_index = __ BytecodeOperandReg(1);
Node* name = __ LoadRegister(name_reg_index);
Node* value = __ GetAccumulator();
Node* raw_slot = __ BytecodeOperandIdx(2);
Node* smi_slot = __ SmiTag(raw_slot);
Node* type_feedback_vector = __ LoadTypeFeedbackVector();
Node* result = __ CallIC(ic.descriptor(), code_target, object, name, value,
smi_slot, type_feedback_vector);
__ SetAccumulator(result);
__ Dispatch();
}
// StoreIC <object> <name> <slot>
//
// Calls the StoreIC at FeedBackVector slot <slot> for <object> and the name
// <name> with the value in the accumulator.
void Interpreter::DoStoreIC(compiler::InterpreterAssembler* assembler) {
Callable ic =
CodeFactory::StoreICInOptimizedCode(isolate_, SLOPPY, UNINITIALIZED);
DoPropertyStoreIC(ic, assembler);
}
// KeyedStoreIC <object> <key> <slot>
//
// Calls the KeyStoreIC at FeedBackVector slot <slot> for <object> and the key
// <key> with the value in the accumulator.
void Interpreter::DoKeyedStoreIC(compiler::InterpreterAssembler* assembler) {
Callable ic =
CodeFactory::KeyedStoreICInOptimizedCode(isolate_, SLOPPY, UNINITIALIZED);
DoPropertyStoreIC(ic, assembler);
}
void Interpreter::DoBinaryOp(int builtin_context_index,
compiler::InterpreterAssembler* assembler) {
// TODO(rmcilroy): Call ICs which back-patch bytecode with type specialized
......
......@@ -55,6 +55,10 @@ class Interpreter {
// Generates code to perform a property load via |ic|.
void DoPropertyLoadIC(Callable ic, compiler::InterpreterAssembler* assembler);
// Generates code to perform a property store via |ic|.
void DoPropertyStoreIC(Callable ic,
compiler::InterpreterAssembler* assembler);
bool IsInterpreterTableInitialized(Handle<FixedArray> handler_table);
Isolate* isolate_;
......
......@@ -21,6 +21,7 @@ class BytecodeGeneratorHelper {
-InterpreterFrameConstants::kLastParamFromRegisterPointer / kPointerSize;
BytecodeGeneratorHelper() {
i::FLAG_vector_stores = true;
i::FLAG_ignition = true;
i::FLAG_ignition_filter = kFunctionName;
CcTest::i_isolate()->interpreter()->Initialize();
......@@ -419,6 +420,110 @@ TEST(PropertyLoads) {
}
}
TEST(PropertyStores) {
InitializedHandleScope handle_scope;
BytecodeGeneratorHelper helper;
Code::Kind ic_kinds[] = { i::Code::STORE_IC, i::Code::STORE_IC };
FeedbackVectorSpec feedback_spec(0, 1, ic_kinds);
Handle<i::TypeFeedbackVector> vector =
helper.factory()->NewTypeFeedbackVector(&feedback_spec);
ExpectedSnippet<const char*> snippets[] = {
{"function f(a) { a.name = \"val\"; }\nf({name : \"test\"})",
2 * kPointerSize, 2, 16,
{
B(Ldar), R(helper.kLastParamIndex),
B(Star), R(0),
B(LdaConstant), U8(0),
B(Star), R(1),
B(LdaConstant), U8(1),
B(StoreIC), R(0), R(1), U8(vector->first_ic_slot_index()),
B(LdaUndefined),
B(Return)
},
2, { "name", "val" }
},
{"function f(a) { a[\"key\"] = \"val\"; }\nf({key : \"test\"})",
2 * kPointerSize, 2, 16,
{
B(Ldar), R(helper.kLastParamIndex),
B(Star), R(0),
B(LdaConstant), U8(0),
B(Star), R(1),
B(LdaConstant), U8(1),
B(StoreIC), R(0), R(1), U8(vector->first_ic_slot_index()),
B(LdaUndefined),
B(Return)
},
2, { "key", "val" }
},
{"function f(a) { a[100] = \"val\"; }\nf({100 : \"test\"})",
2 * kPointerSize, 2, 16,
{
B(Ldar), R(helper.kLastParamIndex),
B(Star), R(0),
B(LdaSmi8), U8(100),
B(Star), R(1),
B(LdaConstant), U8(0),
B(KeyedStoreIC), R(0), R(1), U8(vector->first_ic_slot_index()),
B(LdaUndefined),
B(Return)
},
1, { "val" }
},
{"function f(a, b) { a[b] = \"val\"; }\nf({arg : \"test\"}, \"arg\")",
2 * kPointerSize, 3, 16,
{
B(Ldar), R(helper.kLastParamIndex - 1),
B(Star), R(0),
B(Ldar), R(helper.kLastParamIndex),
B(Star), R(1),
B(LdaConstant), U8(0),
B(KeyedStoreIC), R(0), R(1), U8(vector->first_ic_slot_index()),
B(LdaUndefined),
B(Return)
},
1, { "val" }
},
{"function f(a) { a.name = a[-124]; }\n"
"f({\"-124\" : \"test\", name : 123 })",
3 * kPointerSize, 2, 23,
{
B(Ldar), R(helper.kLastParamIndex),
B(Star), R(0),
B(LdaConstant), U8(0),
B(Star), R(1),
B(Ldar), R(helper.kLastParamIndex),
B(Star), R(2),
B(LdaSmi8), U8(-124),
B(KeyedLoadIC), R(2), U8(vector->first_ic_slot_index()),
B(StoreIC), R(0), R(1), U8(vector->first_ic_slot_index() + 2),
B(LdaUndefined),
B(Return)
},
1, { "name" }
}
};
size_t num_snippets = sizeof(snippets) / sizeof(snippets[0]);
for (size_t i = 0; i < num_snippets; i++) {
Handle<BytecodeArray> ba =
helper.MakeBytecode(snippets[i].code_snippet, "f");
CHECK_EQ(ba->frame_size(), snippets[i].frame_size);
CHECK_EQ(ba->parameter_count(), snippets[i].parameter_count);
CHECK_EQ(ba->length(), snippets[i].bytecode_length);
CHECK(!memcmp(ba->GetFirstBytecodeAddress(), snippets[i].bytecode,
ba->length()));
CHECK_EQ(ba->constant_pool()->length(), snippets[i].constant_count);
for (int j = 0; j < snippets[i].constant_count; j++) {
Handle<String> expected =
helper.factory()->NewStringFromAsciiChecked(snippets[i].constants[j]);
CHECK(String::cast(ba->constant_pool()->get(j))->Equals(*expected));
}
}
}
} // namespace interpreter
} // namespace internal
} // namespance v8
......@@ -59,6 +59,7 @@ class InterpreterTester {
: isolate_(isolate),
bytecode_(bytecode),
feedback_vector_(feedback_vector) {
i::FLAG_vector_stores = true;
i::FLAG_ignition = true;
// Ensure handler table is generated.
isolate->interpreter()->Initialize();
......@@ -109,6 +110,7 @@ class InterpreterTester {
using v8::internal::BytecodeArray;
using v8::internal::Handle;
using v8::internal::Object;
using v8::internal::Runtime;
using v8::internal::Smi;
using v8::internal::Token;
using namespace v8::internal::interpreter;
......@@ -558,3 +560,105 @@ TEST(InterpreterLoadKeyedProperty) {
return_val = callable(object3).ToHandleChecked();
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(789));
}
TEST(InterpreterStoreNamedProperty) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
i::Factory* factory = isolate->factory();
i::Code::Kind ic_kinds[] = {i::Code::STORE_IC};
i::FeedbackVectorSpec feedback_spec(0, 1, ic_kinds);
Handle<i::TypeFeedbackVector> vector =
factory->NewTypeFeedbackVector(&feedback_spec);
Handle<i::String> name = factory->NewStringFromAsciiChecked("val");
name = factory->string_table()->LookupString(isolate, name);
BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone());
builder.set_locals_count(1);
builder.set_parameter_count(1);
builder.LoadLiteral(name)
.StoreAccumulatorInRegister(Register(0))
.LoadLiteral(Smi::FromInt(999))
.StoreNamedProperty(builder.Parameter(0), Register(0),
vector->first_ic_slot_index(), i::SLOPPY)
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
InterpreterTester tester(isolate, bytecode_array, vector);
auto callable = tester.GetCallable<Handle<Object>>();
Handle<Object> object = tester.NewObject("({ val : 123 })");
// Test IC miss.
Handle<Object> result;
callable(object).ToHandleChecked();
CHECK(Runtime::GetObjectProperty(isolate, object, name).ToHandle(&result));
CHECK_EQ(Smi::cast(*result), Smi::FromInt(999));
// Test transition to monomorphic IC.
callable(object).ToHandleChecked();
CHECK(Runtime::GetObjectProperty(isolate, object, name).ToHandle(&result));
CHECK_EQ(Smi::cast(*result), Smi::FromInt(999));
// Test transition to polymorphic IC.
Handle<Object> object2 = tester.NewObject("({ val : 456, other : 123 })");
callable(object2).ToHandleChecked();
CHECK(Runtime::GetObjectProperty(isolate, object2, name).ToHandle(&result));
CHECK_EQ(Smi::cast(*result), Smi::FromInt(999));
// Test transition to megamorphic IC.
Handle<Object> object3 = tester.NewObject("({ val : 789, val2 : 123 })");
callable(object3).ToHandleChecked();
Handle<Object> object4 = tester.NewObject("({ val : 789, val3 : 123 })");
callable(object4).ToHandleChecked();
Handle<Object> object5 = tester.NewObject("({ val : 789, val4 : 123 })");
callable(object5).ToHandleChecked();
CHECK(Runtime::GetObjectProperty(isolate, object5, name).ToHandle(&result));
CHECK_EQ(Smi::cast(*result), Smi::FromInt(999));
}
TEST(InterpreterStoreKeyedProperty) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
i::Factory* factory = isolate->factory();
i::Code::Kind ic_kinds[] = {i::Code::KEYED_STORE_IC};
i::FeedbackVectorSpec feedback_spec(0, 1, ic_kinds);
Handle<i::TypeFeedbackVector> vector =
factory->NewTypeFeedbackVector(&feedback_spec);
Handle<i::String> name = factory->NewStringFromAsciiChecked("val");
name = factory->string_table()->LookupString(isolate, name);
BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone());
builder.set_locals_count(1);
builder.set_parameter_count(1);
builder.LoadLiteral(name)
.StoreAccumulatorInRegister(Register(0))
.LoadLiteral(Smi::FromInt(999))
.StoreKeyedProperty(builder.Parameter(0), Register(0),
vector->first_ic_slot_index(), i::SLOPPY)
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
InterpreterTester tester(isolate, bytecode_array, vector);
auto callable = tester.GetCallable<Handle<Object>>();
Handle<Object> object = tester.NewObject("({ val : 123 })");
// Test IC miss.
Handle<Object> result;
callable(object).ToHandleChecked();
CHECK(Runtime::GetObjectProperty(isolate, object, name).ToHandle(&result));
CHECK_EQ(Smi::cast(*result), Smi::FromInt(999));
// Test transition to monomorphic IC.
callable(object).ToHandleChecked();
CHECK(Runtime::GetObjectProperty(isolate, object, name).ToHandle(&result));
CHECK_EQ(Smi::cast(*result), Smi::FromInt(999));
// Test transition to megamorphic IC.
Handle<Object> object2 = tester.NewObject("({ val : 456, other : 123 })");
callable(object2).ToHandleChecked();
CHECK(Runtime::GetObjectProperty(isolate, object2, name).ToHandle(&result));
CHECK_EQ(Smi::cast(*result), Smi::FromInt(999));
}
......@@ -39,9 +39,11 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
Register reg(0);
builder.LoadAccumulatorWithRegister(reg).StoreAccumulatorInRegister(reg);
// Emit load property operations.
builder.LoadNamedProperty(reg, 0, LanguageMode::SLOPPY);
builder.LoadKeyedProperty(reg, 0, LanguageMode::STRICT);
// Emit load / store property operations.
builder.LoadNamedProperty(reg, 0, LanguageMode::SLOPPY)
.LoadKeyedProperty(reg, 0, LanguageMode::SLOPPY)
.StoreNamedProperty(reg, reg, 0, LanguageMode::SLOPPY)
.StoreKeyedProperty(reg, reg, 0, LanguageMode::SLOPPY);
// Emit binary operators invocations.
builder.BinaryOperation(Token::Value::ADD, reg)
......
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