Open Bug 1990220 Opened 11 months ago Updated 11 months ago

Generate better code for object literals in Ion

Categories

(Core :: JavaScript Engine: JIT, task, P3)

task

Tracking

()

People

(Reporter: jandem, Unassigned)

References

(Blocks 1 open bug)

Details

The esprima-next-wtb test in JetStream 3 has a scanIdentifier function that returns an object:

    return {
      type,
      value: id,
      lineNumber: this.lineNumber,
      lineStart: this.lineStart,
      start,
      end: this.index,
      escaped
    };

In Ion we initialize the property values with MStoreFixedSlot. For the properties after the first two we have a pre-barrier that isn't necessary (the first two are optimized by ShouldInitFixedSlots).

The test case below has the same issue. For the a: 1 property we emit just:

movabsq    $0xfff8800000000001, %r11
movq       %r11, 0x18(%rax)

But then for the later properties we get:

movabsq    $0x7d660e058510, %r11
testl      $0x1, 0x0(%r11)
je         .Lfrom455
movq       0x38(%rax), %r11
shrq       $47, %r11
cmpl       $0x1fff6, %r11d
jb         .Lfrom476
push       %rdx
leaq       0x38(%rax), %rdx
call       .Lfrom486
pop        %rdx
movabsq    $0xfff8800000000005, %r11
movq       %r11, 0x38(%rax)

We should be able to get rid of the pre-barrier for each store. Ideally we'd also be able to optimize LNewPlainObject to not initialize all slots to undefined in this case, but that's a bit more tricky because if we perform a bailout between allocation and initialization, we need to make sure these slots get initialized. Maybe we could reorder instructions to avoid that.

function getObject(obj) {
  return {a: 1, b: obj.x|0, c: 3, d: 4, e: 5};
}
function f() {
  with ({});
  var obj = {x: 1};
  for (var i = 0; i < 10_000_000; i++) {
    getObject(obj);
  }
}
f();

The bytecode for this uses JSOp::InitProp with an IC that we transpile.

One option is to add a new bytecode op that has the property's slot number and doesn't use an IC. The bytecode emitter should have enough information for this. In WarpBuilder we can then mark the store as not needing a pre-barrier.

Another option: maybe we could use our scalar replacement code somehow to allocate and initialize the object in one go.

Severity: -- → N/A
Priority: -- → P3

Jan and I spent some time working through how using scalar replacement here might look. A quick sketch: right now, if an allocation escapes, we don't do any scalar replacement. If we instead track a counter of how often an object escapes, and allow ourselves to scalar-replace objects that only escape a single time, then we can sink the allocation to the point immediately before the lone escape, and initialize the contents of the object at that point. This would allow us to initialize each slot to its final value, instead of storing undefined first, and avoid pre-barriers. In code where an object only escapes on a single cold path, this would also let us completely eliminate the allocation most of the time.

If we could support scalar replacement of AddAndStoreSlot (see bug 1700422), at least in cases where the shape of the object never diverges, then this would maybe let us clear up some cruft in constructors.

You need to log in before you can comment on or make changes to this bug.