Skip to content

Commit 3bf3229

Browse files
committed
Accelerate primitive string charCodeAt calls
1 parent 07970f8 commit 3bf3229

3 files changed

Lines changed: 99 additions & 0 deletions

File tree

crates/qjs-runtime/src/bytecode/vm_call.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,9 @@ pub(super) fn try_fast_global_native_call(
455455
let mut env = realm_env.clone();
456456
crate::string::native_string_prototype_char_at(this_value.clone(), arguments, &mut env)
457457
}
458+
NativeFunction::StringPrototypeCharCodeAt => {
459+
Ok(fast_primitive_string_char_code_at(this_value, arguments)?)
460+
}
458461
NativeFunction::StringPrototypeConcat => {
459462
let Value::String(_) = this_value else {
460463
return None;
@@ -532,6 +535,24 @@ fn fast_primitive_math_pow(arguments: &[Value]) -> Option<f64> {
532535
Some(crate::operations::number_exponentiate(base, exponent))
533536
}
534537

538+
fn fast_primitive_string_char_code_at(this_value: &Value, arguments: &[Value]) -> Option<Value> {
539+
let Value::String(value) = this_value else {
540+
return None;
541+
};
542+
let number = match arguments.first() {
543+
Some(Value::Number(number)) => *number,
544+
None | Some(Value::Undefined) => 0.0,
545+
_ => return None,
546+
};
547+
let position = if number.is_nan() { 0.0 } else { number.trunc() };
548+
let code_unit = if position < 0.0 || !position.is_finite() {
549+
None
550+
} else {
551+
crate::string::string_code_unit_at(value, position as usize)
552+
};
553+
Some(Value::Number(code_unit.map_or(f64::NAN, f64::from)))
554+
}
555+
535556
fn fast_string_sequence_native(
536557
native: NativeFunction,
537558
this_value: &Value,

crates/qjs-runtime/src/tests/strings/code_units.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,18 @@ fn evaluates_string_code_unit_builtins() {
6060
);
6161
assert_eq!(eval("'😀'.charCodeAt(0);"), Ok(Value::Number(55_357.0)));
6262
assert_eq!(eval("'😀'.charCodeAt(1);"), Ok(Value::Number(56_832.0)));
63+
assert_eq!(
64+
eval(
65+
"let conversions = 0; let index = { valueOf() { conversions += 1; return 1; } }; 'abc'.charCodeAt(index) === 98 && conversions === 1;"
66+
),
67+
Ok(Value::Boolean(true))
68+
);
69+
assert_eq!(
70+
eval(
71+
"'abc'.charCodeAt(NaN) === 97 && 'abc'.charCodeAt(1.9, 99) === 98 && Number.isNaN('abc'.charCodeAt(Infinity));"
72+
),
73+
Ok(Value::Boolean(true))
74+
);
6375
assert_eq!(
6476
eval("'\\uD800\\uDC00'.codePointAt(0);"),
6577
Ok(Value::Number(65_536.0))

tasks/T018-broad-performance.md

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2209,6 +2209,72 @@ and `0cbf9018be8b65466ccbff2b4903111ec5bcdae4ba4476412ee743d2ef2c52f0`.
22092209
This is a general native-dispatch improvement with external evidence, not a
22102210
benchmark-specific shortcut; B4 and B5 remain active.
22112211

2212+
The exact-SHA hosted workflows for
2213+
`07970f8d394c33323f36a8d864efb9f1c0a4cf68` all completed successfully. The
2214+
hosted broad preview retained 25/25 cases and measured 1.0062x base and 0.3583x
2215+
QuickJS-NG overall. Its isolated `array_allocation` 1.1462x base movement was
2216+
not reproduced by the local independent rerun recorded above. The hosted
2217+
external preview remained far from B5 at 12.437x QuickJS-NG for 5/5 JetStream,
2218+
7.613x for 7/14 Kraken, and 12.803x for 23/26 SunSpider; `math-partial-sums`
2219+
improved from the preceding hosted artifact's 25.290x QuickJS-NG to 15.271x,
2220+
but still lost decisively. Exact Test262 coverage remained 42,671 pass, one
2221+
fail, and zero timeout. Hosted broad raw/report SHA-256 are
2222+
`65d14956ed6a3f458f9c79c545398ec41800982bcd7358250998959bff558c47`
2223+
and `61bdc2ddb8d9beaf82af6ba945f153c660e389a0e682c29b4e5d670860ea4850`;
2224+
hosted external raw/report SHA-256 are
2225+
`0df30133b3ae726c0298b456e8b2bdd49ff2af1e6e9c0bdf68eb8c9d947cd4da`
2226+
and `b874194fadee20681ce9b71f835e7bce216a8020e468759c71c7811d24c3f98f`.
2227+
2228+
The forty-first v2 unit follows the external SunSpider `string-base64`
2229+
profile. Its encoding and decoding loops repeatedly call
2230+
`String.prototype.charCodeAt` with an already primitive string receiver and
2231+
numeric index, but the general native-call path still constructed and applied
2232+
the caller environment for each code-unit read. The fast native dispatcher
2233+
now reads the code unit directly only for primitive string receivers and
2234+
number/undefined indices. Object receivers, object or string indices, and all
2235+
observable conversions remain on the complete path. Tests cover numeric
2236+
truncation, NaN, infinity, ignored extra arguments, UTF-16 code units, object
2237+
index conversion, and conversion side effects. The implementation has no
2238+
benchmark name, path, iteration count, checksum, or workload-specific table.
2239+
2240+
Against clean base binary SHA-256
2241+
`40c55d677023f3829b964df0e75db88f9651d1b2dc064f5a3d7c49ddefaea478`,
2242+
candidate binary SHA-256
2243+
`4d6b4e3bf0626695baf98cbd69ebe8b4700045c63aa2f3a332558aea51186a67`
2244+
improved eleven-block interleaved external source medians across four distinct
2245+
workloads: `string-base64` was 0.495773x base, `crypto-md5` 0.782206x,
2246+
`crypto-sha1` 0.813148x, and `crypto-aes` 0.889119x. Five-block interleaved
2247+
Kraken bundles also measured `audio-dft` at 0.970906x and
2248+
`stanford-crypto-ccm` at 0.974229x, rejecting apparent regressions in the
2249+
unpaired full previews.
2250+
2251+
Complete one-block candidate and same-thermal-window base rerun previews kept
2252+
identical coverage at 5/5 JetStream, 10/14 Kraken, and 23/26 SunSpider. Their
2253+
common-case suite candidate/base diagnostics were 0.982537x, 0.991921x, and
2254+
0.985346x. The intended SunSpider cases reproduced at 0.495484x for
2255+
`string-base64`, 0.779431x for `crypto-md5`, 0.811919x for `crypto-sha1`, and
2256+
0.908398x for `crypto-aes`. Eleven-block interleaved reruns of the eight worst
2257+
unrelated SunSpider movements placed every one between 0.993864x and 1.016738x
2258+
base. Candidate/QuickJS-NG suite diagnostics improved but still failed B5 at
2259+
9.459241x, 5.548499x, and 6.903639x. Candidate external raw/report SHA-256 are
2260+
`a46d34497bda45152c37fe0f1a6cf97ddc0d094cf4e4aab139fca4e8d36212e6`
2261+
and `64c5d91ed608fbcc7fd8acc2e6318b074c62bb4c8c80cc29be7ec5df5ae4d240`;
2262+
base-rerun raw/report SHA-256 are
2263+
`a767097e73ad5dae244ee46c2cb01a3787bf386753d45b874d45a0e2dbec631d`
2264+
and `d18feb94f8162c674fa71b6ebca4886887b8ddf435bd8f2ef5c497db9a73105f`.
2265+
2266+
The complete local three-role broad diagnostic retained all 25 cases, 225
2267+
eligible exact-checksum measurements, and 600 passing linearity samples.
2268+
Candidate/base was 0.997764x overall; family ratios ranged from 0.988520x for
2269+
string to 1.002026x for property, and the worst individual case was
2270+
`array_allocation` at 1.009524x. Candidate/QuickJS-NG was 0.194364x overall,
2271+
but allocation still failed B4 at 1.135321x. The strict analyzer correctly
2272+
rejects the receipt-less development binaries, so this remains regression
2273+
evidence rather than a fixed-hardware claim. Broad raw SHA-256:
2274+
`2f657b75ebab15ad3e10ee286c8eb80e855c2862ac30ca5ca9ba4d0f30f46ed3`.
2275+
This unit generalizes across external string and crypto workloads, while B4
2276+
and B5 remain active.
2277+
22122278
## Historical Broad V1 Baseline
22132279

22142280
The first complete baseline was recorded on 2026-07-15 at commit

0 commit comments

Comments
 (0)