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

Side by Side Diff: pkg/compiler/lib/src/ssa/locals_handler.dart

Issue 2283443002: Make LocalsHandler depend on GraphBuilder instead of SsaBuilder. (Closed)
Patch Set: fix tests Created 4 years, 3 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
« no previous file with comments | « pkg/compiler/lib/src/ssa/graph_builder.dart ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
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.
4
5 import '../closure.dart';
6 import '../common.dart';
7 import '../compiler.dart' show Compiler;
8 import '../dart_types.dart';
9 import '../elements/elements.dart';
10 import '../io/source_information.dart';
11 import '../js/js.dart' as js;
12 import '../js_backend/js_backend.dart';
13 import '../native/native.dart' as native;
14 import '../tree/tree.dart' as ast;
15 import '../types/types.dart';
16 import '../world.dart' show ClassWorld;
17 import 'builder.dart' show SyntheticLocal;
18 import 'graph_builder.dart';
19 import 'nodes.dart';
20 import 'types.dart';
21
22 /// Keeps track of locals (including parameters and phis) when building. The
23 /// 'this' reference is treated as parameter and hence handled by this class,
24 /// too.
25 class LocalsHandler {
26 /// The values of locals that can be directly accessed (without redirections
27 /// to boxes or closure-fields).
28 ///
29 /// [directLocals] is iterated, so it is "insertion ordered" to make the
30 /// iteration order a function only of insertions and not a function of
31 /// e.g. Element hash codes. I'd prefer to use a SortedMap but some elements
32 /// don't have source locations for [Elements.compareByPosition].
33 Map<Local, HInstruction> directLocals = new Map<Local, HInstruction>();
34 Map<Local, CapturedVariable> redirectionMapping =
35 new Map<Local, CapturedVariable>();
36 final GraphBuilder builder;
37 ClosureClassMap closureData;
38 Map<TypeVariableType, TypeVariableLocal> typeVariableLocals =
39 new Map<TypeVariableType, TypeVariableLocal>();
40 final ExecutableElement executableContext;
41
42 /// The class that defines the current type environment or null if no type
43 /// variables are in scope.
44 ClassElement get contextClass => executableContext.contextClass;
45
46 /// The type of the current instance, if concrete.
47 ///
48 /// This allows for handling fixed type argument in case of inlining. For
49 /// instance, checking `'foo'` against `String` instead of `T` in `main`:
50 ///
51 /// class Foo<T> {
52 /// T field;
53 /// Foo(this.field);
54 /// }
55 /// main() {
56 /// new Foo<String>('foo');
57 /// }
58 ///
59 /// [instanceType] is not used if it contains type variables, since these
60 /// might not be in scope or from the current instance.
61 ///
62 final InterfaceType instanceType;
63
64 final Compiler compiler;
65
66 LocalsHandler(this.builder, this.executableContext,
67 InterfaceType instanceType, this.compiler)
68 : this.instanceType =
69 instanceType == null || instanceType.containsTypeVariables
70 ? null
71 : instanceType;
72
73 /// Substituted type variables occurring in [type] into the context of
74 /// [contextClass].
75 DartType substInContext(DartType type) {
76 if (contextClass != null) {
77 ClassElement typeContext = Types.getClassContext(type);
78 if (typeContext != null) {
79 type = type.substByContext(contextClass.asInstanceOf(typeContext));
80 }
81 }
82 if (instanceType != null) {
83 type = type.substByContext(instanceType);
84 }
85 return type;
86 }
87
88 get typesTask => compiler.typesTask;
89
90 /// Creates a new [LocalsHandler] based on [other]. We only need to
91 /// copy the [directLocals], since the other fields can be shared
92 /// throughout the AST visit.
93 LocalsHandler.from(LocalsHandler other)
94 : directLocals = new Map<Local, HInstruction>.from(other.directLocals),
95 redirectionMapping = other.redirectionMapping,
96 executableContext = other.executableContext,
97 instanceType = other.instanceType,
98 builder = other.builder,
99 closureData = other.closureData,
100 compiler = other.compiler,
101 activationVariables = other.activationVariables,
102 cachedTypeOfThis = other.cachedTypeOfThis,
103 cachedTypesOfCapturedVariables = other.cachedTypesOfCapturedVariables;
104
105 /// Redirects accesses from element [from] to element [to]. The [to] element
106 /// must be a boxed variable or a variable that is stored in a closure-field.
107 void redirectElement(Local from, CapturedVariable to) {
108 assert(redirectionMapping[from] == null);
109 redirectionMapping[from] = to;
110 assert(isStoredInClosureField(from) || isBoxed(from));
111 }
112
113 HInstruction createBox() {
114 // TODO(floitsch): Clean up this hack. Should we create a box-object by
115 // just creating an empty object literal?
116 JavaScriptBackend backend = compiler.backend;
117 HInstruction box = new HForeignCode(
118 js.js.parseForeignJS('{}'), backend.nonNullType, <HInstruction>[],
119 nativeBehavior: native.NativeBehavior.PURE_ALLOCATION);
120 builder.add(box);
121 return box;
122 }
123
124 /// If the scope (function or loop) [node] has captured variables then this
125 /// method creates a box and sets up the redirections.
126 void enterScope(ast.Node node, Element element) {
127 // See if any variable in the top-scope of the function is captured. If yes
128 // we need to create a box-object.
129 ClosureScope scopeData = closureData.capturingScopes[node];
130 if (scopeData == null) return;
131 HInstruction box;
132 // The scope has captured variables.
133 if (element != null && element.isGenerativeConstructorBody) {
134 // The box is passed as a parameter to a generative
135 // constructor body.
136 JavaScriptBackend backend = compiler.backend;
137 box = builder.addParameter(scopeData.boxElement, backend.nonNullType);
138 } else {
139 box = createBox();
140 }
141 // Add the box to the known locals.
142 directLocals[scopeData.boxElement] = box;
143 // Make sure that accesses to the boxed locals go into the box. We also
144 // need to make sure that parameters are copied into the box if necessary.
145 scopeData.forEachCapturedVariable(
146 (LocalVariableElement from, BoxFieldElement to) {
147 // The [from] can only be a parameter for function-scopes and not
148 // loop scopes.
149 if (from.isRegularParameter && !element.isGenerativeConstructorBody) {
150 // Now that the redirection is set up, the update to the local will
151 // write the parameter value into the box.
152 // Store the captured parameter in the box. Get the current value
153 // before we put the redirection in place.
154 // We don't need to update the local for a generative
155 // constructor body, because it receives a box that already
156 // contains the updates as the last parameter.
157 HInstruction instruction = readLocal(from);
158 redirectElement(from, to);
159 updateLocal(from, instruction);
160 } else {
161 redirectElement(from, to);
162 }
163 });
164 }
165
166 /// Replaces the current box with a new box and copies over the given list
167 /// of elements from the old box into the new box.
168 void updateCaptureBox(
169 BoxLocal boxElement, List<LocalVariableElement> toBeCopiedElements) {
170 // Create a new box and copy over the values from the old box into the
171 // new one.
172 HInstruction oldBox = readLocal(boxElement);
173 HInstruction newBox = createBox();
174 for (LocalVariableElement boxedVariable in toBeCopiedElements) {
175 // [readLocal] uses the [boxElement] to find its box. By replacing it
176 // behind its back we can still get to the old values.
177 updateLocal(boxElement, oldBox);
178 HInstruction oldValue = readLocal(boxedVariable);
179 updateLocal(boxElement, newBox);
180 updateLocal(boxedVariable, oldValue);
181 }
182 updateLocal(boxElement, newBox);
183 }
184
185 /// Documentation wanted -- johnniwinther
186 ///
187 /// Invariant: [function] must be an implementation element.
188 void startFunction(AstElement element, ast.Node node) {
189 assert(invariant(element, element.isImplementation));
190 closureData = compiler.closureToClassMapper
191 .computeClosureToClassMapping(element.resolvedAst);
192
193 if (element is FunctionElement) {
194 FunctionElement functionElement = element;
195 FunctionSignature params = functionElement.functionSignature;
196 ClosureScope scopeData = closureData.capturingScopes[node];
197 params.orderedForEachParameter((ParameterElement parameterElement) {
198 if (element.isGenerativeConstructorBody) {
199 if (scopeData != null &&
200 scopeData.isCapturedVariable(parameterElement)) {
201 // The parameter will be a field in the box passed as the
202 // last parameter. So no need to have it.
203 return;
204 }
205 }
206 HInstruction parameter = builder.addParameter(parameterElement,
207 TypeMaskFactory.inferredTypeForElement(parameterElement, compiler));
208 builder.parameters[parameterElement] = parameter;
209 directLocals[parameterElement] = parameter;
210 });
211 }
212
213 enterScope(node, element);
214
215 // If the freeVariableMapping is not empty, then this function was a
216 // nested closure that captures variables. Redirect the captured
217 // variables to fields in the closure.
218 closureData.forEachFreeVariable((Local from, CapturedVariable to) {
219 redirectElement(from, to);
220 });
221 JavaScriptBackend backend = compiler.backend;
222 if (closureData.isClosure) {
223 // Inside closure redirect references to itself to [:this:].
224 HThis thisInstruction =
225 new HThis(closureData.thisLocal, backend.nonNullType);
226 builder.graph.thisInstruction = thisInstruction;
227 builder.graph.entry.addAtEntry(thisInstruction);
228 updateLocal(closureData.closureElement, thisInstruction);
229 } else if (element.isInstanceMember) {
230 // Once closures have been mapped to classes their instance members might
231 // not have any thisElement if the closure was created inside a static
232 // context.
233 HThis thisInstruction = new HThis(closureData.thisLocal, getTypeOfThis());
234 builder.graph.thisInstruction = thisInstruction;
235 builder.graph.entry.addAtEntry(thisInstruction);
236 directLocals[closureData.thisLocal] = thisInstruction;
237 }
238
239 // If this method is an intercepted method, add the extra
240 // parameter to it, that is the actual receiver for intercepted
241 // classes, or the same as [:this:] for non-intercepted classes.
242 ClassElement cls = element.enclosingClass;
243
244 // When the class extends a native class, the instance is pre-constructed
245 // and passed to the generative constructor factory function as a parameter.
246 // Instead of allocating and initializing the object, the constructor
247 // 'upgrades' the native subclass object by initializing the Dart fields.
248 bool isNativeUpgradeFactory =
249 element.isGenerativeConstructor && backend.isNativeOrExtendsNative(cls);
250 if (backend.isInterceptedMethod(element)) {
251 bool isInterceptorClass = backend.isInterceptorClass(cls.declaration);
252 String name = isInterceptorClass ? 'receiver' : '_';
253 SyntheticLocal parameter = new SyntheticLocal(name, executableContext);
254 HParameterValue value = new HParameterValue(parameter, getTypeOfThis());
255 builder.graph.explicitReceiverParameter = value;
256 builder.graph.entry.addAfter(directLocals[closureData.thisLocal], value);
257 if (builder.lastAddedParameter == null) {
258 // If this is the first parameter inserted, make sure it stays first.
259 builder.lastAddedParameter = value;
260 }
261 if (isInterceptorClass) {
262 // Only use the extra parameter in intercepted classes.
263 directLocals[closureData.thisLocal] = value;
264 }
265 } else if (isNativeUpgradeFactory) {
266 SyntheticLocal parameter =
267 new SyntheticLocal('receiver', executableContext);
268 // Unlike `this`, receiver is nullable since direct calls to generative
269 // constructor call the constructor with `null`.
270 ClassWorld classWorld = compiler.world;
271 HParameterValue value =
272 new HParameterValue(parameter, new TypeMask.exact(cls, classWorld));
273 builder.graph.explicitReceiverParameter = value;
274 builder.graph.entry.addAtEntry(value);
275 }
276 }
277
278 /// Returns true if the local can be accessed directly. Boxed variables or
279 /// captured variables that are stored in the closure-field return [:false:].
280 bool isAccessedDirectly(Local local) {
281 assert(local != null);
282 return !redirectionMapping.containsKey(local) &&
283 !closureData.variablesUsedInTryOrGenerator.contains(local);
284 }
285
286 bool isStoredInClosureField(Local local) {
287 assert(local != null);
288 if (isAccessedDirectly(local)) return false;
289 CapturedVariable redirectTarget = redirectionMapping[local];
290 if (redirectTarget == null) return false;
291 return redirectTarget is ClosureFieldElement;
292 }
293
294 bool isBoxed(Local local) {
295 if (isAccessedDirectly(local)) return false;
296 if (isStoredInClosureField(local)) return false;
297 return redirectionMapping.containsKey(local);
298 }
299
300 bool isUsedInTryOrGenerator(Local local) {
301 return closureData.variablesUsedInTryOrGenerator.contains(local);
302 }
303
304 /// Returns an [HInstruction] for the given element. If the element is
305 /// boxed or stored in a closure then the method generates code to retrieve
306 /// the value.
307 HInstruction readLocal(Local local, {SourceInformation sourceInformation}) {
308 if (isAccessedDirectly(local)) {
309 if (directLocals[local] == null) {
310 if (local is TypeVariableElement) {
311 compiler.reporter.internalError(compiler.currentElement,
312 "Runtime type information not available for $local.");
313 } else {
314 compiler.reporter.internalError(
315 local, "Cannot find value $local in ${directLocals.keys}.");
316 }
317 }
318 HInstruction value = directLocals[local];
319 if (sourceInformation != null) {
320 value = new HRef(value, sourceInformation);
321 builder.add(value);
322 }
323 return value;
324 } else if (isStoredInClosureField(local)) {
325 ClosureFieldElement redirect = redirectionMapping[local];
326 HInstruction receiver = readLocal(closureData.closureElement);
327 TypeMask type = local is BoxLocal
328 ? (compiler.backend as JavaScriptBackend).nonNullType
329 : getTypeOfCapturedVariable(redirect);
330 HInstruction fieldGet = new HFieldGet(redirect, receiver, type);
331 builder.add(fieldGet);
332 return fieldGet..sourceInformation = sourceInformation;
333 } else if (isBoxed(local)) {
334 BoxFieldElement redirect = redirectionMapping[local];
335 // In the function that declares the captured variable the box is
336 // accessed as direct local. Inside the nested closure the box is
337 // accessed through a closure-field.
338 // Calling [readLocal] makes sure we generate the correct code to get
339 // the box.
340 HInstruction box = readLocal(redirect.box);
341 HInstruction lookup =
342 new HFieldGet(redirect, box, getTypeOfCapturedVariable(redirect));
343 builder.add(lookup);
344 return lookup..sourceInformation = sourceInformation;
345 } else {
346 assert(isUsedInTryOrGenerator(local));
347 HLocalValue localValue = getLocal(local);
348 HInstruction instruction = new HLocalGet(
349 local,
350 localValue,
351 (compiler.backend as JavaScriptBackend).dynamicType,
352 sourceInformation);
353 builder.add(instruction);
354 return instruction;
355 }
356 }
357
358 HInstruction readThis() {
359 HInstruction res = readLocal(closureData.thisLocal);
360 if (res.instructionType == null) {
361 res.instructionType = getTypeOfThis();
362 }
363 return res;
364 }
365
366 HLocalValue getLocal(Local local, {SourceInformation sourceInformation}) {
367 // If the element is a parameter, we already have a
368 // HParameterValue for it. We cannot create another one because
369 // it could then have another name than the real parameter. And
370 // the other one would not know it is just a copy of the real
371 // parameter.
372 if (local is ParameterElement) {
373 assert(invariant(local, builder.parameters.containsKey(local),
374 message: "No local value for parameter $local in "
375 "${builder.parameters}."));
376 return builder.parameters[local];
377 }
378
379 return activationVariables.putIfAbsent(local, () {
380 JavaScriptBackend backend = compiler.backend;
381 HLocalValue localValue = new HLocalValue(local, backend.nonNullType)
382 ..sourceInformation = sourceInformation;
383 builder.graph.entry.addAtExit(localValue);
384 return localValue;
385 });
386 }
387
388 Local getTypeVariableAsLocal(TypeVariableType type) {
389 return typeVariableLocals.putIfAbsent(type, () {
390 return new TypeVariableLocal(type, executableContext);
391 });
392 }
393
394 /// Sets the [element] to [value]. If the element is boxed or stored in a
395 /// closure then the method generates code to set the value.
396 void updateLocal(Local local, HInstruction value,
397 {SourceInformation sourceInformation}) {
398 if (value is HRef) {
399 HRef ref = value;
400 value = ref.value;
401 }
402 assert(!isStoredInClosureField(local));
403 if (isAccessedDirectly(local)) {
404 directLocals[local] = value;
405 } else if (isBoxed(local)) {
406 BoxFieldElement redirect = redirectionMapping[local];
407 // The box itself could be captured, or be local. A local variable that
408 // is captured will be boxed, but the box itself will be a local.
409 // Inside the closure the box is stored in a closure-field and cannot
410 // be accessed directly.
411 HInstruction box = readLocal(redirect.box);
412 builder.add(new HFieldSet(redirect, box, value)
413 ..sourceInformation = sourceInformation);
414 } else {
415 assert(isUsedInTryOrGenerator(local));
416 HLocalValue localValue = getLocal(local);
417 builder.add(new HLocalSet(local, localValue, value)
418 ..sourceInformation = sourceInformation);
419 }
420 }
421
422 /// This function, startLoop, must be called before visiting any children of
423 /// the loop. In particular it needs to be called before executing the
424 /// initializers.
425 ///
426 /// The [LocalsHandler] will make the boxes and updates at the right moment.
427 /// The builder just needs to call [enterLoopBody] and [enterLoopUpdates]
428 /// (for [ast.For] loops) at the correct places. For phi-handling
429 /// [beginLoopHeader] and [endLoop] must also be called.
430 ///
431 /// The correct place for the box depends on the given loop. In most cases
432 /// the box will be created when entering the loop-body: while, do-while, and
433 /// for-in (assuming the call to [:next:] is inside the body) can always be
434 /// constructed this way.
435 ///
436 /// Things are slightly more complicated for [ast.For] loops. If no declared
437 /// loop variable is boxed then the loop-body approach works here too. If a
438 /// loop-variable is boxed we need to introduce a new box for the
439 /// loop-variable before we enter the initializer so that the initializer
440 /// writes the values into the box. In any case we need to create the box
441 /// before the condition since the condition could box the variable.
442 /// Since the first box is created outside the actual loop we have a second
443 /// location where a box is created: just before the updates. This is
444 /// necessary since updates are considered to be part of the next iteration
445 /// (and can again capture variables).
446 ///
447 /// For example the following Dart code prints 1 3 -- 3 4.
448 ///
449 /// var fs = [];
450 /// for (var i = 0; i < 3; (f() { fs.add(f); print(i); i++; })()) {
451 /// i++;
452 /// }
453 /// print("--");
454 /// for (var i = 0; i < 2; i++) fs[i]();
455 ///
456 /// We solve this by emitting the following code (only for [ast.For] loops):
457 /// <Create box> <== move the first box creation outside the loop.
458 /// <initializer>;
459 /// loop-entry:
460 /// if (!<condition>) goto loop-exit;
461 /// <body>
462 /// <update box> // create a new box and copy the captured loop-variables.
463 /// <updates>
464 /// goto loop-entry;
465 /// loop-exit:
466 void startLoop(ast.Node node) {
467 ClosureScope scopeData = closureData.capturingScopes[node];
468 if (scopeData == null) return;
469 if (scopeData.hasBoxedLoopVariables()) {
470 // If there are boxed loop variables then we set up the box and
471 // redirections already now. This way the initializer can write its
472 // values into the box.
473 // For other loops the box will be created when entering the body.
474 enterScope(node, null);
475 }
476 }
477
478 /// Create phis at the loop entry for local variables (ready for the values
479 /// from the back edge). Populate the phis with the current values.
480 void beginLoopHeader(HBasicBlock loopEntry) {
481 // Create a copy because we modify the map while iterating over it.
482 Map<Local, HInstruction> savedDirectLocals =
483 new Map<Local, HInstruction>.from(directLocals);
484
485 JavaScriptBackend backend = compiler.backend;
486 // Create phis for all elements in the definitions environment.
487 savedDirectLocals.forEach((Local local, HInstruction instruction) {
488 if (isAccessedDirectly(local)) {
489 // We know 'this' cannot be modified.
490 if (local != closureData.thisLocal) {
491 HPhi phi =
492 new HPhi.singleInput(local, instruction, backend.dynamicType);
493 loopEntry.addPhi(phi);
494 directLocals[local] = phi;
495 } else {
496 directLocals[local] = instruction;
497 }
498 }
499 });
500 }
501
502 void enterLoopBody(ast.Node node) {
503 ClosureScope scopeData = closureData.capturingScopes[node];
504 if (scopeData == null) return;
505 // If there are no declared boxed loop variables then we did not create the
506 // box before the initializer and we have to create the box now.
507 if (!scopeData.hasBoxedLoopVariables()) {
508 enterScope(node, null);
509 }
510 }
511
512 void enterLoopUpdates(ast.Node node) {
513 // If there are declared boxed loop variables then the updates might have
514 // access to the box and we must switch to a new box before executing the
515 // updates.
516 // In all other cases a new box will be created when entering the body of
517 // the next iteration.
518 ClosureScope scopeData = closureData.capturingScopes[node];
519 if (scopeData == null) return;
520 if (scopeData.hasBoxedLoopVariables()) {
521 updateCaptureBox(scopeData.boxElement, scopeData.boxedLoopVariables);
522 }
523 }
524
525 /// Goes through the phis created in beginLoopHeader entry and adds the
526 /// input from the back edge (from the current value of directLocals) to them.
527 void endLoop(HBasicBlock loopEntry) {
528 // If the loop has an aborting body, we don't update the loop
529 // phis.
530 if (loopEntry.predecessors.length == 1) return;
531 loopEntry.forEachPhi((HPhi phi) {
532 Local element = phi.sourceElement;
533 HInstruction postLoopDefinition = directLocals[element];
534 phi.addInput(postLoopDefinition);
535 });
536 }
537
538 /// Merge [otherLocals] into this locals handler, creating phi-nodes when
539 /// there is a conflict.
540 /// If a phi node is necessary, it will use this handler's instruction as the
541 /// first input, and the otherLocals instruction as the second.
542 void mergeWith(LocalsHandler otherLocals, HBasicBlock joinBlock) {
543 // If an element is in one map but not the other we can safely
544 // ignore it. It means that a variable was declared in the
545 // block. Since variable declarations are scoped the declared
546 // variable cannot be alive outside the block. Note: this is only
547 // true for nodes where we do joins.
548 Map<Local, HInstruction> joinedLocals = new Map<Local, HInstruction>();
549 JavaScriptBackend backend = compiler.backend;
550 otherLocals.directLocals.forEach((Local local, HInstruction instruction) {
551 // We know 'this' cannot be modified.
552 if (local == closureData.thisLocal) {
553 assert(directLocals[local] == instruction);
554 joinedLocals[local] = instruction;
555 } else {
556 HInstruction mine = directLocals[local];
557 if (mine == null) return;
558 if (identical(instruction, mine)) {
559 joinedLocals[local] = instruction;
560 } else {
561 HInstruction phi = new HPhi.manyInputs(
562 local, <HInstruction>[mine, instruction], backend.dynamicType);
563 joinBlock.addPhi(phi);
564 joinedLocals[local] = phi;
565 }
566 }
567 });
568 directLocals = joinedLocals;
569 }
570
571 /// When control flow merges, this method can be used to merge several
572 /// localsHandlers into a new one using phis. The new localsHandler is
573 /// returned. Unless it is also in the list, the current localsHandler is not
574 /// used for its values, only for its declared variables. This is a way to
575 /// exclude local values from the result when they are no longer in scope.
576 LocalsHandler mergeMultiple(
577 List<LocalsHandler> localsHandlers, HBasicBlock joinBlock) {
578 assert(localsHandlers.length > 0);
579 if (localsHandlers.length == 1) return localsHandlers[0];
580 Map<Local, HInstruction> joinedLocals = new Map<Local, HInstruction>();
581 HInstruction thisValue = null;
582 JavaScriptBackend backend = compiler.backend;
583 directLocals.forEach((Local local, HInstruction instruction) {
584 if (local != closureData.thisLocal) {
585 HPhi phi = new HPhi.noInputs(local, backend.dynamicType);
586 joinedLocals[local] = phi;
587 joinBlock.addPhi(phi);
588 } else {
589 // We know that "this" never changes, if it's there.
590 // Save it for later. While merging, there is no phi for "this",
591 // so we don't have to special case it in the merge loop.
592 thisValue = instruction;
593 }
594 });
595 for (LocalsHandler handler in localsHandlers) {
596 handler.directLocals.forEach((Local local, HInstruction instruction) {
597 HPhi phi = joinedLocals[local];
598 if (phi != null) {
599 phi.addInput(instruction);
600 }
601 });
602 }
603 if (thisValue != null) {
604 // If there was a "this" for the scope, add it to the new locals.
605 joinedLocals[closureData.thisLocal] = thisValue;
606 }
607
608 // Remove locals that are not in all handlers.
609 directLocals = new Map<Local, HInstruction>();
610 joinedLocals.forEach((Local local, HInstruction instruction) {
611 if (local != closureData.thisLocal &&
612 instruction.inputs.length != localsHandlers.length) {
613 joinBlock.removePhi(instruction);
614 } else {
615 directLocals[local] = instruction;
616 }
617 });
618 return this;
619 }
620
621 TypeMask cachedTypeOfThis;
622
623 TypeMask getTypeOfThis() {
624 TypeMask result = cachedTypeOfThis;
625 if (result == null) {
626 ThisLocal local = closureData.thisLocal;
627 ClassElement cls = local.enclosingClass;
628 ClassWorld classWorld = compiler.world;
629 if (classWorld.isUsedAsMixin(cls)) {
630 // If the enclosing class is used as a mixin, [:this:] can be
631 // of the class that mixins the enclosing class. These two
632 // classes do not have a subclass relationship, so, for
633 // simplicity, we mark the type as an interface type.
634 result = new TypeMask.nonNullSubtype(cls.declaration, compiler.world);
635 } else {
636 result = new TypeMask.nonNullSubclass(cls.declaration, compiler.world);
637 }
638 cachedTypeOfThis = result;
639 }
640 return result;
641 }
642
643 Map<Element, TypeMask> cachedTypesOfCapturedVariables =
644 new Map<Element, TypeMask>();
645
646 TypeMask getTypeOfCapturedVariable(Element element) {
647 assert(element.isField);
648 return cachedTypesOfCapturedVariables.putIfAbsent(element, () {
649 return TypeMaskFactory.inferredTypeForElement(element, compiler);
650 });
651 }
652
653 /// Variables stored in the current activation. These variables are
654 /// being updated in try/catch blocks, and should be
655 /// accessed indirectly through [HLocalGet] and [HLocalSet].
656 Map<Local, HLocalValue> activationVariables = <Local, HLocalValue>{};
657 }
OLDNEW
« no previous file with comments | « pkg/compiler/lib/src/ssa/graph_builder.dart ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698