Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(103)

Side by Side Diff: pkg/compiler/lib/src/js_backend/lookup_map_analysis.dart

Issue 2745133002: Create LookupMapAnalysis late. (Closed)
Patch Set: Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 /// Analysis to determine how to generate code for `LookupMap`s. 5 /// Analysis to determine how to generate code for `LookupMap`s.
6 library compiler.src.js_backend.lookup_map_analysis; 6 library compiler.src.js_backend.lookup_map_analysis;
7 7
8 import 'package:pub_semver/pub_semver.dart'; 8 import 'package:pub_semver/pub_semver.dart';
9 9
10 import '../common.dart'; 10 import '../common.dart';
11 import '../common_elements.dart'; 11 import '../common_elements.dart';
12 import '../common/backend_api.dart'; 12 import '../common/backend_api.dart';
13 import '../compile_time_constants.dart';
14 import '../constants/constant_system.dart';
13 import '../constants/values.dart' 15 import '../constants/values.dart'
14 show 16 show
15 ConstantValue, 17 ConstantValue,
16 ConstructedConstantValue, 18 ConstructedConstantValue,
17 ListConstantValue, 19 ListConstantValue,
18 NullConstantValue, 20 NullConstantValue,
19 StringConstantValue, 21 StringConstantValue,
20 TypeConstantValue; 22 TypeConstantValue;
21 import '../elements/elements.dart' show ClassElement, FieldElement; 23 import '../elements/elements.dart' show ClassElement, FieldElement;
22 import '../elements/entities.dart'; 24 import '../elements/entities.dart';
23 import '../elements/resolution_types.dart' show ResolutionInterfaceType; 25 import '../elements/resolution_types.dart' show ResolutionInterfaceType;
24 import '../options.dart'; 26 import '../options.dart';
25 import '../universe/use.dart' show ConstantUse, StaticUse; 27 import '../universe/use.dart' show ConstantUse, StaticUse;
26 import '../universe/world_impact.dart' 28 import '../universe/world_impact.dart'
27 show WorldImpact, StagedWorldImpactBuilder; 29 show WorldImpact, StagedWorldImpactBuilder;
28 import 'backend.dart' show JavaScriptBackend;
29 import 'backend_helpers.dart'; 30 import 'backend_helpers.dart';
30 31
31 /// Lookup map handling for resolution. 32 /// Lookup map handling for resolution.
32 class LookupMapLibraryAccess { 33 class LookupMapLibraryAccess {
33 static final Uri PACKAGE_LOOKUP_MAP = 34 static final Uri PACKAGE_LOOKUP_MAP =
34 new Uri(scheme: 'package', path: 'lookup_map/lookup_map.dart'); 35 new Uri(scheme: 'package', path: 'lookup_map/lookup_map.dart');
35 36
36 /// Reference the diagnostic reporting system for logging and reporting issues 37 /// Reference the diagnostic reporting system for logging and reporting issues
37 /// to the end-user. 38 /// to the end-user.
38 final DiagnosticReporter _reporter; 39 final DiagnosticReporter _reporter;
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after
106 /// 107 ///
107 /// * Include all generic-type arguments, if the program uses type 108 /// * Include all generic-type arguments, if the program uses type
108 /// variables in expressions such as `class A<T> { Type get extract => T }`. 109 /// variables in expressions such as `class A<T> { Type get extract => T }`.
109 /// 110 ///
110 // TODO(sigmund): add support for const expressions, currently this 111 // TODO(sigmund): add support for const expressions, currently this
111 // implementation only supports Type literals. To support const expressions we 112 // implementation only supports Type literals. To support const expressions we
112 // need to change some of the invariants below (e.g. we can no longer use the 113 // need to change some of the invariants below (e.g. we can no longer use the
113 // ClassElement of a type to refer to keys we need to discover). 114 // ClassElement of a type to refer to keys we need to discover).
114 // TODO(sigmund): detect uses of mirrors 115 // TODO(sigmund): detect uses of mirrors
115 class LookupMapAnalysis { 116 class LookupMapAnalysis {
117 const LookupMapAnalysis._();
118
119 factory LookupMapAnalysis(
120 DiagnosticReporter reporter,
121 ConstantSystem constantSystem,
122 ConstantEnvironment constants,
123 ElementEnvironment elementEnvironment,
124 CommonElements commonElements,
125 BackendHelpers helpers,
126 BackendClasses backendClasses,
127 LookupMapLibraryAccess analysis) {
128 /// Checks if the version of lookup_map is valid, and if so, enable this
129 /// analysis during codegen.
130 FieldElement lookupMapVersionVariable = analysis.lookupMapVersionVariable;
131 if (lookupMapVersionVariable == null) return const LookupMapAnalysis._();
132
133 // At this point, the lookupMapVersionVariable should be resolved and it's
134 // constant value should be available.
135 StringConstantValue value =
136 constants.getConstantValue(lookupMapVersionVariable.constant);
137 if (value == null) {
138 reporter.reportHintMessage(lookupMapVersionVariable,
139 MessageKind.UNRECOGNIZED_VERSION_OF_LOOKUP_MAP);
140 return const LookupMapAnalysis._();
141 }
142
143 // TODO(sigmund): add proper version resolution using the pub_semver package
144 // when we introduce the next version.
145 Version version;
146 try {
147 version = new Version.parse(value.primitiveValue.slowToString());
148 } catch (e) {}
149
150 if (version == null || !_validLookupMapVersionConstraint.allows(version)) {
151 reporter.reportHintMessage(lookupMapVersionVariable,
152 MessageKind.UNRECOGNIZED_VERSION_OF_LOOKUP_MAP);
153 return const LookupMapAnalysis._();
154 }
155
156 ClassEntity typeLookupMapClass =
157 elementEnvironment.lookupClass(analysis.lookupMapLibrary, 'LookupMap');
158 FieldElement entriesField =
159 elementEnvironment.lookupClassMember(typeLookupMapClass, '_entries');
160 FieldElement keyField =
161 elementEnvironment.lookupClassMember(typeLookupMapClass, '_key');
162 FieldElement valueField =
163 elementEnvironment.lookupClassMember(typeLookupMapClass, '_value');
164 // TODO(sigmund): Maybe inline nested maps to make the output code smaller?
165
166 return new LookupMapAnalysisImpl(constantSystem, commonElements, helpers,
167 backendClasses, entriesField, keyField, valueField, typeLookupMapClass);
168 }
169
170 /// Compute the [WorldImpact] for the constants registered since last flush.
171 WorldImpact flush() => const WorldImpact();
172
173 /// Whether [constant] is an instance of a `LookupMap`.
174 bool isLookupMap(ConstantValue constant) => false;
175
176 /// Registers an instance of a lookup-map with the analysis.
177 void registerLookupMapReference(ConstantValue lookupMap) {}
178
179 /// Callback from the enqueuer, invoked when [element] is instantiated.
180 void registerInstantiatedClass(ClassElement element) {}
181
182 /// Callback from the enqueuer, invoked when [type] is instantiated.
183 void registerInstantiatedType(ResolutionInterfaceType type) {}
184
185 /// Callback from the codegen enqueuer, invoked when a constant (which is
186 /// possibly a const key or a type literal) is used in the program.
187 void registerTypeConstant(ClassElement element) {}
188
189 void registerConstantKey(ConstantValue constant) {}
190
191 void logSummary(void log(String message)) {}
192
193 void onQueueClosed() {}
194 }
195
196 class LookupMapAnalysisImpl implements LookupMapAnalysis {
Siggi Cherem (dart-lang) 2017/03/14 05:47:00 maybe give it a private name instead?
Johnni Winther 2017/03/15 13:38:00 Done.
116 static final Uri PACKAGE_LOOKUP_MAP = 197 static final Uri PACKAGE_LOOKUP_MAP =
117 new Uri(scheme: 'package', path: 'lookup_map/lookup_map.dart'); 198 new Uri(scheme: 'package', path: 'lookup_map/lookup_map.dart');
118 199
119 /// Reference to [JavaScriptBackend] to be able to enqueue work when we 200 final ConstantSystem _constantSystem;
120 /// discover that a key in a map is potentially used.
121 final JavaScriptBackend _backend;
122
123 final CompilerOptions _options;
124
125 /// Reference the diagnostic reporting system for logging and reporting issues
126 /// to the end-user.
127 final DiagnosticReporter _reporter;
128
129 final ElementEnvironment _elementEnvironment;
130 201
131 final CommonElements _commonElements; 202 final CommonElements _commonElements;
132 203
133 final BackendHelpers _helpers; 204 final BackendHelpers _helpers;
134 205
135 final BackendClasses _backendClasses; 206 final BackendClasses _backendClasses;
136 207
137 /// The resolved [ClassElement] associated with `LookupMap`. 208 /// The resolved [ClassElement] associated with `LookupMap`.
138 ClassElement typeLookupMapClass; 209 final ClassElement _typeLookupMapClass;
139 210
140 /// The resolved [FieldElement] for `LookupMap._entries`. 211 /// The resolved [FieldElement] for `LookupMap._entries`.
141 FieldElement entriesField; 212 final FieldElement _entriesField;
142 213
143 /// The resolved [FieldElement] for `LookupMap._key`. 214 /// The resolved [FieldElement] for `LookupMap._key`.
144 FieldElement keyField; 215 final FieldElement _keyField;
145 216
146 /// The resolved [FieldElement] for `LookupMap._value`. 217 /// The resolved [FieldElement] for `LookupMap._value`.
147 FieldElement valueField; 218 final FieldElement _valueField;
148 219
149 /// Constant instances of `LookupMap` and information about them tracked by 220 /// Constant instances of `LookupMap` and information about them tracked by
150 /// this analysis. 221 /// this analysis.
151 final Map<ConstantValue, _LookupMapInfo> _lookupMaps = {}; 222 final Map<ConstantValue, _LookupMapInfo> _lookupMaps = {};
152 223
153 /// Keys that we have discovered to be in use in the program. 224 /// Keys that we have discovered to be in use in the program.
154 final _inUse = new Set<ConstantValue>(); 225 final _inUse = new Set<ConstantValue>();
155 226
156 /// Internal helper to memoize the mapping between class elements and their 227 /// Internal helper to memoize the mapping between class elements and their
157 /// corresponding type constants. 228 /// corresponding type constants.
158 final _typeConstants = <ClassElement, TypeConstantValue>{}; 229 final _typeConstants = <ClassElement, TypeConstantValue>{};
159 230
160 /// Internal helper to memoize which classes (ignoring Type) override equals. 231 /// Internal helper to memoize which classes (ignoring Type) override equals.
161 /// 232 ///
162 /// Const keys of these types will not be tree-shaken because we can't 233 /// Const keys of these types will not be tree-shaken because we can't
163 /// statically guarantee that the program doesn't produce an equivalent key at 234 /// statically guarantee that the program doesn't produce an equivalent key at
164 /// runtime. Technically if we limit lookup-maps to check for identical keys, 235 /// runtime. Technically if we limit lookup-maps to check for identical keys,
165 /// we could allow const instances of these types. However, we internally use 236 /// we could allow const instances of these types. However, we internally use
166 /// a hash map within lookup-maps today, so we need this restriction. 237 /// a hash map within lookup-maps today, so we need this restriction.
167 final _typesWithEquals = <ClassElement, bool>{}; 238 final _typesWithEquals = <ClassElement, bool>{};
168 239
169 /// Pending work to do if we discover that a new key is in use. For each key 240 /// Pending work to do if we discover that a new key is in use. For each key
170 /// that we haven't seen, we record the list of lookup-maps that contain an 241 /// that we haven't seen, we record the list of lookup-maps that contain an
171 /// entry with that key. 242 /// entry with that key.
172 final _pending = <ConstantValue, List<_LookupMapInfo>>{}; 243 final _pending = <ConstantValue, List<_LookupMapInfo>>{};
173 244
174 final StagedWorldImpactBuilder _impactBuilder = 245 final StagedWorldImpactBuilder _impactBuilder =
175 new StagedWorldImpactBuilder(); 246 new StagedWorldImpactBuilder();
176 247
177 /// Whether the backend is currently processing the codegen queue. 248 LookupMapAnalysisImpl(
178 bool _inCodegen = false; 249 this._constantSystem,
179
180 LookupMapAnalysis(
181 this._backend,
182 this._options,
183 this._reporter,
184 this._elementEnvironment,
185 this._commonElements, 250 this._commonElements,
186 this._helpers, 251 this._helpers,
187 this._backendClasses); 252 this._backendClasses,
253 this._entriesField,
254 this._keyField,
255 this._valueField,
256 this._typeLookupMapClass);
188 257
189 /// Compute the [WorldImpact] for the constants registered since last flush. 258 /// Compute the [WorldImpact] for the constants registered since last flush.
190 WorldImpact flush() { 259 WorldImpact flush() {
191 return _impactBuilder.flush(); 260 return _impactBuilder.flush();
192 } 261 }
193 262
194 /// Whether this analysis and optimization is enabled.
195 bool get _isEnabled {
196 // `lookupMap==off` kept here to make it easy to test disabling this feature
197 if (const String.fromEnvironment('lookupMap') == 'off') return false;
198 return typeLookupMapClass != null;
199 }
200
201 /// Checks if the version of lookup_map is valid, and if so, enable this
202 /// analysis during codegen.
203 void onCodegenStart(LookupMapLibraryAccess analysis) {
204 _inCodegen = true;
205 FieldElement lookupMapVersionVariable = analysis.lookupMapVersionVariable;
206 if (lookupMapVersionVariable == null) return;
207
208 // At this point, the lookupMapVersionVariable should be resolved and it's
209 // constant value should be available.
210 StringConstantValue value =
211 _backend.constants.getConstantValue(lookupMapVersionVariable.constant);
212 if (value == null) {
213 _reporter.reportHintMessage(lookupMapVersionVariable,
214 MessageKind.UNRECOGNIZED_VERSION_OF_LOOKUP_MAP);
215 return;
216 }
217
218 // TODO(sigmund): add proper version resolution using the pub_semver package
219 // when we introduce the next version.
220 Version version;
221 try {
222 version = new Version.parse(value.primitiveValue.slowToString());
223 } catch (e) {}
224
225 if (version == null || !_validLookupMapVersionConstraint.allows(version)) {
226 _reporter.reportHintMessage(lookupMapVersionVariable,
227 MessageKind.UNRECOGNIZED_VERSION_OF_LOOKUP_MAP);
228 return;
229 }
230
231 ClassEntity cls =
232 _elementEnvironment.lookupClass(analysis.lookupMapLibrary, 'LookupMap');
233 entriesField = _elementEnvironment.lookupClassMember(cls, '_entries');
234 keyField = _elementEnvironment.lookupClassMember(cls, '_key');
235 valueField = _elementEnvironment.lookupClassMember(cls, '_value');
236 // TODO(sigmund): Maybe inline nested maps to make the output code smaller?
237 typeLookupMapClass = cls;
238 }
239
240 /// Whether [constant] is an instance of a `LookupMap`. 263 /// Whether [constant] is an instance of a `LookupMap`.
241 bool isLookupMap(ConstantValue constant) { 264 bool isLookupMap(ConstantValue constant) {
242 if (_isEnabled && constant is ConstructedConstantValue) { 265 if (constant is ConstructedConstantValue) {
243 ResolutionInterfaceType type = constant.type; 266 ResolutionInterfaceType type = constant.type;
244 return type.element.isSubclassOf(typeLookupMapClass); 267 return type.element.isSubclassOf(_typeLookupMapClass);
245 } 268 }
246 return false; 269 return false;
247 } 270 }
248 271
249 /// Registers an instance of a lookup-map with the analysis. 272 /// Registers an instance of a lookup-map with the analysis.
250 void registerLookupMapReference(ConstantValue lookupMap) { 273 void registerLookupMapReference(ConstantValue lookupMap) {
251 if (!_isEnabled || !_inCodegen) return;
252 assert(isLookupMap(lookupMap)); 274 assert(isLookupMap(lookupMap));
253 _lookupMaps.putIfAbsent( 275 _lookupMaps.putIfAbsent(
254 lookupMap, () => new _LookupMapInfo(lookupMap, this).._updateUsed()); 276 lookupMap, () => new _LookupMapInfo(lookupMap, this).._updateUsed());
255 } 277 }
256 278
257 /// Whether [key] is a constant value whose type overrides equals. 279 /// Whether [key] is a constant value whose type overrides equals.
258 bool _overridesEquals(ConstantValue key) { 280 bool _overridesEquals(ConstantValue key) {
259 if (key is ConstructedConstantValue) { 281 if (key is ConstructedConstantValue) {
260 ClassElement element = key.type.element; 282 ClassElement element = key.type.element;
261 return _typesWithEquals.putIfAbsent( 283 return _typesWithEquals.putIfAbsent(
262 element, () => !element.lookupMember('==').enclosingClass.isObject); 284 element, () => !element.lookupMember('==').enclosingClass.isObject);
263 } 285 }
264 return false; 286 return false;
265 } 287 }
266 288
267 /// Whether we need to preserve [key]. This is true for keys that are not 289 /// Whether we need to preserve [key]. This is true for keys that are not
268 /// candidates for tree-shaking in the first place (primitives and non-type 290 /// candidates for tree-shaking in the first place (primitives and non-type
269 /// const values overriding equals) and keys that we have seen in the program. 291 /// const values overriding equals) and keys that we have seen in the program.
270 bool _shouldKeep(ConstantValue key) => 292 bool _shouldKeep(ConstantValue key) =>
271 key.isPrimitive || _inUse.contains(key) || _overridesEquals(key); 293 key.isPrimitive || _inUse.contains(key) || _overridesEquals(key);
272 294
273 void _addClassUse(ClassElement cls) { 295 void _addClassUse(ClassElement cls) {
274 ConstantValue key = _typeConstants.putIfAbsent( 296 ConstantValue key = _typeConstants.putIfAbsent(
275 cls, 297 cls,
276 () => _backend.constantSystem 298 () => _constantSystem.createType(
277 .createType(_commonElements, _backendClasses, cls.rawType)); 299 _commonElements, _backendClasses, cls.rawType));
278 _addUse(key); 300 _addUse(key);
279 } 301 }
280 302
281 /// Record that [key] is used and update every lookup map that contains it. 303 /// Record that [key] is used and update every lookup map that contains it.
282 void _addUse(ConstantValue key) { 304 void _addUse(ConstantValue key) {
283 if (_inUse.add(key)) { 305 if (_inUse.add(key)) {
284 _pending[key]?.forEach((info) => info._markUsed(key)); 306 _pending[key]?.forEach((info) => info._markUsed(key));
285 _pending.remove(key); 307 _pending.remove(key);
286 } 308 }
287 } 309 }
288 310
289 /// If [key] is a type, cache it in [_typeConstants]. 311 /// If [key] is a type, cache it in [_typeConstants].
290 _registerTypeKey(ConstantValue key) { 312 _registerTypeKey(ConstantValue key) {
291 if (key is TypeConstantValue && 313 if (key is TypeConstantValue &&
292 key.representedType is ResolutionInterfaceType) { 314 key.representedType is ResolutionInterfaceType) {
293 ResolutionInterfaceType type = key.representedType; 315 ResolutionInterfaceType type = key.representedType;
294 _typeConstants[type.element] = key; 316 _typeConstants[type.element] = key;
295 } else { 317 } else {
296 // TODO(sigmund): report error? 318 // TODO(sigmund): report error?
297 } 319 }
298 } 320 }
299 321
300 /// Callback from the enqueuer, invoked when [element] is instantiated. 322 /// Callback from the enqueuer, invoked when [element] is instantiated.
301 void registerInstantiatedClass(ClassElement element) { 323 void registerInstantiatedClass(ClassElement element) {
302 if (!_isEnabled || !_inCodegen) return;
303 // TODO(sigmund): only add if .runtimeType is ever used 324 // TODO(sigmund): only add if .runtimeType is ever used
304 _addClassUse(element); 325 _addClassUse(element);
305 } 326 }
306 327
307 /// Callback from the enqueuer, invoked when [type] is instantiated. 328 /// Callback from the enqueuer, invoked when [type] is instantiated.
308 void registerInstantiatedType(ResolutionInterfaceType type) { 329 void registerInstantiatedType(ResolutionInterfaceType type) {
309 if (!_isEnabled || !_inCodegen) return;
310 // TODO(sigmund): only add if .runtimeType is ever used 330 // TODO(sigmund): only add if .runtimeType is ever used
311 _addClassUse(type.element); 331 _addClassUse(type.element);
312 // TODO(sigmund): only do this when type-argument expressions are used? 332 // TODO(sigmund): only do this when type-argument expressions are used?
313 _addGenerics(type); 333 _addGenerics(type);
314 } 334 }
315 335
316 /// Records generic type arguments in [type], in case they are retrieved and 336 /// Records generic type arguments in [type], in case they are retrieved and
317 /// returned using a type-argument expression. 337 /// returned using a type-argument expression.
318 void _addGenerics(ResolutionInterfaceType type) { 338 void _addGenerics(ResolutionInterfaceType type) {
319 if (!type.isGeneric) return; 339 if (!type.isGeneric) return;
320 for (var arg in type.typeArguments) { 340 for (var arg in type.typeArguments) {
321 if (arg is ResolutionInterfaceType) { 341 if (arg is ResolutionInterfaceType) {
322 _addClassUse(arg.element); 342 _addClassUse(arg.element);
323 // Note: this call was needed to generate correct code for 343 // Note: this call was needed to generate correct code for
324 // type_lookup_map/generic_type_test 344 // type_lookup_map/generic_type_test
325 // TODO(sigmund): can we get rid of this? 345 // TODO(sigmund): can we get rid of this?
326 _impactBuilder.registerStaticUse(new StaticUse.staticInvoke( 346 _impactBuilder.registerStaticUse(new StaticUse.staticInvoke(
327 // TODO(johnniwinther): Find the right [CallStructure]. 347 // TODO(johnniwinther): Find the right [CallStructure].
328 _helpers.createRuntimeType, 348 _helpers.createRuntimeType,
329 null)); 349 null));
330 _addGenerics(arg); 350 _addGenerics(arg);
331 } 351 }
332 } 352 }
333 } 353 }
334 354
335 /// Callback from the codegen enqueuer, invoked when a constant (which is 355 /// Callback from the codegen enqueuer, invoked when a constant (which is
336 /// possibly a const key or a type literal) is used in the program. 356 /// possibly a const key or a type literal) is used in the program.
337 void registerTypeConstant(ClassElement element) { 357 void registerTypeConstant(ClassElement element) {
338 if (!_isEnabled || !_inCodegen) return;
339 _addClassUse(element); 358 _addClassUse(element);
340 } 359 }
341 360
342 void registerConstantKey(ConstantValue constant) { 361 void registerConstantKey(ConstantValue constant) {
343 if (!_isEnabled || !_inCodegen) return;
344 if (constant.isPrimitive || _overridesEquals(constant)) return; 362 if (constant.isPrimitive || _overridesEquals(constant)) return;
345 _addUse(constant); 363 _addUse(constant);
346 } 364 }
347 365
366 void logSummary(void log(String message)) {
367 // When --verbose is passed, we show the total number and set of keys that
368 // were tree-shaken from lookup maps.
369 var sb = new StringBuffer();
370 int count = 0;
371 for (var info in _lookupMaps.values) {
372 for (var key in info.unusedEntries.keys) {
373 if (count != 0) sb.write(',');
374 sb.write(key.toDartText());
375 count++;
376 }
377 }
378 log(count == 0
379 ? 'lookup-map: nothing was tree-shaken'
380 : 'lookup-map: found $count unused keys ($sb)');
381 }
382
348 /// Callback from the backend, invoked when reaching the end of the enqueuing 383 /// Callback from the backend, invoked when reaching the end of the enqueuing
349 /// process, but before emitting the code. At this moment we have discovered 384 /// process, but before emitting the code. At this moment we have discovered
350 /// all types used in the program and we can tree-shake anything that is 385 /// all types used in the program and we can tree-shake anything that is
351 /// unused. 386 /// unused.
352 void onQueueClosed() { 387 void onQueueClosed() {
353 if (!_isEnabled || !_inCodegen) return;
354
355 _lookupMaps.values.forEach((info) { 388 _lookupMaps.values.forEach((info) {
356 assert(!info.emitted); 389 assert(!info.emitted);
357 info.emitted = true; 390 info.emitted = true;
358 info._prepareForEmission(); 391 info._prepareForEmission();
359 }); 392 });
360 393
361 // When --verbose is passed, we show the total number and set of keys that
362 // were tree-shaken from lookup maps.
363 if (_options.verbose) {
364 var sb = new StringBuffer();
365 int count = 0;
366 for (var info in _lookupMaps.values) {
367 for (var key in info.unusedEntries.keys) {
368 if (count != 0) sb.write(',');
369 sb.write(key.toDartText());
370 count++;
371 }
372 }
373 _reporter.log(count == 0
374 ? 'lookup-map: nothing was tree-shaken'
375 : 'lookup-map: found $count unused keys ($sb)');
376 }
377
378 // Release resources. 394 // Release resources.
379 _lookupMaps.clear(); 395 _lookupMaps.clear();
380 _pending.clear(); 396 _pending.clear();
381 _inUse.clear(); 397 _inUse.clear();
382 } 398 }
383 } 399 }
384 400
385 /// Internal information about the entries on a lookup-map. 401 /// Internal information about the entries on a lookup-map.
386 class _LookupMapInfo { 402 class _LookupMapInfo {
387 /// The original reference to the constant value. 403 /// The original reference to the constant value.
388 /// 404 ///
389 /// This reference will be mutated in place to remove it's entries when the 405 /// This reference will be mutated in place to remove it's entries when the
390 /// map is first seen during codegen, and to restore them (or a subset of 406 /// map is first seen during codegen, and to restore them (or a subset of
391 /// them) when we have finished discovering which entries are used. This has 407 /// them) when we have finished discovering which entries are used. This has
392 /// the side-effect that `orignal.getDependencies()` will be empty during 408 /// the side-effect that `orignal.getDependencies()` will be empty during
393 /// most of codegen until we are ready to emit the constants. However, 409 /// most of codegen until we are ready to emit the constants. However,
394 /// restoring the entries before emitting code lets us keep the emitter logic 410 /// restoring the entries before emitting code lets us keep the emitter logic
395 /// agnostic of this optimization. 411 /// agnostic of this optimization.
396 final ConstructedConstantValue original; 412 final ConstructedConstantValue original;
397 413
398 /// Reference to the lookup map analysis to be able to refer to data shared 414 /// Reference to the lookup map analysis to be able to refer to data shared
399 /// accross infos. 415 /// accross infos.
400 final LookupMapAnalysis analysis; 416 final LookupMapAnalysisImpl analysis;
401 417
402 /// Whether we have already emitted this constant. 418 /// Whether we have already emitted this constant.
403 bool emitted = false; 419 bool emitted = false;
404 420
405 /// Whether the `LookupMap` constant was built using the `LookupMap.pair` 421 /// Whether the `LookupMap` constant was built using the `LookupMap.pair`
406 /// constructor. 422 /// constructor.
407 bool singlePair; 423 bool singlePair;
408 424
409 /// Entries in the lookup map whose keys have not been seen in the rest of the 425 /// Entries in the lookup map whose keys have not been seen in the rest of the
410 /// program. 426 /// program.
411 Map<ConstantValue, ConstantValue> unusedEntries = 427 Map<ConstantValue, ConstantValue> unusedEntries =
412 <ConstantValue, ConstantValue>{}; 428 <ConstantValue, ConstantValue>{};
413 429
414 /// Entries that have been used, and thus will be part of the generated code. 430 /// Entries that have been used, and thus will be part of the generated code.
415 Map<ConstantValue, ConstantValue> usedEntries = 431 Map<ConstantValue, ConstantValue> usedEntries =
416 <ConstantValue, ConstantValue>{}; 432 <ConstantValue, ConstantValue>{};
417 433
418 /// Creates and initializes the information containing all keys of the 434 /// Creates and initializes the information containing all keys of the
419 /// original map marked as unused. 435 /// original map marked as unused.
420 _LookupMapInfo(this.original, this.analysis) { 436 _LookupMapInfo(this.original, this.analysis) {
421 ConstantValue key = original.fields[analysis.keyField]; 437 ConstantValue key = original.fields[analysis._keyField];
422 singlePair = !key.isNull; 438 singlePair = !key.isNull;
423 439
424 if (singlePair) { 440 if (singlePair) {
425 unusedEntries[key] = original.fields[analysis.valueField]; 441 unusedEntries[key] = original.fields[analysis._valueField];
426 442
427 // Note: we modify the constant in-place, see comment in [original]. 443 // Note: we modify the constant in-place, see comment in [original].
428 original.fields[analysis.keyField] = new NullConstantValue(); 444 original.fields[analysis._keyField] = new NullConstantValue();
429 original.fields[analysis.valueField] = new NullConstantValue(); 445 original.fields[analysis._valueField] = new NullConstantValue();
430 } else { 446 } else {
431 ListConstantValue list = original.fields[analysis.entriesField]; 447 ListConstantValue list = original.fields[analysis._entriesField];
432 List<ConstantValue> keyValuePairs = list.entries; 448 List<ConstantValue> keyValuePairs = list.entries;
433 for (int i = 0; i < keyValuePairs.length; i += 2) { 449 for (int i = 0; i < keyValuePairs.length; i += 2) {
434 ConstantValue key = keyValuePairs[i]; 450 ConstantValue key = keyValuePairs[i];
435 unusedEntries[key] = keyValuePairs[i + 1]; 451 unusedEntries[key] = keyValuePairs[i + 1];
436 } 452 }
437 453
438 // Note: we modify the constant in-place, see comment in [original]. 454 // Note: we modify the constant in-place, see comment in [original].
439 original.fields[analysis.entriesField] = 455 original.fields[analysis._entriesField] =
440 new ListConstantValue(list.type, []); 456 new ListConstantValue(list.type, []);
441 } 457 }
442 } 458 }
443 459
444 /// Check every key in unusedEntries and mark it as used if the analysis has 460 /// Check every key in unusedEntries and mark it as used if the analysis has
445 /// already discovered them. This is meant to be called once to finalize 461 /// already discovered them. This is meant to be called once to finalize
446 /// initialization after constructing an instance of this class. Afterwards, 462 /// initialization after constructing an instance of this class. Afterwards,
447 /// we call [_markUsed] on each individual key as it gets discovered. 463 /// we call [_markUsed] on each individual key as it gets discovered.
448 void _updateUsed() { 464 void _updateUsed() {
449 // Note: we call toList because `_markUsed` modifies the map. 465 // Note: we call toList because `_markUsed` modifies the map.
(...skipping 14 matching lines...) Expand all
464 assert(unusedEntries.containsKey(key)); 480 assert(unusedEntries.containsKey(key));
465 assert(!usedEntries.containsKey(key)); 481 assert(!usedEntries.containsKey(key));
466 ConstantValue constant = unusedEntries.remove(key); 482 ConstantValue constant = unusedEntries.remove(key);
467 usedEntries[key] = constant; 483 usedEntries[key] = constant;
468 analysis._impactBuilder 484 analysis._impactBuilder
469 .registerConstantUse(new ConstantUse.lookupMap(constant)); 485 .registerConstantUse(new ConstantUse.lookupMap(constant));
470 } 486 }
471 487
472 /// Restores [original] to contain all of the entries marked as possibly used. 488 /// Restores [original] to contain all of the entries marked as possibly used.
473 void _prepareForEmission() { 489 void _prepareForEmission() {
474 ListConstantValue originalEntries = original.fields[analysis.entriesField]; 490 ListConstantValue originalEntries = original.fields[analysis._entriesField];
475 ResolutionInterfaceType listType = originalEntries.type; 491 ResolutionInterfaceType listType = originalEntries.type;
476 List<ConstantValue> keyValuePairs = <ConstantValue>[]; 492 List<ConstantValue> keyValuePairs = <ConstantValue>[];
477 usedEntries.forEach((key, value) { 493 usedEntries.forEach((key, value) {
478 keyValuePairs.add(key); 494 keyValuePairs.add(key);
479 keyValuePairs.add(value); 495 keyValuePairs.add(value);
480 }); 496 });
481 497
482 // Note: we are restoring the entries here, see comment in [original]. 498 // Note: we are restoring the entries here, see comment in [original].
483 if (singlePair) { 499 if (singlePair) {
484 assert(keyValuePairs.length == 0 || keyValuePairs.length == 2); 500 assert(keyValuePairs.length == 0 || keyValuePairs.length == 2);
485 if (keyValuePairs.length == 2) { 501 if (keyValuePairs.length == 2) {
486 original.fields[analysis.keyField] = keyValuePairs[0]; 502 original.fields[analysis._keyField] = keyValuePairs[0];
487 original.fields[analysis.valueField] = keyValuePairs[1]; 503 original.fields[analysis._valueField] = keyValuePairs[1];
488 } 504 }
489 } else { 505 } else {
490 original.fields[analysis.entriesField] = 506 original.fields[analysis._entriesField] =
491 new ListConstantValue(listType, keyValuePairs); 507 new ListConstantValue(listType, keyValuePairs);
492 } 508 }
493 } 509 }
494 } 510 }
495 511
496 final _validLookupMapVersionConstraint = new VersionConstraint.parse('^0.0.1'); 512 final _validLookupMapVersionConstraint = new VersionConstraint.parse('^0.0.1');
OLDNEW
« no previous file with comments | « pkg/compiler/lib/src/js_backend/enqueuer.dart ('k') | pkg/compiler/lib/src/js_backend/resolution_listener.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698