Closed Bug 1112167 Opened 9 years ago Closed 6 years ago

Use Value Types instead of Objects for SIMD

Categories

(Core :: JavaScript Engine: JIT, defect, P5)

defect

Tracking

()

RESOLVED WONTFIX

People

(Reporter: nbp, Unassigned)

References

Details

Currently SIMD constructors are building objects which can be identified.  As
they can be identified, the operator == and the operator === are capable of
identifying objects allocations.

js> var a = SIMD.int32x4(1,2,3,4)
js> var b = SIMD.int32x4(1,2,3,4)
js> a === a
true
js> a === b
false

This property has terrible consequences on what we can do as IonMonkey
optimization, as this means that we can identify if a loop was taken or not:

function f(v, n) {
  for (var i = 0; i < n; i++) {
    v = ...;
  }
  return v;
}

var x = SIMD.int32x4(1,2,3,4);
assertEq(f(x, 0), x);

Being capable of doing so implies that IonMonkey has to keep Phi node for the
allocations of objects which are used to hold the content of SIMD registers.

Doing so implies that join-block might have a Phi node which has SIMD objects
as operands.  Phi nodes cannot be recovered yet, and would be the alternative
if this issue cannot be solved.

On the other hand, using value types for SIMD implies that we cannot
distinguish between 2 SIMD constructors.  Thus, IonMonkey should be able to
replace any Phi node which is explicitly manipulating SIMD operands by a new
SIMD constructor which would be recovered on bailout.
Flags: needinfo?(sunfish)
Flags: needinfo?(luke)
We definitely want value types, to the extent possible.

The current SIMD polyfill does happen to construct objects that have unique identities, however I believe this is only out of a present lack of better options. However in theory, regular SIMD code shouldn't be doing == or similar on SIMD objects anyway; we have explicit comparison functions like SIMD.float32x4.lessThan and signMask/allTrue for that purpose.

I believe the plan is that the eventual SIMD.js spec will be written to use value semantics, and the polyfill will just be non-conforming in this regard. We'll use value types if they're available in the spec by that time, or we'll anticipate value types if they're not.
Flags: needinfo?(sunfish)
Agreed that pure value semantics is the intended semantics.  From irc, I can see two rough implementation schemes:
 1. add exceptions into equality tests for values
 2. canonicalize SIMD objects when constructing (using a value->object map)

Both seem to have pros/cons.  For 1, we have to gross up equality which could hurt performance and would require finding and extending every place where we test equality.  For 2, the canonicalization will be slow, probably preventing allocation from within JIT code, though the whole point of SIMD is you don't ever actually create objects once we reach Ion (if you are, you are doing something wrong).  Also, we have to think carefully about cross-compartment cases, although on first consideration, it seems like we could do something similar to what we do with symbols (allocate in the runtime and never wrap).

There is also the question of whether these SIMD values are actually "objects" (i.e., typeof SIMD.int32x4(0,0,0,0) == "object").  This has general semantic ramifications (symbol is also an example here as it is not an object).  Niko: is there any consensus on this in TC39?  I can remember multiple different opinions on this over time.
Flags: needinfo?(luke) → needinfo?(nmatsakis)
There is consensus that value types are not objects. They rather follow the precedent set by string / symbol / etc. I believe this summary is still roughly accurate: https://github.com/nikomatsakis/typed-objects-explainer/blob/master/valuetypes.md
Flags: needinfo?(nmatsakis)
Both implementation schemes given in comment 2 are hard to get totally right, because we'd also have to gross up Value::isObject(), too. Really it forks into two questions: Value::isECMAObject(), which is slow, and Value::isImplementedAsObject(). The standard library will want isECMAObject() throughout. For example, Object(simdval) should detect that simdval is not an object.

It'll be tricky getting SIMDObject's ObjectOps right, too.

So here's a third scheme. Rename SIMDObject -> SIMDValue. Make a common base class between JS::Symbol and SIMDValue. The common operations between those two (possible pure virtual methods in the base class) are things like:

-   type-testing and downcasting, a la JSObject::is<T>() / JSObject::as<T>();
-   support for testing equality (===, ==, SameValue);
-   `typeof`;
-   GC tracing support;
-   ToNumber, ToString, etc.

Then change JSVAL_TAG_SYMBOL to JSVAL_TAG_VALUETYPE or DATAVALUE or something.

In Ion, treat Symbol and SIMD values as different types.
In JS::Value, both are a pointer to the base class, tagged with JSVAL_TAG_VALUETYPE.
In the interpreter and GC, handling them means calling a virtual method.

Performance: This costs a word per Symbol (for the vptr). I think the SIMD types have a type pointer anyway, so no big cost there. And it could slow down access to symbol-keyed properties, in the interpreter only, by slowing down ValueToId. But I'm not sure about that.

This is more initial work, but with a lower total cost of compliance, and I think a lower complexity tax on the whole rest of the codebase. And it should put us in a better position to implement 64-bit integers and Niko's ValueType() proposal down the road.
Priority: -- → P5
SIMD.js is being removed (bug 1416723).
Status: NEW → RESOLVED
Closed: 6 years ago
Resolution: --- → WONTFIX
You need to log in before you can comment on or make changes to this bug.