1

As far as I know, V8 Javascript engine makes double number conversion (to i32 and back) to perform bit operation. Let's consider next example:

const int32 = new Int32Array(2);
int32[0] = 42;
int32[1] = 2;
const y = int32[0] | int32[1];

Does V8 makes double conversion in this case ? If no, does it mean bit operations are faster on Int32Array values?

UPDATE: By double conversion I mean: from 64 bit (53 bits precision) to -> 32bit and again to -> 64 bit

  • 1
    Double conversion means i32 -> double -> i32, right? – D. Pardal Apr 30 '20 at 08:12
  • @D.Pardal sorry, I was not clear enough. It is mean: from 64 bit (53 bits precision) to -> 32bit and again to -> 64 bit – captain-yossarian from Ukraine Apr 30 '20 at 08:40
  • 1
    @SerhiiBilyk No, "*double -> i32 -> double*" makes no sense. The value is stored as a i32, and the bitwise operations happens on an i32. It might get converted to a double *in between* (as mandated by the spec), or the conversion is optimised away. – Bergi Apr 30 '20 at 12:06

1 Answers1

4

V8 developer here. The short answer is: there are no conversions here, bitwise operations are always performed on 32-bit integers, and an Int32Array also stores its elements as 32-bit integers.

The longer answer is "it depends". In unoptimized code, all values use a unified representation. In V8, that means number values are either represented as a "Smi" ("small integer") if they are in range (31 bits including sign, i.e. about -1 billion to +1 billion), or as "HeapNumbers" (64-bit double with a small object header) otherwise. So for an element load like int32[0], the 32-bit value is loaded from the array, inspected for range, and then either tagged as a Smi or boxed as a HeapNumber. The following | operation looks at the input, converts it to a 32-bit integer (which could be as simple as untagging a Smi, or as complex as invoking .valueOf on an object), and performs the bitwise "or". The result is then again either tagged as a Smi or boxed as a HeapNumber, so that the next operation can decide what to do with it.

If the function runs often enough to eventually get optimized, then the optimizing compiler makes use of type feedback (guarded by type checks where necessary) to elide all these conversions that are unnecessary in this case, and the simple answer will be true.

jmrk
  • 34,271
  • 7
  • 59
  • 74
  • Are the HeapNumber and the SMI considered to be the same by the type feedback, so that the range check is optimised away? – Bergi Apr 30 '20 at 12:11
  • 1
    @Bergi: the type feedback system tracks Smis separately, so that the optimizing compiler can decide whether it makes sense to emit HeapNumber-handling code or not. – jmrk Apr 30 '20 at 14:23
  • So will the optimising compiler emit code that avoids HeapNumbers and double conversions even for `1 <<< 30` (i.e. any `int32` or `uint32` outside the SMI range)? – Bergi Apr 30 '20 at 14:35
  • In most cases that's what I would expect. If it's literally `1 << 30` then it should even constant-fold that. That said, for variable inputs the tradeoff is smaller code vs. the risk of having to deopt later; so whether or not a HeapNumber path is emitted in any given situation is totally something that the team might iterate on over time, so I'd rather not make any absolute statements about that. – jmrk Apr 30 '20 at 15:19
  • 1
    My experience has been that the optimizing compiler will issue a 'not a smi' depot. – Ryder Brooks Mar 17 '21 at 16:36