Skip to content

Commit da2b685

Browse files
authored
Optimize get/set property access (#4452)
It changes the following: - Prevent creation of temporary object on field access (i.e. `"Hello".charCodeAt(1)` creates 1 temporary `String` object for access to `charCodeAt` method through the prototype) - Specialize get and set property instructions - Add micro benchmark for object access ### Benchmarks #### main ``` PROGRESS Richards RESULT Richards 180 PROGRESS DeltaBlue RESULT DeltaBlue 181 PROGRESS Encrypt PROGRESS Decrypt RESULT Crypto 144 PROGRESS RayTrace RESULT RayTrace 387 PROGRESS Earley PROGRESS Boyer RESULT EarleyBoyer 452 PROGRESS RegExp RESULT RegExp 71.8 PROGRESS Splay RESULT Splay 717 PROGRESS NavierStokes RESULT NavierStokes 314 SCORE 246 ``` #### PR ``` PROGRESS Richards RESULT Richards 185 PROGRESS DeltaBlue RESULT DeltaBlue 185 PROGRESS Encrypt PROGRESS Decrypt RESULT Crypto 143 PROGRESS RayTrace RESULT RayTrace 401 PROGRESS Earley PROGRESS Boyer RESULT EarleyBoyer 464 PROGRESS RegExp RESULT RegExp 70.4 PROGRESS Splay RESULT Splay 736 PROGRESS NavierStokes RESULT NavierStokes 323 SCORE 250 ```
1 parent 19bd761 commit da2b685

File tree

14 files changed

+517
-250
lines changed

14 files changed

+517
-250
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
(() => {
2+
let sum = "";
3+
let string = "Hello, world!!!";
4+
for (let i = 0; i < string.length; ++i) {
5+
sum += string.charCodeAt(i).toString(16);
6+
}
7+
return sum;
8+
})();

core/engine/benches/full.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ macro_rules! full_benchmarks {
9595
}
9696

9797
full_benchmarks!(
98+
{"String Code Point Sum", string_code_point_sum},
9899
{"Symbols", symbol_creation},
99100
{"For loop", for_loop},
100101
{"Fibonacci", fibonacci},

core/engine/src/bytecompiler/declaration/declaration_pattern.rs

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,7 @@ impl ByteCompiler<'_> {
4040

4141
match name {
4242
PropertyName::Literal(ident) => {
43-
self.emit_get_property_by_name(
44-
&dst,
45-
object,
46-
object,
47-
ident.sym(),
48-
);
43+
self.emit_get_property_by_name(&dst, None, object, ident.sym());
4944
let key = self.register_allocator.alloc();
5045
self.emit_push_literal(
5146
Literal::String(
@@ -162,7 +157,7 @@ impl ByteCompiler<'_> {
162157
PropertyName::Literal(ident) => {
163158
compiler.emit_get_property_by_name(
164159
&dst,
165-
object,
160+
None,
166161
object,
167162
ident.sym(),
168163
);
@@ -209,12 +204,7 @@ impl ByteCompiler<'_> {
209204

210205
match name {
211206
PropertyName::Literal(ident) => {
212-
self.emit_get_property_by_name(
213-
&dst,
214-
object,
215-
object,
216-
ident.sym(),
217-
);
207+
self.emit_get_property_by_name(&dst, None, object, ident.sym());
218208
}
219209
PropertyName::Computed(node) => {
220210
let key = self.register_allocator.alloc();

core/engine/src/bytecompiler/expression/assign.rs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -183,11 +183,11 @@ impl ByteCompiler<'_> {
183183
let object = compiler.register_allocator.alloc();
184184
compiler.compile_expr(access.target(), &object);
185185

186-
compiler.emit_get_property_by_name(dst, &object, &object, name.sym());
186+
compiler.emit_get_property_by_name(dst, None, &object, name.sym());
187187

188188
early_exit = emit(&mut compiler, dst, assign.rhs(), assign.op());
189189

190-
compiler.emit_set_property_by_name(dst, &object, &object, name.sym());
190+
compiler.emit_set_property_by_name(dst, None, &object, name.sym());
191191

192192
compiler.register_allocator.dealloc(object);
193193
}
@@ -247,11 +247,21 @@ impl ByteCompiler<'_> {
247247
compiler.bytecode.emit_super(object.variable());
248248
compiler.bytecode.emit_this(receiver.variable());
249249

250-
compiler.emit_get_property_by_name(dst, &receiver, &object, name.sym());
250+
compiler.emit_get_property_by_name(
251+
dst,
252+
Some(&receiver),
253+
&object,
254+
name.sym(),
255+
);
251256

252257
early_exit = emit(&mut compiler, dst, assign.rhs(), assign.op());
253258

254-
compiler.emit_set_property_by_name(dst, &receiver, &object, name.sym());
259+
compiler.emit_set_property_by_name(
260+
dst,
261+
Some(&receiver),
262+
&object,
263+
name.sym(),
264+
);
255265

256266
compiler.register_allocator.dealloc(receiver);
257267
compiler.register_allocator.dealloc(object);

core/engine/src/bytecompiler/expression/mod.rs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -263,12 +263,7 @@ impl ByteCompiler<'_> {
263263
self.compile_expr(access.target(), &this);
264264
match access.field() {
265265
PropertyAccessField::Const(ident) => {
266-
self.emit_get_property_by_name(
267-
&function,
268-
&this,
269-
&this,
270-
ident.sym(),
271-
);
266+
self.emit_get_property_by_name(&function, None, &this, ident.sym());
272267
}
273268
PropertyAccessField::Expr(field) => {
274269
let key = self.register_allocator.alloc();

core/engine/src/bytecompiler/expression/update.rs

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -83,20 +83,15 @@ impl ByteCompiler<'_> {
8383

8484
match access.field() {
8585
PropertyAccessField::Const(ident) => {
86-
compiler.emit_get_property_by_name(dst, &object, &object, ident.sym());
86+
compiler.emit_get_property_by_name(dst, None, &object, ident.sym());
8787
let value = compiler.register_allocator.alloc();
8888
if increment {
8989
compiler.bytecode.emit_inc(value.variable(), dst.variable());
9090
} else {
9191
compiler.bytecode.emit_dec(value.variable(), dst.variable());
9292
}
9393

94-
compiler.emit_set_property_by_name(
95-
&value,
96-
&object,
97-
&object,
98-
ident.sym(),
99-
);
94+
compiler.emit_set_property_by_name(&value, None, &object, ident.sym());
10095

10196
if !post {
10297
compiler
@@ -184,7 +179,12 @@ impl ByteCompiler<'_> {
184179
compiler.bytecode.emit_super(object.variable());
185180
compiler.bytecode.emit_this(receiver.variable());
186181

187-
compiler.emit_get_property_by_name(dst, &receiver, &object, ident.sym());
182+
compiler.emit_get_property_by_name(
183+
dst,
184+
Some(&receiver),
185+
&object,
186+
ident.sym(),
187+
);
188188

189189
let value = compiler.register_allocator.alloc();
190190
if increment {
@@ -193,7 +193,12 @@ impl ByteCompiler<'_> {
193193
compiler.bytecode.emit_dec(value.variable(), dst.variable());
194194
}
195195

196-
compiler.emit_set_property_by_name(&value, &receiver, &object, ident.sym());
196+
compiler.emit_set_property_by_name(
197+
&value,
198+
Some(&receiver),
199+
&object,
200+
ident.sym(),
201+
);
197202
if !post {
198203
compiler
199204
.bytecode

core/engine/src/bytecompiler/mod.rs

Lines changed: 49 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ use boa_ast::{
5252
use boa_gc::Gc;
5353
use boa_interner::{Interner, Sym};
5454
use boa_macros::js_str;
55+
use boa_string::StaticJsStrings;
5556
use rustc_hash::FxHashMap;
5657
use thin_vec::ThinVec;
5758

@@ -921,7 +922,7 @@ impl<'ctx> ByteCompiler<'ctx> {
921922
fn emit_get_property_by_name(
922923
&mut self,
923924
dst: &Register,
924-
receiver: &Register,
925+
receiver: Option<&Register>,
925926
value: &Register,
926927
ident: Sym,
927928
) {
@@ -933,18 +934,32 @@ impl<'ctx> ByteCompiler<'ctx> {
933934
};
934935
self.ic.push(InlineCache::new(name.clone()));
935936

936-
self.bytecode.emit_get_property_by_name(
937-
dst.variable(),
938-
receiver.variable(),
939-
value.variable(),
940-
ic_index.into(),
941-
);
937+
if let Some(receiver) = receiver {
938+
self.bytecode.emit_get_property_by_name_with_this(
939+
dst.variable(),
940+
receiver.variable(),
941+
value.variable(),
942+
ic_index.into(),
943+
);
944+
} else if name == &StaticJsStrings::LENGTH {
945+
self.bytecode.emit_get_length_property(
946+
dst.variable(),
947+
value.variable(),
948+
ic_index.into(),
949+
);
950+
} else {
951+
self.bytecode.emit_get_property_by_name(
952+
dst.variable(),
953+
value.variable(),
954+
ic_index.into(),
955+
);
956+
}
942957
}
943958

944959
fn emit_set_property_by_name(
945960
&mut self,
946961
value: &Register,
947-
receiver: &Register,
962+
receiver: Option<&Register>,
948963
object: &Register,
949964
ident: Sym,
950965
) {
@@ -956,12 +971,20 @@ impl<'ctx> ByteCompiler<'ctx> {
956971
};
957972
self.ic.push(InlineCache::new(name.clone()));
958973

959-
self.bytecode.emit_set_property_by_name(
960-
value.variable(),
961-
receiver.variable(),
962-
object.variable(),
963-
ic_index.into(),
964-
);
974+
if let Some(receiver) = receiver {
975+
self.bytecode.emit_set_property_by_name_with_this(
976+
value.variable(),
977+
receiver.variable(),
978+
object.variable(),
979+
ic_index.into(),
980+
);
981+
} else {
982+
self.bytecode.emit_set_property_by_name(
983+
value.variable(),
984+
object.variable(),
985+
ic_index.into(),
986+
);
987+
}
965988
}
966989

967990
fn emit_type_error(&mut self, message: &str) {
@@ -1119,7 +1142,7 @@ impl<'ctx> ByteCompiler<'ctx> {
11191142

11201143
match access.field() {
11211144
PropertyAccessField::Const(ident) => {
1122-
compiler.emit_get_property_by_name(dst, &object, &object, ident.sym());
1145+
compiler.emit_get_property_by_name(dst, None, &object, ident.sym());
11231146
}
11241147
PropertyAccessField::Expr(expr) => {
11251148
let key = compiler.register_allocator.alloc();
@@ -1157,7 +1180,12 @@ impl<'ctx> ByteCompiler<'ctx> {
11571180
compiler.bytecode.emit_this(receiver.variable());
11581181
match access.field() {
11591182
PropertyAccessField::Const(ident) => {
1160-
compiler.emit_get_property_by_name(dst, &receiver, &value, ident.sym());
1183+
compiler.emit_get_property_by_name(
1184+
dst,
1185+
Some(&receiver),
1186+
&value,
1187+
ident.sym(),
1188+
);
11611189
}
11621190
PropertyAccessField::Expr(expr) => {
11631191
let key = compiler.register_allocator.alloc();
@@ -1222,7 +1250,7 @@ impl<'ctx> ByteCompiler<'ctx> {
12221250
let object = self.register_allocator.alloc();
12231251
self.compile_expr(access.target(), &object);
12241252
let value = expr_fn(self);
1225-
self.emit_set_property_by_name(value, &object, &object, name.sym());
1253+
self.emit_set_property_by_name(value, None, &object, name.sym());
12261254
self.register_allocator.dealloc(object);
12271255
}
12281256
PropertyAccessField::Expr(expr) => {
@@ -1271,7 +1299,7 @@ impl<'ctx> ByteCompiler<'ctx> {
12711299

12721300
let value = expr_fn(self);
12731301

1274-
self.emit_set_property_by_name(value, &receiver, &object, name.sym());
1302+
self.emit_set_property_by_name(value, Some(&receiver), &object, name.sym());
12751303

12761304
self.register_allocator.dealloc(receiver);
12771305
self.register_allocator.dealloc(object);
@@ -1393,7 +1421,7 @@ impl<'ctx> ByteCompiler<'ctx> {
13931421

13941422
match access.field() {
13951423
PropertyAccessField::Const(ident) => {
1396-
self.emit_get_property_by_name(dst, this, this, ident.sym());
1424+
self.emit_get_property_by_name(dst, None, this, ident.sym());
13971425
}
13981426
PropertyAccessField::Expr(field) => {
13991427
let key = self.register_allocator.alloc();
@@ -1422,7 +1450,7 @@ impl<'ctx> ByteCompiler<'ctx> {
14221450

14231451
match access.field() {
14241452
PropertyAccessField::Const(ident) => {
1425-
self.emit_get_property_by_name(dst, this, &object, ident.sym());
1453+
self.emit_get_property_by_name(dst, Some(this), &object, ident.sym());
14261454
}
14271455
PropertyAccessField::Expr(expr) => {
14281456
let key = self.register_allocator.alloc();
@@ -1525,7 +1553,7 @@ impl<'ctx> ByteCompiler<'ctx> {
15251553
self.bytecode.emit_move(this.variable(), value.variable());
15261554
match field {
15271555
PropertyAccessField::Const(name) => {
1528-
self.emit_get_property_by_name(value, value, value, name.sym());
1556+
self.emit_get_property_by_name(value, None, value, name.sym());
15291557
}
15301558
PropertyAccessField::Expr(expr) => {
15311559
let key = self.register_allocator.alloc();

core/engine/src/value/mod.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -614,6 +614,23 @@ impl JsValue {
614614
}
615615
}
616616

617+
pub(crate) fn base_class(&self, context: &Context) -> JsResult<JsObject> {
618+
let constructors = context.intrinsics().constructors();
619+
match self.variant() {
620+
JsVariant::Undefined | JsVariant::Null => Err(JsNativeError::typ()
621+
.with_message("cannot convert 'null' or 'undefined' to object")
622+
.into()),
623+
JsVariant::Boolean(_) => Ok(constructors.boolean().prototype()),
624+
JsVariant::Integer32(_) | JsVariant::Float64(_) => {
625+
Ok(constructors.number().prototype())
626+
}
627+
JsVariant::String(_) => Ok(constructors.string().prototype()),
628+
JsVariant::Symbol(_) => Ok(constructors.symbol().prototype()),
629+
JsVariant::BigInt(_) => Ok(constructors.bigint().prototype()),
630+
JsVariant::Object(object) => Ok(object.clone()),
631+
}
632+
}
633+
617634
/// Converts the value to a `PropertyKey`, that can be used as a key for properties.
618635
///
619636
/// See <https://tc39.es/ecma262/#sec-topropertykey>

core/engine/src/vm/code_block.rs

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -626,7 +626,30 @@ impl CodeBlock {
626626
Instruction::DeletePropertyByName { object, name_index } => {
627627
format!("object:{object}, name_index:{name_index}")
628628
}
629+
Instruction::GetLengthProperty {
630+
dst,
631+
value,
632+
ic_index,
633+
} => {
634+
let ic = &self.ic[u32::from(*ic_index) as usize];
635+
format!(
636+
"dst:{dst}, value:{value}, shape:0x{:x}]",
637+
ic.shape.borrow().to_addr_usize(),
638+
)
639+
}
629640
Instruction::GetPropertyByName {
641+
dst,
642+
value,
643+
ic_index,
644+
} => {
645+
let ic = &self.ic[u32::from(*ic_index) as usize];
646+
format!(
647+
"dst:{dst}, value:{value}, ic:[name:{}, shape:0x{:x}]",
648+
ic.name.to_std_string_escaped(),
649+
ic.shape.borrow().to_addr_usize(),
650+
)
651+
}
652+
Instruction::GetPropertyByNameWithThis {
630653
dst,
631654
receiver,
632655
value,
@@ -640,6 +663,17 @@ impl CodeBlock {
640663
)
641664
}
642665
Instruction::SetPropertyByName {
666+
value,
667+
object,
668+
ic_index,
669+
} => {
670+
let ic = &self.ic[u32::from(*ic_index) as usize];
671+
format!(
672+
"object:{object}, value:{value}, ic:shape:0x{:x}",
673+
ic.shape.borrow().to_addr_usize(),
674+
)
675+
}
676+
Instruction::SetPropertyByNameWithThis {
643677
value,
644678
receiver,
645679
object,
@@ -901,10 +935,7 @@ impl CodeBlock {
901935
| Instruction::Reserved57
902936
| Instruction::Reserved58
903937
| Instruction::Reserved59
904-
| Instruction::Reserved60
905-
| Instruction::Reserved61
906-
| Instruction::Reserved62
907-
| Instruction::Reserved63 => unreachable!("Reserved opcodes are unreachable"),
938+
| Instruction::Reserved60 => unreachable!("Reserved opcodes are unreachable"),
908939
}
909940
}
910941
}

0 commit comments

Comments
 (0)