Commit 43633af0 authored by Shu-yu Guo's avatar Shu-yu Guo Committed by V8 LUCI CQ

[interpreter] Use fast paths again for object literals with spread cloning

[1] fixes the behavior of StaNamedOwnProperty to no longer do prototype
lookups. This lets us revert [2] and go back to using the fast path in
the clone spread object literal bytecode.

The test case from [2] is kept.

[1] https://chromium-review.googlesource.com/c/v8/v8/+/2795831
[2] https://chromium-review.googlesource.com/c/v8/v8/+/3178969

Bug: v8:9888, chromium:1251366
Change-Id: I9d2cb69b803c403f63365f55d27c4de20ff7dafb
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3224666Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Reviewed-by: 's avatarPatrick Thier <pthier@chromium.org>
Reviewed-by: 's avatarLeszek Swirski <leszeks@chromium.org>
Commit-Queue: Shu-yu Guo <syg@chromium.org>
Cr-Commit-Position: refs/heads/main@{#77444}
parent 56540024
...@@ -3015,60 +3015,51 @@ void BytecodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { ...@@ -3015,60 +3015,51 @@ void BytecodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
BuildCreateObjectLiteral(literal, flags, entry); BuildCreateObjectLiteral(literal, flags, entry);
} }
// If we used CloneObject for the first element is spread case, we already
// copied accessors. Therefore skip the static initialization and treat all
// properties after the spread as dynamic.
// TOOD(v8:9888): Use new Define ICs instead of Set ICs in the clone object
// spread case.
if (!clone_object_spread) {
// Store computed values into the literal. // Store computed values into the literal.
AccessorTable<ObjectLiteral::Property> accessor_table(zone()); AccessorTable<ObjectLiteral::Property> accessor_table(zone());
for (; property_index < expr->properties()->length(); property_index++) { for (; property_index < expr->properties()->length(); property_index++) {
ObjectLiteral::Property* property = ObjectLiteral::Property* property = expr->properties()->at(property_index);
expr->properties()->at(property_index);
if (property->is_computed_name()) break; if (property->is_computed_name()) break;
if (property->IsCompileTimeValue()) continue; if (!clone_object_spread && property->IsCompileTimeValue()) continue;
RegisterAllocationScope inner_register_scope(this); RegisterAllocationScope inner_register_scope(this);
Literal* key = property->key()->AsLiteral(); Literal* key = property->key()->AsLiteral();
switch (property->kind()) { switch (property->kind()) {
case ObjectLiteral::Property::SPREAD: case ObjectLiteral::Property::SPREAD:
case ObjectLiteral::Property::CONSTANT:
UNREACHABLE(); UNREACHABLE();
case ObjectLiteral::Property::CONSTANT:
case ObjectLiteral::Property::MATERIALIZED_LITERAL: case ObjectLiteral::Property::MATERIALIZED_LITERAL:
DCHECK(!property->value()->IsCompileTimeValue()); DCHECK(clone_object_spread || !property->value()->IsCompileTimeValue());
V8_FALLTHROUGH; V8_FALLTHROUGH;
case ObjectLiteral::Property::COMPUTED: { case ObjectLiteral::Property::COMPUTED: {
// It is safe to use [[Put]] here because the boilerplate already // It is safe to use [[Put]] here because the boilerplate already
// contains computed properties with an uninitialized value. // contains computed properties with an uninitialized value.
Register key_reg;
if (key->IsStringLiteral()) { if (key->IsStringLiteral()) {
DCHECK(key->IsPropertyName()); DCHECK(key->IsPropertyName());
object_literal_context_scope.SetEnteredIf(
property->value()->IsConciseMethodDefinition());
if (property->emit_store()) {
builder()->SetExpressionPosition(property->value());
VisitForAccumulatorValue(property->value());
FeedbackSlot slot = feedback_spec()->AddStoreOwnICSlot();
builder()->StoreNamedOwnProperty(
literal, key->AsRawPropertyName(), feedback_index(slot));
} else { } else {
builder()->SetExpressionPosition(property->value()); key_reg = register_allocator()->NewRegister();
VisitForEffect(property->value());
}
} else {
RegisterList args = register_allocator()->NewRegisterList(3);
builder()->MoveRegister(literal, args[0]);
builder()->SetExpressionPosition(property->key()); builder()->SetExpressionPosition(property->key());
VisitForRegisterValue(property->key(), args[1]); VisitForRegisterValue(property->key(), key_reg);
}
object_literal_context_scope.SetEnteredIf( object_literal_context_scope.SetEnteredIf(
property->value()->IsConciseMethodDefinition()); property->value()->IsConciseMethodDefinition());
builder()->SetExpressionPosition(property->value()); builder()->SetExpressionPosition(property->value());
VisitForRegisterValue(property->value(), args[2]);
if (property->emit_store()) { if (property->emit_store()) {
builder()->CallRuntime(Runtime::kSetKeyedProperty, args); VisitForAccumulatorValue(property->value());
if (key->IsStringLiteral()) {
FeedbackSlot slot = feedback_spec()->AddStoreOwnICSlot();
builder()->StoreNamedOwnProperty(literal, key->AsRawPropertyName(),
feedback_index(slot));
} else {
FeedbackSlot slot = feedback_spec()->AddKeyedDefineOwnICSlot();
builder()->DefineKeyedProperty(literal, key_reg,
feedback_index(slot));
} }
} else {
VisitForEffect(property->value());
} }
break; break;
} }
...@@ -3113,7 +3104,6 @@ void BytecodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { ...@@ -3113,7 +3104,6 @@ void BytecodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
.StoreAccumulatorInRegister(args[4]) .StoreAccumulatorInRegister(args[4])
.CallRuntime(Runtime::kDefineAccessorPropertyUnchecked, args); .CallRuntime(Runtime::kDefineAccessorPropertyUnchecked, args);
} }
}
// Object literals have two parts. The "static" part on the left contains no // Object literals have two parts. The "static" part on the left contains no
// computed property names, and so we can compute its map ahead of time; see // computed property names, and so we can compute its map ahead of time; see
......
...@@ -231,20 +231,19 @@ handlers: [ ...@@ -231,20 +231,19 @@ handlers: [
snippet: " snippet: "
var a = 1; return { 1: a }; var a = 1; return { 1: a };
" "
frame size: 5 frame size: 3
parameter count: 1 parameter count: 1
bytecode array length: 25 bytecode array length: 20
bytecodes: [ bytecodes: [
/* 42 S> */ B(LdaSmi), I8(1), /* 42 S> */ B(LdaSmi), I8(1),
B(Star0), B(Star0),
/* 45 S> */ B(CreateObjectLiteral), U8(0), U8(0), U8(41), /* 45 S> */ B(CreateObjectLiteral), U8(0), U8(0), U8(41),
B(Star1), B(Star1),
B(LdaSmi), I8(1), B(LdaSmi), I8(1),
B(Star3), B(Star2),
B(Mov), R(1), R(2), B(Ldar), R(0),
B(Mov), R(0), R(4), /* 57 E> */ B(StaKeyedPropertyAsDefine), R(1), R(2), U8(1),
/* 57 E> */ B(CallRuntime), U16(Runtime::kSetKeyedProperty), R(2), U8(3), B(Ldar), R(1),
B(Ldar), R(2),
/* 61 S> */ B(Return), /* 61 S> */ B(Return),
] ]
constant pool: [ constant pool: [
......
...@@ -6,8 +6,15 @@ Object.defineProperty(Object.prototype, "x", { ...@@ -6,8 +6,15 @@ Object.defineProperty(Object.prototype, "x", {
set: function (val) {} set: function (val) {}
}); });
Object.defineProperty(Object.prototype, "0", {
set: function (val) {}
});
let o = {...{}, x: 3}; let o = {...{}, x: 3};
assertEquals(o.x, 3); assertEquals(3, o.x);
let o2 = {...{x: 1}, x: 3}; let o2 = {...{x: 1}, x: 3};
assertEquals(o2.x, 3); assertEquals(3, o2.x);
let o3 = {...{}, 0: 42}
assertEquals(42, o3[0]);
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