Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions test_arrow.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const add = (a, b) => a + b;
let sum = 0;
for (let i = 0; i < 1000000; i++) {
sum = add(i, 1);
}
10 changes: 10 additions & 0 deletions benches/scripts/calls/call_arrow.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const test = () => 1;
// Warmup
for (let i = 0; i < 1000; i++) {
test();
}
let sum = 0;
for (let i = 0; i < 10000000; i++) {
sum += test();
}
console.log("Arrow Call Sum:", sum);
17 changes: 17 additions & 0 deletions benches/scripts/calls/call_async.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
async function test() {
return 1;
}

(async function () {
// Warmup
for (let i = 0; i < 1000; i++) {
await test();
}

let sum = 0;
for (let i = 0; i < 1000000; i++) { // Using 1M for async due to Promise overhead
sum += await test();
}

console.log("Async Call Sum:", sum);
})();
19 changes: 19 additions & 0 deletions benches/scripts/calls/call_construct.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
class Test {
constructor(a, b) {
this.a = a;
this.b = b;
}
}

// Warmup
for (let i = 0; i < 1000; i++) {
new Test(i, i + 1);
}

const start = Date.now();
for (let i = 0; i < 1000000; i++) {
new Test(i, i + 1);
}
const end = Date.now();

console.log("Time taken for 1M instantiations: " + (end - start) + "ms");
12 changes: 12 additions & 0 deletions benches/scripts/calls/call_simple.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
function test() {
return 1;
}
// Warmup
for (let i = 0; i < 1000; i++) {
test();
}
let sum = 0;
for (let i = 0; i < 10000000; i++) {
sum += test();
}
console.log(sum);
17 changes: 17 additions & 0 deletions benches/scripts/calls/call_spread.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
function test(a, b, c) {
return a + b + c;
}

const args = [1, 2, 3];

// Warmup
for (let i = 0; i < 1000; i++) {
test(...args);
}

let sum = 0;
for (let i = 0; i < 10000000; i++) {
sum += test(...args);
}

console.log("Spread Call Sum:", sum);
21 changes: 21 additions & 0 deletions core/engine/src/builtins/promise/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,27 @@ impl PromiseCapability {
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-newpromisecapability
///
/// `NewPromiseCapability ( C )`
/// Fast path for creating a promise capability for internal VM use (async functions).
/// Bypasses the executor closure and construct call.
pub(crate) fn new_internal(context: &mut Context) -> Self {
let promise = JsObject::from_proto_and_data(
context.intrinsics().constructors().promise().prototype(),
Promise::new(),
);
let promise_typed: JsObject<Promise> = promise
.clone()
.downcast()
.expect("promise object should be a Promise");
let resolving_functions = Promise::create_resolving_functions(&promise_typed, context);
Self {
promise,
functions: resolving_functions,
}
}

/// `NewPromiseCapability ( C )`
pub(crate) fn new(c: &JsObject, context: &mut Context) -> JsResult<Self> {
#[derive(Debug, Clone, Trace, Finalize)]
struct RejectResolve {
Expand Down
5 changes: 4 additions & 1 deletion core/engine/src/bytecompiler/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,10 @@ impl FunctionCompiler {
// See: 15.6.2 Runtime Semantics: EvaluateAsyncGeneratorBody: https://tc39.es/ecma262/#sec-runtime-semantics-evaluateasyncgeneratorbody
if compiler.is_async() && !compiler.is_generator() {
// 1. Let promiseCapability be ! NewPromiseCapability(%Promise%).
compiler.bytecode.emit_create_promise_capability();
let ic_index = compiler.push_async_call_ic();
compiler
.bytecode
.emit_create_promise_capability(ic_index.into());

// 2. Let declResult be Completion(FunctionDeclarationInstantiation(functionObject, argumentsList)).
//
Expand Down
36 changes: 31 additions & 5 deletions core/engine/src/bytecompiler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ use crate::{
builtins::function::{ThisMode, arguments::MappedArguments},
js_string,
vm::{
CallFrame, CodeBlock, CodeBlockFlags, Constant, GeneratorResumeKind, GlobalFunctionBinding,
Handler, InlineCache,
AsyncCallCache, CallFrame, CallSpreadCache, CodeBlock, CodeBlockFlags, Constant,
GeneratorResumeKind, GlobalFunctionBinding, Handler, InlineCache,
opcode::{Address, BindingOpcode, BytecodeEmitter, RegisterOperand},
source_info::{SourceInfo, SourceMap, SourceMapBuilder, SourcePath},
},
Expand Down Expand Up @@ -520,6 +520,10 @@ pub struct ByteCompiler<'ctx> {
code_block_flags: CodeBlockFlags,
handlers: ThinVec<Handler>,
pub(crate) ic: Vec<InlineCache>,
pub(crate) call_spread_ic: Vec<CallSpreadCache>,

/// Inline cache for async calls.
pub(crate) async_call_ic: Vec<AsyncCallCache>,
literals_map: FxHashMap<Literal, u32>,
names_map: FxHashMap<Sym, u32>,
bindings_map: FxHashMap<BindingLocator, u32>,
Expand Down Expand Up @@ -649,7 +653,9 @@ impl<'ctx> ByteCompiler<'ctx> {
register_allocator,
code_block_flags,
handlers: ThinVec::default(),
ic: Vec::default(),
ic: Vec::new(),
call_spread_ic: Vec::new(),
async_call_ic: Vec::new(),

literals_map: FxHashMap::default(),
names_map: FxHashMap::default(),
Expand Down Expand Up @@ -883,6 +889,20 @@ impl<'ctx> ByteCompiler<'ctx> {
self.bytecode.emit_push_from_register(src.variable());
}

/// Pushes a new call spread inline cache to the code block.
pub(crate) fn push_call_spread_ic(&mut self) -> u32 {
let index = self.call_spread_ic.len() as u32;
self.call_spread_ic.push(CallSpreadCache::new());
index
}

/// Pushes an async call inline cache index.
pub(crate) fn push_async_call_ic(&mut self) -> u32 {
let index = self.async_call_ic.len() as u32;
self.async_call_ic.push(AsyncCallCache::new());
index
}

pub(crate) fn emit_binding_access(
&mut self,
opcode: BindingAccessOpcode,
Expand Down Expand Up @@ -2087,7 +2107,8 @@ impl<'ctx> ByteCompiler<'ctx> {
self.register_allocator.dealloc(value);
self.register_allocator.dealloc(array);

self.bytecode.emit_call_spread();
let ic_index = self.push_call_spread_ic();
self.bytecode.emit_call_spread(ic_index.into());
} else {
for arg in args {
self.compile_expr_to_stack(arg);
Expand Down Expand Up @@ -2678,7 +2699,10 @@ impl<'ctx> ByteCompiler<'ctx> {
.emit_call_eval((call.args().len() as u32).into(), scope_index.into());
}
}
CallKind::Call if contains_spread => compiler.bytecode.emit_call_spread(),
CallKind::Call if contains_spread => {
let ic_index = compiler.push_call_spread_ic();
compiler.bytecode.emit_call_spread(ic_index.into());
}
CallKind::Call => {
compiler
.bytecode
Expand Down Expand Up @@ -2731,6 +2755,8 @@ impl<'ctx> ByteCompiler<'ctx> {
handlers: self.handlers,
flags: Cell::new(self.code_block_flags),
ic: self.ic.into_boxed_slice(),
call_spread_ic: self.call_spread_ic.into_boxed_slice(),
async_call_ic: self.async_call_ic.into_boxed_slice(),
source_info: SourceInfo::new(
SourceMap::new(source_map_entries, self.source_path),
self.function_name,
Expand Down
10 changes: 9 additions & 1 deletion core/engine/src/object/jsobject.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,13 +128,21 @@ impl JsObject {
vtable: &'static InternalObjectMethods,
) -> Self {
let inner = Gc::new(VTableObject {
object: GcRefCell::new(object),
vtable,
object: GcRefCell::new(object),
});

JsObject { inner }.upcast()
}

/// Returns the address of the inner `Gc` pointer.
#[inline]
#[must_use]
pub fn addr(&self) -> usize {
let ptr: *const _ = &raw const *self.inner;
ptr as usize
}

/// Creates a new ordinary object with its prototype set to the `Object` prototype.
///
/// This is equivalent to calling the specification's abstract operation
Expand Down
22 changes: 14 additions & 8 deletions core/engine/src/vm/code_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ use std::{cell::Cell, fmt::Display, fmt::Write as _};
use thin_vec::ThinVec;

use super::{
InlineCache,
opcode::{Address, Bytecode, Instruction, InstructionIterator},
source_info::{SourceInfo, SourceMap, SourcePath},
};
use crate::vm::{AsyncCallCache, CallSpreadCache, InlineCache};

bitflags! {
/// Flags for [`CodeBlock`].
Expand Down Expand Up @@ -159,6 +159,10 @@ pub struct CodeBlock {
/// inline caching
pub(crate) ic: Box<[InlineCache]>,

pub(crate) call_spread_ic: Box<[CallSpreadCache]>,

pub(crate) async_call_ic: Box<[AsyncCallCache]>,

/// Bytecode to source code mapping.
pub(crate) source_info: SourceInfo,

Expand Down Expand Up @@ -193,6 +197,8 @@ impl CodeBlock {
parameter_length: 0,
handlers: ThinVec::default(),
ic: Box::default(),
call_spread_ic: Box::default(),
async_call_ic: Box::default(),
source_info: SourceInfo::new(
SourceMap::new(Box::default(), SourcePath::None),
name,
Expand Down Expand Up @@ -509,6 +515,7 @@ impl CodeBlock {
format!("scope_index:{scope_index}")
}
Instruction::Call { argument_count }
| Instruction::CallArrow { argument_count }
| Instruction::New { argument_count }
| Instruction::SuperCall { argument_count } => {
format!("argument_count:{argument_count}")
Expand Down Expand Up @@ -636,9 +643,6 @@ impl CodeBlock {
} => {
format!("object:{object}, proto:{proto}, value:{value}, name_index:{name_index}")
}
Instruction::ThrowMutateImmutable { index } => {
format!("index:{index}")
}
Instruction::DeletePropertyByName { object, name_index }
| Instruction::GetMethod { object, name_index } => {
format!("object:{object}, name_index:{name_index}")
Expand Down Expand Up @@ -864,17 +868,20 @@ impl CodeBlock {
| Instruction::CheckReturn
| Instruction::Return
| Instruction::AsyncGeneratorClose
| Instruction::CreatePromiseCapability
| Instruction::PopEnvironment
| Instruction::IncrementLoopIteration
| Instruction::IteratorNext
| Instruction::SuperCallDerived
| Instruction::CallSpread
| Instruction::NewSpread
| Instruction::SuperCallSpread
| Instruction::PopPrivateEnvironment
| Instruction::Generator
| Instruction::AsyncGenerator => String::new(),
Instruction::ThrowMutateImmutable { index }
| Instruction::CallSpread { index }
| Instruction::CreatePromiseCapability { index } => {
format!("index:{index}")
}
Instruction::Reserved1
| Instruction::Reserved2
| Instruction::Reserved3
Expand Down Expand Up @@ -933,8 +940,7 @@ impl CodeBlock {
| Instruction::Reserved56
| Instruction::Reserved57
| Instruction::Reserved58
| Instruction::Reserved59
| Instruction::Reserved60 => unreachable!("Reserved opcodes are unreachable"),
| Instruction::Reserved59 => unreachable!("Reserved opcodes are unreachable"),
}
}
}
Expand Down
8 changes: 4 additions & 4 deletions core/engine/src/vm/flowgraph/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ impl CodeBlock {
}
Instruction::CallEval { .. }
| Instruction::Call { .. }
| Instruction::CallArrow { .. }
| Instruction::New { .. }
| Instruction::SuperCall { .. }
| Instruction::ConcatToString { .. }
Expand Down Expand Up @@ -345,14 +346,14 @@ impl CodeBlock {
| Instruction::GeneratorYield { .. }
| Instruction::AsyncGeneratorYield { .. }
| Instruction::AsyncGeneratorClose
| Instruction::CreatePromiseCapability
| Instruction::CreatePromiseCapability { .. }
| Instruction::PushClassField { .. }
| Instruction::SuperCallDerived
| Instruction::Await { .. }
| Instruction::NewTarget { .. }
| Instruction::ImportMeta { .. }
| Instruction::CallEvalSpread { .. }
| Instruction::CallSpread
| Instruction::CallSpread { .. }
| Instruction::NewSpread
| Instruction::SuperCallSpread
| Instruction::SetPrototype { .. }
Expand Down Expand Up @@ -432,8 +433,7 @@ impl CodeBlock {
| Instruction::Reserved56
| Instruction::Reserved57
| Instruction::Reserved58
| Instruction::Reserved59
| Instruction::Reserved60 => unreachable!("Reserved opcodes are unreachable"),
| Instruction::Reserved59 => unreachable!("Reserved opcodes are unreachable"),
}
}

Expand Down
35 changes: 34 additions & 1 deletion core/engine/src/vm/inline_cache/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use boa_gc::GcRefCell;
use boa_macros::{Finalize, Trace};

use crate::{
JsString,
JsString, JsValue,
object::shape::{Shape, WeakShape, slot::Slot},
};

Expand Down Expand Up @@ -112,3 +112,36 @@ impl InlineCache {
result
}
}

/// A cached entry for a spread call.
#[derive(Clone, Debug, Trace, Finalize)]
pub(crate) struct CallSpreadCache {
/// The cached arguments.
pub(crate) arguments: GcRefCell<Option<(usize, Vec<JsValue>)>>,
}

impl CallSpreadCache {
/// Creates a new `CallSpreadCache`.
pub(crate) fn new() -> Self {
Self {
arguments: GcRefCell::new(None),
}
}
}

/// A cached entry for an async call.
#[derive(Clone, Debug, Trace, Finalize)]
pub(crate) struct AsyncCallCache {
#[allow(dead_code)]
#[unsafe_ignore_trace]
pub(crate) placeholder: Cell<bool>,
}

impl AsyncCallCache {
/// Creates a new `AsyncCallCache`.
pub(crate) fn new() -> Self {
Self {
placeholder: Cell::new(false),
}
}
}
Loading
Loading