[dart2js] lazyFinal can use a variable for the result.

Returning the value is faster than reading the holder slot on V8.
Some benchmarks which read a lazy final in a tight loop are up to
40% faster.
JSC and SM unchanged.

Change-Id: I72afa456160469e4ccfe6ab80f8a53694133d794
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/220132
Commit-Queue: Stephen Adams <sra@google.com>
Reviewed-by: Mayank Patke <fishythefish@google.com>
This commit is contained in:
Stephen Adams 2021-11-15 22:25:31 +00:00 committed by commit-bot@chromium.org
parent 7bc9ca5ee9
commit 405734f18b

View file

@ -189,11 +189,12 @@ function lazy(holder, name, getterName, initializer) {
}; };
} }
// Creates a lazy final field that uses non-nullable initialization semantics. // Creates a lazy final static field that uses non-nullable initialization
// semantics.
// //
// A lazy field has a storage entry, [name], which holds the value, and a // A lazy final field has a storage entry, [name], which holds the value, and a
// getter ([getterName]) to access the field. If the field wasn't set before // getter ([getterName]) to access the field. The field is initialized on first
// the first access, it is initialized with the [initializer]. // access with the [initializer].
function lazyFinal(holder, name, getterName, initializer) { function lazyFinal(holder, name, getterName, initializer) {
var uninitializedSentinel = holder; var uninitializedSentinel = holder;
holder[name] = uninitializedSentinel; holder[name] = uninitializedSentinel;
@ -201,12 +202,21 @@ function lazyFinal(holder, name, getterName, initializer) {
if (holder[name] === uninitializedSentinel) { if (holder[name] === uninitializedSentinel) {
var value = initializer(); var value = initializer();
if (holder[name] !== uninitializedSentinel) { if (holder[name] !== uninitializedSentinel) {
// Since there is no setter, the only way to get here is via bounded
// recursion, where `initializer` calls the lazy final getter.
#throwLateFieldADI(name); #throwLateFieldADI(name);
} }
holder[name] = value; holder[name] = value;
} }
holder[getterName] = function() { return this[name]; }; // TODO(sra): Does the value need to be stored in the holder at all?
return holder[name]; // Potentially a call to the getter could be replaced with an access to
// holder slot if dominated by a previous call to the getter. Does the
// optimizer do this? If so, within a single function, the dominance
// relations should allow a local copy via GVN, so it is not that valuable
// an optimization for a `final` static variable.
var finalValue = holder[name];
holder[getterName] = function() { return finalValue; };
return finalValue;
}; };
} }