| OLD | NEW |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, 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 library tree_ir_builder; | 5 library tree_ir_builder; |
| 6 | 6 |
| 7 import '../common.dart'; | 7 import '../common.dart'; |
| 8 import '../constants/values.dart'; | 8 import '../constants/values.dart'; |
| 9 import '../cps_ir/cps_ir_nodes.dart' as cps_ir; | 9 import '../cps_ir/cps_ir_nodes.dart' as cps_ir; |
| 10 import '../elements/elements.dart'; | 10 import '../elements/elements.dart'; |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 46 * particular, intermediate values and blocks used for local control flow are | 46 * particular, intermediate values and blocks used for local control flow are |
| 47 * still all named. | 47 * still all named. |
| 48 */ | 48 */ |
| 49 class Builder implements cps_ir.Visitor/*<NodeCallback|Node>*/ { | 49 class Builder implements cps_ir.Visitor/*<NodeCallback|Node>*/ { |
| 50 final InternalErrorFunction internalError; | 50 final InternalErrorFunction internalError; |
| 51 | 51 |
| 52 final Map<cps_ir.Primitive, Variable> primitive2variable = | 52 final Map<cps_ir.Primitive, Variable> primitive2variable = |
| 53 <cps_ir.Primitive, Variable>{}; | 53 <cps_ir.Primitive, Variable>{}; |
| 54 final Map<cps_ir.MutableVariable, Variable> mutable2variable = | 54 final Map<cps_ir.MutableVariable, Variable> mutable2variable = |
| 55 <cps_ir.MutableVariable, Variable>{}; | 55 <cps_ir.MutableVariable, Variable>{}; |
| 56 final Set<cps_ir.Constant> inlinedConstants = new Set<cps_ir.Constant>(); |
| 56 | 57 |
| 57 // Continuations with more than one use are replaced with Tree labels. This | 58 // Continuations with more than one use are replaced with Tree labels. This |
| 58 // is the mapping from continuations to labels. | 59 // is the mapping from continuations to labels. |
| 59 final Map<cps_ir.Continuation, Label> labels = <cps_ir.Continuation, Label>{}; | 60 final Map<cps_ir.Continuation, Label> labels = <cps_ir.Continuation, Label>{}; |
| 60 | 61 |
| 61 ExecutableElement currentElement; | 62 ExecutableElement currentElement; |
| 62 /// The 'this' Parameter for currentElement or the enclosing method. | 63 /// The 'this' Parameter for currentElement or the enclosing method. |
| 63 cps_ir.Parameter thisParameter; | 64 cps_ir.Parameter thisParameter; |
| 64 cps_ir.Continuation returnContinuation; | 65 cps_ir.Continuation returnContinuation; |
| 65 | 66 |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 102 return primitive2variable.putIfAbsent(primitive, | 103 return primitive2variable.putIfAbsent(primitive, |
| 103 () => new Variable(currentElement, primitive.hint)); | 104 () => new Variable(currentElement, primitive.hint)); |
| 104 } | 105 } |
| 105 | 106 |
| 106 /// Obtains a reference to the tree Variable corresponding to the IR primitive | 107 /// Obtains a reference to the tree Variable corresponding to the IR primitive |
| 107 /// referred to by [reference]. | 108 /// referred to by [reference]. |
| 108 /// This increments the reference count for the given variable, so the | 109 /// This increments the reference count for the given variable, so the |
| 109 /// returned expression must be used in the tree. | 110 /// returned expression must be used in the tree. |
| 110 Expression getVariableUse(cps_ir.Reference<cps_ir.Primitive> reference) { | 111 Expression getVariableUse(cps_ir.Reference<cps_ir.Primitive> reference) { |
| 111 cps_ir.Primitive prim = reference.definition.effectiveDefinition; | 112 cps_ir.Primitive prim = reference.definition.effectiveDefinition; |
| 113 if (prim is cps_ir.Constant && inlinedConstants.contains(prim)) { |
| 114 return new Constant(prim.value); |
| 115 } |
| 112 if (thisParameter != null && prim == thisParameter) { | 116 if (thisParameter != null && prim == thisParameter) { |
| 113 return new This(); | 117 return new This(); |
| 114 } | 118 } |
| 115 return new VariableUse(getVariable(prim)); | 119 return new VariableUse(getVariable(prim)); |
| 116 } | 120 } |
| 117 | 121 |
| 118 Expression getVariableUseOrNull( | 122 Expression getVariableUseOrNull( |
| 119 cps_ir.Reference<cps_ir.Primitive> reference) { | 123 cps_ir.Reference<cps_ir.Primitive> reference) { |
| 120 return reference == null ? null : getVariableUse(reference); | 124 return reference == null ? null : getVariableUse(reference); |
| 121 } | 125 } |
| (...skipping 150 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 272 return result; | 276 return result; |
| 273 } | 277 } |
| 274 | 278 |
| 275 /// Translates a CPS primitive to a tree expression. | 279 /// Translates a CPS primitive to a tree expression. |
| 276 /// | 280 /// |
| 277 /// This simply calls the visit method for the primitive. | 281 /// This simply calls the visit method for the primitive. |
| 278 translatePrimitive(cps_ir.Primitive prim) { | 282 translatePrimitive(cps_ir.Primitive prim) { |
| 279 return prim.accept(this); | 283 return prim.accept(this); |
| 280 } | 284 } |
| 281 | 285 |
| 286 /************************ CONSTANT COPYING *****************************/ |
| 287 |
| 288 /// Estimate of the number of characters needed to emit a use of the given |
| 289 /// constant. |
| 290 int constantSize(PrimitiveConstantValue value) { |
| 291 // TODO(asgerf): We could interface with the emitter to get the exact size. |
| 292 if (value is StringConstantValue) { |
| 293 // Account for the quotes, but ignore the cost of encoding non-ASCII |
| 294 // characters to avoid traversing the string and depending on encoding. |
| 295 return value.length + 2; |
| 296 } else if (value is BoolConstantValue) { |
| 297 return 2; // Printed as !0 and !1 when minified |
| 298 } else { |
| 299 // TODO(asgerf): Get the exact length of numbers using '1e10' notation. |
| 300 return '${value.primitiveValue}'.length; |
| 301 } |
| 302 } |
| 303 |
| 304 /// The number of uses [prim] has, or `-1` if it is used in a phi assignment. |
| 305 int countNonPhiUses(cps_ir.Primitive prim) { |
| 306 int count = 0; |
| 307 for (cps_ir.Reference ref = prim.firstRef; ref != null; ref = ref.next) { |
| 308 cps_ir.Node use = ref.parent; |
| 309 if (use is cps_ir.InvokeContinuation) { |
| 310 return -1; |
| 311 } |
| 312 count++; |
| 313 } |
| 314 return count; |
| 315 } |
| 316 |
| 317 /// True if the given [constant] should be copied to every use site. |
| 318 bool shouldCopyToUses(cps_ir.Constant constant) { |
| 319 if (!constant.value.isPrimitive) return false; |
| 320 if (constant.hasAtMostOneUse) return true; |
| 321 int uses = countNonPhiUses(constant); |
| 322 if (uses == -1) return false; // Copying might prevent elimination of a phi. |
| 323 int size = constantSize(constant.value); |
| 324 // Compare the expected code size output of copying vs sharing. |
| 325 const int USE = 2; // Minified locals usually have length 2. |
| 326 const int ASSIGN = USE + 2; // Variable and '=' and ';' |
| 327 const int BIAS = 2; // Artificial bias to slightly favor copying. |
| 328 int costOfSharing = USE * uses + size + ASSIGN + BIAS; |
| 329 int costOfCopying = size * uses; |
| 330 return costOfCopying <= costOfSharing; |
| 331 } |
| 332 |
| 282 /************************ INTERIOR EXPRESSIONS ************************/ | 333 /************************ INTERIOR EXPRESSIONS ************************/ |
| 283 // | 334 // |
| 284 // Visit methods for interior expressions must return a function: | 335 // Visit methods for interior expressions must return a function: |
| 285 // | 336 // |
| 286 // (Statement next) => <result statement> | 337 // (Statement next) => <result statement> |
| 287 // | 338 // |
| 288 | 339 |
| 289 NodeCallback visitLetPrim(cps_ir.LetPrim node) { | 340 NodeCallback visitLetPrim(cps_ir.LetPrim node) { |
| 341 if (node.primitive is cps_ir.Constant && shouldCopyToUses(node.primitive)) { |
| 342 inlinedConstants.add(node.primitive); |
| 343 return (Statement next) => next; |
| 344 } |
| 290 Variable variable = getVariable(node.primitive); | 345 Variable variable = getVariable(node.primitive); |
| 291 var value = translatePrimitive(node.primitive); | 346 var value = translatePrimitive(node.primitive); |
| 292 if (value is Expression) { | 347 if (value is Expression) { |
| 293 if (node.primitive.hasAtLeastOneUse) { | 348 if (node.primitive.hasAtLeastOneUse) { |
| 294 return (Statement next) => Assign.makeStatement(variable, value, next); | 349 return (Statement next) => Assign.makeStatement(variable, value, next); |
| 295 } else { | 350 } else { |
| 296 return (Statement next) => new ExpressionStatement(value, next); | 351 return (Statement next) => new ExpressionStatement(value, next); |
| 297 } | 352 } |
| 298 } else { | 353 } else { |
| 299 assert(value is NodeCallback); | 354 assert(value is NodeCallback); |
| (...skipping 399 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 699 internalError(CURRENT_ELEMENT_SPANNABLE, 'Unexpected IR node: $node'); | 754 internalError(CURRENT_ELEMENT_SPANNABLE, 'Unexpected IR node: $node'); |
| 700 } | 755 } |
| 701 | 756 |
| 702 visitFunctionDefinition(cps_ir.FunctionDefinition node) { | 757 visitFunctionDefinition(cps_ir.FunctionDefinition node) { |
| 703 unexpectedNode(node); | 758 unexpectedNode(node); |
| 704 } | 759 } |
| 705 visitParameter(cps_ir.Parameter node) => unexpectedNode(node); | 760 visitParameter(cps_ir.Parameter node) => unexpectedNode(node); |
| 706 visitContinuation(cps_ir.Continuation node) => unexpectedNode(node); | 761 visitContinuation(cps_ir.Continuation node) => unexpectedNode(node); |
| 707 visitMutableVariable(cps_ir.MutableVariable node) => unexpectedNode(node); | 762 visitMutableVariable(cps_ir.MutableVariable node) => unexpectedNode(node); |
| 708 } | 763 } |
| OLD | NEW |