Skip to content

Commit 8d4816b

Browse files
johnniwinthercommit-bot@chromium.org
authored andcommitted
Reimplement member usage tracking
We now track member usage in terms of static, dynamic and super access for reads, writes and invocations. The information collected during closed world computation is now the basis for the potential member usage in codegen, thus ensuring that we cannot conclude in codegen that for instance a field is read dynamically when the closed world knows that it is never read dynamically. Closes #36516 Change-Id: I3a1cb87c71268c34bcd67e14a035d9d1be324ab0 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/100840 Commit-Queue: Johnni Winther <[email protected]> Reviewed-by: Sigmund Cherem <[email protected]>
1 parent b36734f commit 8d4816b

19 files changed

+1022
-740
lines changed

pkg/compiler/lib/src/deferred_load.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,7 @@ abstract class DeferredLoadTask extends CompilerTask {
296296
_collectTypeArgumentDependencies(
297297
staticUse.type.typeArguments, dependencies);
298298
break;
299-
case StaticUseKind.INVOKE:
299+
case StaticUseKind.STATIC_INVOKE:
300300
case StaticUseKind.CLOSURE_CALL:
301301
case StaticUseKind.DIRECT_INVOKE:
302302
// TODO(johnniwinther): Use rti need data to skip unneeded type

pkg/compiler/lib/src/enqueue.dart

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ abstract class EnqueuerImpl extends Enqueuer {
207207

208208
/// Check enqueuer consistency after the queue has been closed.
209209
bool checkEnqueuerConsistency(ElementEnvironment elementEnvironment) {
210-
task.measure(() {
210+
task.measureSubtask('resolution.check', () {
211211
// Run through the classes and see if we need to enqueue more methods.
212212
for (ClassEntity classElement
213213
in worldBuilder.directlyInstantiatedClasses) {
@@ -284,7 +284,7 @@ class ResolutionEnqueuer extends EnqueuerImpl {
284284
{ConstructorEntity constructor,
285285
bool nativeUsage: false,
286286
bool globalDependency: false}) {
287-
task.measure(() {
287+
task.measureSubtask('resolution.typeUse', () {
288288
_worldBuilder.registerTypeInstantiation(type, _applyClassUse,
289289
constructor: constructor);
290290
listener.registerInstantiatedType(type,
@@ -307,7 +307,7 @@ class ResolutionEnqueuer extends EnqueuerImpl {
307307
_reporter.internalError(member,
308308
'Unenqueued use of $member: ${useSet.iterable(MemberUse.values)}');
309309
}
310-
}, dryRun: true);
310+
}, checkEnqueuerConsistency: true);
311311
}
312312

313313
/// Callback for applying the use of a [member].
@@ -343,14 +343,14 @@ class ResolutionEnqueuer extends EnqueuerImpl {
343343

344344
@override
345345
void processDynamicUse(DynamicUse dynamicUse) {
346-
task.measure(() {
346+
task.measureSubtask('resolution.dynamicUse', () {
347347
_worldBuilder.registerDynamicUse(dynamicUse, _applyMemberUse);
348348
});
349349
}
350350

351351
@override
352352
void processConstantUse(ConstantUse constantUse) {
353-
task.measure(() {
353+
task.measureSubtask('resolution.constantUse', () {
354354
if (_worldBuilder.registerConstantUse(constantUse)) {
355355
applyImpact(listener.registerUsedConstant(constantUse.value),
356356
impactSource: 'constant use');
@@ -361,18 +361,20 @@ class ResolutionEnqueuer extends EnqueuerImpl {
361361

362362
@override
363363
void processStaticUse(StaticUse staticUse) {
364-
_worldBuilder.registerStaticUse(staticUse, _applyMemberUse);
365-
// TODO(johnniwinther): Add `ResolutionWorldBuilder.registerConstructorUse`
366-
// for these:
367-
switch (staticUse.kind) {
368-
case StaticUseKind.CONSTRUCTOR_INVOKE:
369-
case StaticUseKind.CONST_CONSTRUCTOR_INVOKE:
370-
_registerInstantiatedType(staticUse.type,
371-
constructor: staticUse.element, globalDependency: false);
372-
break;
373-
default:
374-
break;
375-
}
364+
task.measureSubtask('resolution.staticUse', () {
365+
_worldBuilder.registerStaticUse(staticUse, _applyMemberUse);
366+
// TODO(johnniwinther): Add `ResolutionWorldBuilder.registerConstructorUse`
367+
// for these:
368+
switch (staticUse.kind) {
369+
case StaticUseKind.CONSTRUCTOR_INVOKE:
370+
case StaticUseKind.CONST_CONSTRUCTOR_INVOKE:
371+
_registerInstantiatedType(staticUse.type,
372+
constructor: staticUse.element, globalDependency: false);
373+
break;
374+
default:
375+
break;
376+
}
377+
});
376378
}
377379

378380
@override

pkg/compiler/lib/src/js_backend/enqueuer.dart

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ class CodegenEnqueuer extends EnqueuerImpl {
107107

108108
void _registerInstantiatedType(InterfaceType type,
109109
{bool nativeUsage: false}) {
110-
task.measure(() {
110+
task.measureSubtask('codegen.typeUse', () {
111111
_worldBuilder.registerTypeInstantiation(type, _applyClassUse);
112112
listener.registerInstantiatedType(type, nativeUsage: nativeUsage);
113113
});
@@ -127,7 +127,7 @@ class CodegenEnqueuer extends EnqueuerImpl {
127127
failedAt(member,
128128
'Unenqueued use of $member: ${useSet.iterable(MemberUse.values)}');
129129
}
130-
}, dryRun: true);
130+
}, checkEnqueuerConsistency: true);
131131
}
132132

133133
/// Callback for applying the use of a [cls].
@@ -160,26 +160,28 @@ class CodegenEnqueuer extends EnqueuerImpl {
160160

161161
@override
162162
void processDynamicUse(DynamicUse dynamicUse) {
163-
task.measure(() {
163+
task.measureSubtask('codegen.dynamicUse', () {
164164
_worldBuilder.registerDynamicUse(dynamicUse, _applyMemberUse);
165165
});
166166
}
167167

168168
@override
169169
void processStaticUse(StaticUse staticUse) {
170-
_worldBuilder.registerStaticUse(staticUse, _applyMemberUse);
171-
switch (staticUse.kind) {
172-
case StaticUseKind.CONSTRUCTOR_INVOKE:
173-
case StaticUseKind.CONST_CONSTRUCTOR_INVOKE:
174-
processTypeUse(new TypeUse.instantiation(staticUse.type));
175-
break;
176-
case StaticUseKind.INLINING:
177-
// TODO(johnniwinther): Should this be tracked with _MemberUsage ?
178-
listener.registerUsedElement(staticUse.element);
179-
break;
180-
default:
181-
break;
182-
}
170+
task.measureSubtask('codegen.staticUse', () {
171+
_worldBuilder.registerStaticUse(staticUse, _applyMemberUse);
172+
switch (staticUse.kind) {
173+
case StaticUseKind.CONSTRUCTOR_INVOKE:
174+
case StaticUseKind.CONST_CONSTRUCTOR_INVOKE:
175+
processTypeUse(new TypeUse.instantiation(staticUse.type));
176+
break;
177+
case StaticUseKind.INLINING:
178+
// TODO(johnniwinther): Should this be tracked with _MemberUsage ?
179+
listener.registerUsedElement(staticUse.element);
180+
break;
181+
default:
182+
break;
183+
}
184+
});
183185
}
184186

185187
@override
@@ -229,7 +231,7 @@ class CodegenEnqueuer extends EnqueuerImpl {
229231

230232
@override
231233
void processConstantUse(ConstantUse constantUse) {
232-
task.measure(() {
234+
task.measureSubtask('codegen.constantUse', () {
233235
if (_worldBuilder.registerConstantUse(constantUse)) {
234236
applyImpact(listener.registerUsedConstant(constantUse.value));
235237
_recentConstants = true;

pkg/compiler/lib/src/js_emitter/parameter_stub_generator.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ class ParameterStubGenerator {
5353
_closedWorld.elementEnvironment;
5454

5555
bool needsSuperGetter(FunctionEntity element) =>
56-
_codegenWorld.methodsNeedingSuperGetter.contains(element);
56+
_codegenWorld.methodsNeedsSuperGetter(element);
5757

5858
/// Generates stubs to fill in missing optional named or positional arguments
5959
/// and missing type arguments. Returns `null` if no stub is needed.

pkg/compiler/lib/src/js_emitter/program_builder/program_builder.dart

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,7 @@ class ProgramBuilder {
477477

478478
void _addJsInteropStubs(LibrariesMap librariesMap) {
479479
if (_classes.containsKey(_commonElements.objectClass)) {
480-
var toStringInvocation = _namer.invocationName(Selectors.toString_);
480+
js.Name toStringInvocation = _namer.invocationName(Selectors.toString_);
481481
// TODO(jacobr): register toString as used so that it is always accessible
482482
// from JavaScript.
483483
_classes[_commonElements.objectClass].callStubs.add(_buildStubMethod(
@@ -492,22 +492,23 @@ class ProgramBuilder {
492492
// a regular getter that returns a JavaScript function and tearing off
493493
// a method in the case where there exist multiple JavaScript classes
494494
// that conflict on whether the member is a getter or a method.
495-
var interceptorClass = _classes[_commonElements.jsJavaScriptObjectClass];
496-
var stubNames = new Set<String>();
495+
Class interceptorClass = _classes[_commonElements.jsJavaScriptObjectClass];
496+
Set<String> stubNames = {};
497497
librariesMap
498498
.forEach((LibraryEntity library, List<ClassEntity> classElements, _) {
499499
for (ClassEntity cls in classElements) {
500500
if (_nativeData.isJsInteropClass(cls)) {
501501
_elementEnvironment.forEachLocalClassMember(cls,
502502
(MemberEntity member) {
503-
var jsName = _nativeData.computeUnescapedJSInteropName(member.name);
503+
String jsName =
504+
_nativeData.computeUnescapedJSInteropName(member.name);
504505
if (!member.isInstanceMember) return;
505506
if (member.isGetter || member.isField || member.isFunction) {
506-
var selectors =
507+
Iterable<Selector> selectors =
507508
_codegenWorld.getterInvocationsByName(member.name);
508509
if (selectors != null && !selectors.isEmpty) {
509-
for (var selector in selectors.keys) {
510-
var stubName = _namer.invocationName(selector);
510+
for (Selector selector in selectors) {
511+
js.Name stubName = _namer.invocationName(selector);
511512
if (stubNames.add(stubName.key)) {
512513
interceptorClass.callStubs.add(_buildStubMethod(stubName,
513514
js.js('function(obj) { return obj.# }', [jsName]),
@@ -518,7 +519,7 @@ class ProgramBuilder {
518519
}
519520

520521
if (member.isSetter || (member.isField && !member.isConst)) {
521-
var selectors =
522+
Iterable<Selector> selectors =
522523
_codegenWorld.setterInvocationsByName(member.name);
523524
if (selectors != null && !selectors.isEmpty) {
524525
var stubName = _namer.setterForMember(member);
@@ -765,11 +766,11 @@ class ProgramBuilder {
765766
assert(!field.needsUncheckedSetter);
766767
FieldEntity element = field.element;
767768
js.Expression code = _generatedCode[element];
769+
assert(code != null, "No setter code for field: $field");
768770
if (code == null) {
769-
// TODO(johnniwinther): Static types are not honoured in the dynamic
770-
// uses created in codegen, leading to dead code, as known by the
771-
// closed world computation, being triggered by the codegen
772-
// enqueuer. We cautiously generate an empty function for this case.
771+
// This should never occur because codegen member usage is now
772+
// limited by closed world member usage. In the case we've missed a
773+
// spot we cautiously generate an empty function.
773774
code = js.js("function() {}");
774775
}
775776
js.Name name = _namer.deriveSetterName(field.accessorName);
@@ -901,9 +902,8 @@ class ProgramBuilder {
901902
isClosureCallMethod = true;
902903
} else {
903904
// Careful with operators.
904-
canTearOff = _codegenWorld.hasInvokedGetter(element);
905-
assert(canTearOff ||
906-
!_codegenWorld.methodsNeedingSuperGetter.contains(element));
905+
canTearOff = _codegenWorld.hasInvokedGetter(element) ||
906+
_codegenWorld.methodsNeedsSuperGetter(element);
907907
tearOffName = _namer.getterForElement(element);
908908
}
909909
}

pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1084,11 +1084,12 @@ class FragmentEmitter {
10841084
js.Expression code;
10851085
if (field.isElided) {
10861086
ConstantValue constantValue = field.constantValue;
1087+
assert(
1088+
constantValue != null, "No constant value for elided field: $field");
10871089
if (constantValue == null) {
1088-
// TODO(johnniwinther): Static types are not honoured in the dynamic
1089-
// uses created in codegen, leading to dead code, as known by the closed
1090-
// world computation, being triggered by the codegen enqueuer. We
1091-
// cautiously generate a null constant for this case.
1090+
// This should never occur because codegen member usage is now limited
1091+
// by closed world member usage. In the case we've missed a spot we
1092+
// cautiously generate a null constant.
10921093
constantValue = new NullConstantValue();
10931094
}
10941095
code = js.js(

pkg/compiler/lib/src/js_model/js_world.dart

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import '../serialization/serialization.dart';
3232
import '../universe/class_hierarchy.dart';
3333
import '../universe/class_set.dart';
3434
import '../universe/function_set.dart' show FunctionSet;
35+
import '../universe/member_usage.dart';
3536
import '../universe/selector.dart';
3637
import '../world.dart';
3738
import 'element_map.dart';
@@ -93,6 +94,8 @@ class JsClosedWorld implements JClosedWorld {
9394
final OutputUnitData outputUnitData;
9495
Sorter _sorter;
9596

97+
final Map<MemberEntity, MemberAccess> memberAccess;
98+
9699
JsClosedWorld(
97100
this.elementMap,
98101
this.nativeData,
@@ -113,7 +116,8 @@ class JsClosedWorld implements JClosedWorld {
113116
this.annotationsData,
114117
this.globalLocalsMap,
115118
this.closureDataLookup,
116-
this.outputUnitData) {
119+
this.outputUnitData,
120+
this.memberAccess) {
117121
_abstractValueDomain = abstractValueStrategy.createDomain(this);
118122
}
119123

@@ -167,6 +171,9 @@ class JsClosedWorld implements JClosedWorld {
167171
OutputUnitData outputUnitData =
168172
new OutputUnitData.readFromDataSource(source);
169173

174+
Map<MemberEntity, MemberAccess> memberAccess =
175+
source.readMemberMap(() => new MemberAccess.readFromDataSource(source));
176+
170177
source.end(tag);
171178

172179
return new JsClosedWorld(
@@ -189,7 +196,8 @@ class JsClosedWorld implements JClosedWorld {
189196
annotationsData,
190197
globalLocalsMap,
191198
closureData,
192-
outputUnitData);
199+
outputUnitData,
200+
memberAccess);
193201
}
194202

195203
/// Serializes this [JsClosedWorld] to [sink].
@@ -217,6 +225,8 @@ class JsClosedWorld implements JClosedWorld {
217225
annotationsData.writeToDataSink(sink);
218226
closureDataLookup.writeToDataSink(sink);
219227
outputUnitData.writeToDataSink(sink);
228+
sink.writeMemberMap(
229+
memberAccess, (MemberAccess access) => access.writeToDataSink(sink));
220230
sink.end(tag);
221231
}
222232

@@ -539,6 +549,11 @@ class JsClosedWorld implements JClosedWorld {
539549
return elementEnvironment.isMixinApplication(cls) &&
540550
!elementEnvironment.isUnnamedMixinApplication(cls);
541551
}
552+
553+
@override
554+
MemberAccess getMemberAccess(MemberEntity member) {
555+
return memberAccess[member];
556+
}
542557
}
543558

544559
class KernelSorter implements Sorter {

pkg/compiler/lib/src/js_model/js_world_builder.dart

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import '../options.dart';
2929
import '../universe/class_hierarchy.dart';
3030
import '../universe/class_set.dart';
3131
import '../universe/feature.dart';
32+
import '../universe/member_usage.dart';
3233
import '../universe/selector.dart';
3334
import '../world.dart';
3435
import 'closure.dart';
@@ -202,6 +203,11 @@ class JsClosedWorldBuilder {
202203
OutputUnitData outputUnitData =
203204
_convertOutputUnitData(map, kOutputUnitData, closureData);
204205

206+
Map<MemberEntity, MemberAccess> memberAccess = map.toBackendMemberMap(
207+
closedWorld.liveMemberUsage,
208+
(MemberUsage usage) =>
209+
new MemberAccess(usage.reads, usage.writes, usage.invokes));
210+
205211
return new JsClosedWorld(
206212
_elementMap,
207213
nativeData,
@@ -226,7 +232,8 @@ class JsClosedWorldBuilder {
226232
annotationsData,
227233
_globalLocalsMap,
228234
closureData,
229-
outputUnitData);
235+
outputUnitData,
236+
memberAccess);
230237
}
231238

232239
BackendUsage _convertBackendUsage(
@@ -487,7 +494,7 @@ class JsClosedWorldBuilder {
487494
map.toBackendLibrary,
488495
convertClassMap,
489496
convertMemberMap,
490-
(m) => convertMap<ConstantValue, OutputUnit>(
497+
(m) => convertMap<ConstantValue, OutputUnit, OutputUnit>(
491498
m, map.toBackendConstant, (v) => v));
492499
}
493500
}
@@ -633,20 +640,20 @@ abstract class JsToFrontendMap {
633640
return convertMap(map, toBackendClass, convert);
634641
}
635642

636-
Map<MemberEntity, V> toBackendMemberMap<V>(
637-
Map<MemberEntity, V> map, V convert(V value)) {
643+
Map<MemberEntity, V2> toBackendMemberMap<V1, V2>(
644+
Map<MemberEntity, V1> map, V2 convert(V1 value)) {
638645
return convertMap(map, toBackendMember, convert);
639646
}
640647
}
641648

642649
E identity<E>(E element) => element;
643650

644-
Map<K, V> convertMap<K, V>(
645-
Map<K, V> map, K convertKey(K key), V convertValue(V value)) {
646-
Map<K, V> newMap = <K, V>{};
647-
map.forEach((K key, V value) {
651+
Map<K, V2> convertMap<K, V1, V2>(
652+
Map<K, V1> map, K convertKey(K key), V2 convertValue(V1 value)) {
653+
Map<K, V2> newMap = <K, V2>{};
654+
map.forEach((K key, V1 value) {
648655
K newKey = convertKey(key);
649-
V newValue = convertValue(value);
656+
V2 newValue = convertValue(value);
650657
if (newKey != null && newValue != null) {
651658
// Entities that are not used don't have a corresponding backend entity.
652659
newMap[newKey] = newValue;

0 commit comments

Comments
 (0)