Refactor OneShotInterceptorData to prepare for modular codegen

Change-Id: I52d47c28978c0d935d07b438520728a304d311d6
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/102365
Reviewed-by: Sigmund Cherem <sigmund@google.com>
This commit is contained in:
Johnni Winther 2019-05-20 08:57:39 +00:00 committed by commit-bot@chromium.org
parent c0b967d7da
commit a9827a0c05
7 changed files with 142 additions and 66 deletions

View file

@ -663,7 +663,9 @@ class JavaScriptBackend {
CodegenInputs onCodegenStart(JClosedWorld closedWorld) {
RuntimeTypeTags rtiTags = const RuntimeTypeTags();
OneShotInterceptorData oneShotInterceptorData = new OneShotInterceptorData(
closedWorld.interceptorData, closedWorld.commonElements);
closedWorld.interceptorData,
closedWorld.commonElements,
closedWorld.nativeData);
Tracer tracer = new Tracer(closedWorld, compiler.outputProvider);
_rtiEncoder = new RuntimeTypesEncoderImpl(
rtiTags,

View file

@ -14,7 +14,7 @@ import '../js/js.dart' as jsAst;
import '../serialization/serialization.dart';
import '../universe/selector.dart';
import '../world.dart' show JClosedWorld;
import 'namer.dart';
import 'namer.dart' show ModularNamer, suffixForGetInterceptor;
import 'native_data.dart';
abstract class InterceptorData {
@ -354,55 +354,79 @@ class InterceptorDataBuilderImpl implements InterceptorDataBuilder {
class OneShotInterceptorData {
final InterceptorData _interceptorData;
final CommonElements _commonElements;
final NativeData _nativeData;
OneShotInterceptorData(this._interceptorData, this._commonElements);
OneShotInterceptorData(
this._interceptorData, this._commonElements, this._nativeData);
/// A collection of selectors that must have a one shot interceptor generated.
final Map<jsAst.Name, Selector> _oneShotInterceptors =
<jsAst.Name, Selector>{};
Iterable<OneShotInterceptor> get oneShotInterceptors {
List<OneShotInterceptor> interceptors = [];
for (var map in _oneShotInterceptors.values) {
interceptors.addAll(map.values);
}
return interceptors;
}
Selector getOneShotInterceptorSelector(jsAst.Name name) =>
_oneShotInterceptors[name];
Map<Selector, Map<String, OneShotInterceptor>> _oneShotInterceptors = {};
Iterable<jsAst.Name> get oneShotInterceptorNames =>
_oneShotInterceptors.keys.toList()..sort();
/// A map of specialized versions of the [getInterceptorMethod].
/// A set of specialized versions of the [getInterceptorMethod].
///
/// Since [getInterceptorMethod] is a hot method at runtime, we're always
/// specializing it based on the incoming type. The keys in the map are the
/// names of these specialized versions. Note that the generic version that
/// contains all possible type checks is also stored in this map.
final Map<jsAst.Name, Set<ClassEntity>> _specializedGetInterceptors =
<jsAst.Name, Set<ClassEntity>>{};
Iterable<SpecializedGetInterceptor> get specializedGetInterceptors =>
_specializedGetInterceptors.values;
Iterable<jsAst.Name> get specializedGetInterceptorNames =>
_specializedGetInterceptors.keys.toList()..sort();
Set<ClassEntity> getSpecializedGetInterceptorsFor(jsAst.Name name) =>
_specializedGetInterceptors[name];
Map<String, SpecializedGetInterceptor> _specializedGetInterceptors = {};
jsAst.Name registerOneShotInterceptor(
Selector selector, Namer namer, JClosedWorld closedWorld) {
Selector selector, ModularNamer namer, JClosedWorld closedWorld) {
selector = selector.toNormalized();
Set<ClassEntity> classes =
_interceptorData.getInterceptedClassesOn(selector.name, closedWorld);
jsAst.Name name = namer.nameForGetOneShotInterceptor(selector, classes);
if (!_oneShotInterceptors.containsKey(name)) {
registerSpecializedGetInterceptor(classes, namer);
_oneShotInterceptors[name] = selector;
}
return name;
String key = suffixForGetInterceptor(_commonElements, _nativeData, classes);
Map<String, OneShotInterceptor> interceptors =
_oneShotInterceptors[selector] ??= {};
OneShotInterceptor interceptor = interceptors.putIfAbsent(
key, () => new OneShotInterceptor(key, selector));
interceptor.classes.addAll(classes);
registerSpecializedGetInterceptor(classes, namer);
return namer.nameForGetOneShotInterceptor(selector, classes);
}
void registerSpecializedGetInterceptor(
Set<ClassEntity> classes, Namer namer) {
jsAst.Name name = namer.nameForGetInterceptor(classes);
Set<ClassEntity> classes, ModularNamer namer) {
if (classes.contains(_commonElements.jsInterceptorClass)) {
// We can't use a specialized [getInterceptorMethod], so we make
// sure we emit the one with all checks.
_specializedGetInterceptors[name] = _interceptorData.interceptedClasses;
} else {
_specializedGetInterceptors[name] = classes;
classes = _interceptorData.interceptedClasses;
}
String key = suffixForGetInterceptor(_commonElements, _nativeData, classes);
SpecializedGetInterceptor interceptor =
_specializedGetInterceptors[key] ??= new SpecializedGetInterceptor(key);
interceptor.classes.addAll(classes);
}
}
class OneShotInterceptor {
final Selector selector;
final String key;
final Set<ClassEntity> classes = {};
OneShotInterceptor(this.key, this.selector);
@override
String toString() =>
'OneShotInterceptor(selector=$selector,key=$key,classes=$classes)';
}
class SpecializedGetInterceptor {
final String key;
final Set<ClassEntity> classes = {};
SpecializedGetInterceptor(this.key);
@override
String toString() => 'SpecializedGetInterceptor(key=$key,classes=$classes)';
}

View file

@ -31,7 +31,6 @@ class InterceptorStubGenerator {
final Emitter _emitter;
final NativeCodegenEnqueuer _nativeCodegenEnqueuer;
final Namer _namer;
final OneShotInterceptorData _oneShotInterceptorData;
final CustomElementsCodegenAnalysis _customElementsCodegenAnalysis;
final CodegenWorld _codegenWorld;
final JClosedWorld _closedWorld;
@ -42,7 +41,6 @@ class InterceptorStubGenerator {
this._emitter,
this._nativeCodegenEnqueuer,
this._namer,
this._oneShotInterceptorData,
this._customElementsCodegenAnalysis,
this._codegenWorld,
this._closedWorld);
@ -51,7 +49,10 @@ class InterceptorStubGenerator {
InterceptorData get _interceptorData => _closedWorld.interceptorData;
jsAst.Expression generateGetInterceptorMethod(Set<ClassEntity> classes) {
jsAst.Expression generateGetInterceptorMethod(
SpecializedGetInterceptor interceptor) {
Set<ClassEntity> classes = interceptor.classes;
jsAst.Expression interceptorFor(ClassEntity cls) {
return _emitter.interceptorPrototypeAccess(cls);
}
@ -374,11 +375,9 @@ class InterceptorStubGenerator {
return null;
}
jsAst.Expression generateOneShotInterceptor(jsAst.Name name) {
Selector selector =
_oneShotInterceptorData.getOneShotInterceptorSelector(name);
Set<ClassEntity> classes =
_interceptorData.getInterceptedClassesOn(selector.name, _closedWorld);
jsAst.Expression generateOneShotInterceptor(OneShotInterceptor interceptor) {
Selector selector = interceptor.selector;
Set<ClassEntity> classes = interceptor.classes;
jsAst.Name getInterceptorName = _namer.nameForGetInterceptor(classes);
List<String> parameterNames = <String>[];

View file

@ -85,10 +85,9 @@ class Collector {
// Go over specialized interceptors and then constants to know which
// interceptors are needed.
Set<ClassEntity> needed = new Set<ClassEntity>();
for (js.Name name
in _oneShotInterceptorData.specializedGetInterceptorNames) {
needed.addAll(
_oneShotInterceptorData.getSpecializedGetInterceptorsFor(name));
for (SpecializedGetInterceptor interceptor
in _oneShotInterceptorData.specializedGetInterceptors) {
needed.addAll(interceptor.classes);
}
// Add interceptors referenced by constants.

View file

@ -345,7 +345,6 @@ class ProgramBuilder {
_task.emitter,
_nativeCodegenEnqueuer,
_namer,
_oneShotInterceptorData,
_customElementsCodegenAnalysis,
_codegenWorld,
_closedWorld);
@ -1002,11 +1001,10 @@ class ProgramBuilder {
// We must evaluate these classes eagerly so that the prototype is
// accessible.
void _markEagerInterceptorClasses() {
Iterable<js.Name> names =
_oneShotInterceptorData.specializedGetInterceptorNames;
for (js.Name name in names) {
for (ClassEntity element
in _oneShotInterceptorData.getSpecializedGetInterceptorsFor(name)) {
Iterable<SpecializedGetInterceptor> interceptors =
_oneShotInterceptorData.specializedGetInterceptors;
for (SpecializedGetInterceptor interceptor in interceptors) {
for (ClassEntity element in interceptor.classes) {
Class cls = _classes[element];
if (cls != null) cls.isEager = true;
}
@ -1020,7 +1018,6 @@ class ProgramBuilder {
_task.emitter,
_nativeCodegenEnqueuer,
_namer,
_oneShotInterceptorData,
_customElementsCodegenAnalysis,
_codegenWorld,
_closedWorld);
@ -1030,13 +1027,23 @@ class ProgramBuilder {
// TODO(floitsch): we shouldn't update the registry in the middle of
// generating the interceptor methods.
Holder holder = _registry.registerHolder(holderName);
Iterable<js.Name> names =
_oneShotInterceptorData.specializedGetInterceptorNames;
List<js.Name> names = [];
Map<js.Name, SpecializedGetInterceptor> interceptorMap = {};
for (SpecializedGetInterceptor interceptor
in _oneShotInterceptorData.specializedGetInterceptors) {
js.Name name = _namer.nameForGetInterceptor(interceptor.classes);
names.add(name);
assert(
!interceptorMap.containsKey(name),
"Duplicate specialized get interceptor for $name: Existing: "
"${interceptorMap[name]}, new ${interceptor}.");
interceptorMap[name] = interceptor;
}
names.sort();
return names.map((js.Name name) {
Set<ClassEntity> classes =
_oneShotInterceptorData.getSpecializedGetInterceptorsFor(name);
js.Expression code = stubGenerator.generateGetInterceptorMethod(classes);
SpecializedGetInterceptor interceptor = interceptorMap[name];
js.Expression code =
stubGenerator.generateGetInterceptorMethod(interceptor);
return new StaticStubMethod(name, holder, code);
});
}
@ -1116,7 +1123,6 @@ class ProgramBuilder {
_task.emitter,
_nativeCodegenEnqueuer,
_namer,
_oneShotInterceptorData,
_customElementsCodegenAnalysis,
_codegenWorld,
_closedWorld);
@ -1126,10 +1132,24 @@ class ProgramBuilder {
// TODO(floitsch): we shouldn't update the registry in the middle of
// generating the interceptor methods.
Holder holder = _registry.registerHolder(holderName);
List<js.Name> names = _oneShotInterceptorData.oneShotInterceptorNames;
List<js.Name> names = [];
Map<js.Name, OneShotInterceptor> interceptorMap = {};
for (OneShotInterceptor interceptor
in _oneShotInterceptorData.oneShotInterceptors) {
js.Name name = _namer.nameForGetOneShotInterceptor(
interceptor.selector, interceptor.classes);
names.add(name);
assert(
!interceptorMap.containsKey(name),
"Duplicate specialized get interceptor for $name: Existing: "
"${interceptorMap[name]}, new ${interceptor}.");
interceptorMap[name] = interceptor;
}
names.sort();
return names.map((js.Name name) {
js.Expression code = stubGenerator.generateOneShotInterceptor(name);
OneShotInterceptor interceptor = interceptorMap[name];
js.Expression code =
stubGenerator.generateOneShotInterceptor(interceptor);
return new StaticStubMethod(name, holder, code);
});
}

View file

@ -67,6 +67,15 @@ class CallStructure {
sink.end(tag);
}
/// Returns `true` if this call structure is normalized, that is, its named
/// arguments are sorted.
bool get isNormalized => true;
/// Returns the normalized version of this call structure.
///
/// A [CallStructure] is normalized if its named arguments are sorted.
CallStructure toNormalized() => this;
CallStructure withTypeArgumentCount(int typeArgumentCount) =>
new CallStructure(argumentCount, namedArguments, typeArgumentCount);
@ -185,17 +194,21 @@ class CallStructure {
}
}
///
/// Call structure with named arguments.
class NamedCallStructure extends CallStructure {
@override
final List<String> namedArguments;
final List<String> _orderedNamedArguments = <String>[];
final List<String> _orderedNamedArguments;
NamedCallStructure(
int argumentCount, this.namedArguments, int typeArgumentCount)
: super.unnamed(argumentCount, typeArgumentCount) {
assert(namedArguments.isNotEmpty);
}
int argumentCount, List<String> namedArguments, int typeArgumentCount)
: this.internal(
argumentCount, namedArguments, typeArgumentCount, <String>[]);
NamedCallStructure.internal(int argumentCount, this.namedArguments,
int typeArgumentCount, this._orderedNamedArguments)
: assert(namedArguments.isNotEmpty),
super.unnamed(argumentCount, typeArgumentCount);
@override
bool get isNamed => true;
@ -209,6 +222,16 @@ class NamedCallStructure extends CallStructure {
@override
int get positionalArgumentCount => argumentCount - namedArgumentCount;
@override
bool get isNormalized => namedArguments == _orderedNamedArguments;
@override
CallStructure toNormalized() => new NamedCallStructure.internal(
argumentCount,
getOrderedNamedArguments(),
typeArgumentCount,
getOrderedNamedArguments());
@override
List<String> getOrderedNamedArguments() {
if (!_orderedNamedArguments.isEmpty) return _orderedNamedArguments;

View file

@ -315,6 +315,15 @@ class Selector {
return 'Selector($kind, $name, ${callStructure.structureToString()})';
}
/// Returns the normalized version of this selector.
///
/// A selector is normalized if its call structure is normalized.
// TODO(johnniwinther): Use normalized selectors as much as possible,
// especially where selectors are used in sets or as keys in maps.
Selector toNormalized() => callStructure.isNormalized
? this
: new Selector(kind, memberName, callStructure.toNormalized());
Selector toCallSelector() => new Selector.callClosureFrom(this);
/// Returns the non-generic [Selector] corresponding to this selector.