Skip to content

Fast paths for float div/mod, bitwise ops on float64, and field lookup on string primitives - #600

Open
Baltsat wants to merge 4 commits into
bellard:masterfrom
Baltsat:fast-paths
Open

Baltsat wants to merge 4 commits into
bellard:masterfrom
Baltsat:fast-paths

Conversation

@Baltsat

@Baltsat Baltsat commented Nov 5, 2026 •

Copy link
Copy Markdown

four small fast paths, one commit each, so any of them can be taken alone.

  1. OP_div / OP_mod. OP_div inlines only int/int, so any float operand goes through binary_arith_slow. now number/number is a plain double division, same result as the slow path. OP_mod also bailed out for every negative dividend; it now handles it when the remainder is non-zero.
  2. bitwise and shifts on float64. & | ^ << >> >>> drop to js_binary_logic_slow / js_shr_slow as soon as an operand is not int32, for example after >>> 0, then run ToNumeric and ToInt32 twice. for int or float64 operands the conversion has no side effects, so it is done directly.
  3. GET_FIELD_INLINE on string primitives and array methods. .length on a string primitive is returned directly, and other names walk String.prototype with the existing inline loop. method lookups on arrays (push, pop, ...) now go through the inline path instead of the slow one, which is the XXX next to it.
  4. JS_DefinePropertyValue for new properties and array appends. a new data property on an ordinary extensible object is added directly instead of going through JS_DefineProperty(). JS_DefinePropertyValueInt64 / JS_CreateDataPropertyUint32 append at the end of a fast array without turning the index into an atom. this is the path of object literals, JSON.parse and map/filter/slice/concat/Array.from/split. the checks are the same as in JS_CreateProperty (extensible, no getter/setter, C_W_E, read-only length still throws).

callgrind on master 712e8c0, gcc, aarch64, each commit measured alone on small scripts that stress its path:

                                   master Ir    patched Ir
divmod  (i*1.5)/3 + (-i % 7)       863.5M   ->  487.2M    1.77x
bitwise hash with >>> 0 and ^      1233.1M  ->  830.3M    1.49x
string .length in a loop           438.6M   ->  396.6M    1.11x
array push/pop                     1534.7M  ->  1421.7M   1.08x
string methods (indexOf, slice)    392.5M   ->  368.9M    1.06x
JSON.parse + stringify             1000.4M  ->  947.7M    1.06x   (commit 4, on top of 1-3)
array methods (map, filter, ...)   853.1M   ->  591.3M    1.44x   (commit 4)
object literals                    1280.8M  ->  1072.4M   1.19x   (commit 4)

no slowdown on the rest i checked (plain calls, arguments, global writes, Map, JSON, typed arrays): every commit stays within 0.3% of master there.

make test passes, also with CONFIG_ASAN=y. output is identical to master on 625 pairs of edge values (0, -0, ±1, ±0.5, 2^31, 2^32, ±2^53, ±1e300, NaN, ±Infinity, 5e-324, ...) for 12 operators, plus string and array cases, and on object literals, class fields, frozen/sealed/proxy targets, read-only array length and array-method species cases for commit 4.

Copilot AI balanced review requested due to automatic review settings November 5, 2026 09:29

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota. Try requesting a review at a lower effort level.

…and appends to fast arrays

JS_DefinePropertyValue() adds a data property of an ordinary extensible
object directly instead of going through JS_DefineProperty(). The
Int64/Uint32 variants append at the end of a fast array without
converting the index to an atom.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants