| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2013, 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 library dart2js.ir_builder; | |
| 6 | |
| 7 import '../constants/expressions.dart'; | |
| 8 import '../constants/values.dart' show PrimitiveConstantValue; | |
| 9 import '../dart_backend/dart_backend.dart' show DartBackend; | |
| 10 import '../dart_types.dart'; | |
| 11 import '../dart2jslib.dart'; | |
| 12 import '../elements/elements.dart'; | |
| 13 import '../source_file.dart'; | |
| 14 import '../tree/tree.dart' as ast; | |
| 15 import '../scanner/scannerlib.dart' show Token, isUserDefinableOperator; | |
| 16 import '../universe/universe.dart' show SelectorKind; | |
| 17 import 'cps_ir_nodes.dart' as ir; | |
| 18 | |
| 19 part 'cps_ir_builder_visitor.dart'; | |
| 20 | |
| 21 /// A mapping from variable elements to their compile-time values. | |
| 22 /// | |
| 23 /// Map elements denoted by parameters and local variables to the | |
| 24 /// [ir.Primitive] that is their value. Parameters and locals are | |
| 25 /// assigned indexes which can be used to refer to them. | |
| 26 class Environment { | |
| 27 /// A map from elements to their environment index. | |
| 28 final Map<Element, int> variable2index; | |
| 29 | |
| 30 /// A reverse map from environment indexes to the variable. | |
| 31 final List<Element> index2variable; | |
| 32 | |
| 33 /// A map from environment indexes to their value. | |
| 34 final List<ir.Primitive> index2value; | |
| 35 | |
| 36 Environment.empty() | |
| 37 : variable2index = <Element, int>{}, | |
| 38 index2variable = <Element>[], | |
| 39 index2value = <ir.Primitive>[]; | |
| 40 | |
| 41 /// Construct an environment that is a copy of another one. | |
| 42 /// | |
| 43 /// The mapping from elements to indexes is shared, not copied. | |
| 44 Environment.from(Environment other) | |
| 45 : variable2index = other.variable2index, | |
| 46 index2variable = new List<Element>.from(other.index2variable), | |
| 47 index2value = new List<ir.Primitive>.from(other.index2value); | |
| 48 | |
| 49 get length => index2variable.length; | |
| 50 | |
| 51 ir.Primitive operator [](int index) => index2value[index]; | |
| 52 | |
| 53 void extend(Element element, ir.Primitive value) { | |
| 54 // Assert that the name is not already in the environment. `null` is used | |
| 55 // as the name of anonymous variables. Because the variable2index map is | |
| 56 // shared, `null` can already occur. This is safe because such variables | |
| 57 // are not looked up by name. | |
| 58 // | |
| 59 // TODO(kmillikin): This is still kind of fishy. Refactor to not share | |
| 60 // name maps or else garbage collect unneeded names. | |
| 61 assert(element == null || !variable2index.containsKey(element)); | |
| 62 variable2index[element] = index2variable.length; | |
| 63 index2variable.add(element); | |
| 64 index2value.add(value); | |
| 65 } | |
| 66 | |
| 67 ir.Primitive lookup(Element element) { | |
| 68 assert(!element.isConst); | |
| 69 assert(invariant(element, variable2index.containsKey(element), | |
| 70 message: "Unknown variable: $element.")); | |
| 71 return index2value[variable2index[element]]; | |
| 72 } | |
| 73 | |
| 74 void update(Element element, ir.Primitive value) { | |
| 75 index2value[variable2index[element]] = value; | |
| 76 } | |
| 77 | |
| 78 /// Verify that the variable2index and index2variable maps agree up to the | |
| 79 /// index [length] exclusive. | |
| 80 bool sameDomain(int length, Environment other) { | |
| 81 assert(this.length >= length); | |
| 82 assert(other.length >= length); | |
| 83 for (int i = 0; i < length; ++i) { | |
| 84 // An index maps to the same variable in both environments. | |
| 85 Element variable = index2variable[i]; | |
| 86 if (variable != other.index2variable[i]) return false; | |
| 87 | |
| 88 // The variable maps to the same index in both environments. | |
| 89 int index = variable2index[variable]; | |
| 90 if (index == null || index != other.variable2index[variable]) { | |
| 91 return false; | |
| 92 } | |
| 93 } | |
| 94 return true; | |
| 95 } | |
| 96 } | |
| 97 | |
| 98 /// A class to collect breaks or continues. | |
| 99 /// | |
| 100 /// When visiting a potential target of breaks or continues, any breaks or | |
| 101 /// continues are collected by a JumpCollector and processed later, on demand. | |
| 102 /// The site of the break or continue is represented by a continuation | |
| 103 /// invocation that will have its target and arguments filled in later. | |
| 104 /// | |
| 105 /// The environment of the builder at that point is captured and should not | |
| 106 /// be subsequently mutated until the jump is resolved. | |
| 107 class JumpCollector { | |
| 108 final JumpTarget target; | |
| 109 final List<ir.InvokeContinuation> _invocations = <ir.InvokeContinuation>[]; | |
| 110 final List<Environment> _environments = <Environment>[]; | |
| 111 | |
| 112 JumpCollector(this.target); | |
| 113 | |
| 114 bool get isEmpty => _invocations.isEmpty; | |
| 115 int get length => _invocations.length; | |
| 116 List<ir.InvokeContinuation> get invocations => _invocations; | |
| 117 List<Environment> get environments => _environments; | |
| 118 | |
| 119 void addJump(IrBuilder builder) { | |
| 120 ir.InvokeContinuation invoke = new ir.InvokeContinuation.uninitialized(); | |
| 121 builder.add(invoke); | |
| 122 _invocations.add(invoke); | |
| 123 _environments.add(builder.environment); | |
| 124 builder._current = null; | |
| 125 // TODO(kmillikin): Can we set builder.environment to null to make it | |
| 126 // less likely to mutate it? | |
| 127 } | |
| 128 } | |
| 129 | |
| 130 /// Function for building nodes in the context of the provided [builder]. | |
| 131 typedef ir.Node SubbuildFunction(IrBuilder builder); | |
| 132 | |
| 133 /// Mixin that provides encapsulated access to nested builders. | |
| 134 abstract class IrBuilderMixin<N> { | |
| 135 IrBuilder _irBuilder; | |
| 136 | |
| 137 /// Execute [f] with [builder] as the current builder. | |
| 138 withBuilder(IrBuilder builder, f()) { | |
| 139 assert(builder != null); | |
| 140 IrBuilder prev = _irBuilder; | |
| 141 _irBuilder = builder; | |
| 142 var result = f(); | |
| 143 _irBuilder = prev; | |
| 144 return result; | |
| 145 } | |
| 146 | |
| 147 /// The current builder. | |
| 148 IrBuilder get irBuilder { | |
| 149 assert(_irBuilder != null); | |
| 150 return _irBuilder; | |
| 151 } | |
| 152 | |
| 153 /// Visits the [node]. | |
| 154 ir.Primitive visit(N node); | |
| 155 | |
| 156 /// Builds and returns the [ir.Node] for [node] or returns `null` if | |
| 157 /// [node] is `null`. | |
| 158 ir.Node build(N node) => node != null ? visit(node) : null; | |
| 159 | |
| 160 /// Returns a closure that takes an [IrBuilder] and builds [node] in its | |
| 161 /// context using [build]. | |
| 162 SubbuildFunction subbuild(N node) { | |
| 163 return (IrBuilder builder) => withBuilder(builder, () => build(node)); | |
| 164 } | |
| 165 } | |
| 166 | |
| 167 /// Shared state between nested builders. | |
| 168 class IrBuilderSharedState { | |
| 169 final ConstantSystem constantSystem; | |
| 170 | |
| 171 /// A stack of collectors for breaks. | |
| 172 final List<JumpCollector> breakCollectors = <JumpCollector>[]; | |
| 173 | |
| 174 /// A stack of collectors for continues. | |
| 175 final List<JumpCollector> continueCollectors = <JumpCollector>[]; | |
| 176 | |
| 177 final List<ConstDeclaration> localConstants = <ConstDeclaration>[]; | |
| 178 | |
| 179 final Iterable<Entity> closureLocals; | |
| 180 | |
| 181 final FunctionElement currentFunction; | |
| 182 | |
| 183 final ir.Continuation returnContinuation = new ir.Continuation.retrn(); | |
| 184 | |
| 185 IrBuilderSharedState(this.constantSystem, | |
| 186 this.currentFunction, | |
| 187 this.closureLocals); | |
| 188 } | |
| 189 | |
| 190 /// A factory for building the cps IR. | |
| 191 class IrBuilder { | |
| 192 // TODO(johnniwinther): Make these field final and remove the default values | |
| 193 // when [IrBuilder] is a property of [IrBuilderVisitor] instead of a mixin. | |
| 194 | |
| 195 final List<ir.Parameter> _parameters = <ir.Parameter>[]; | |
| 196 | |
| 197 final IrBuilderSharedState state; | |
| 198 | |
| 199 /// A map from variable indexes to their values. | |
| 200 Environment environment; | |
| 201 | |
| 202 // The IR builder maintains a context, which is an expression with a hole in | |
| 203 // it. The hole represents the focus where new expressions can be added. | |
| 204 // The context is implemented by 'root' which is the root of the expression | |
| 205 // and 'current' which is the expression that immediately contains the hole. | |
| 206 // Not all expressions have a hole (e.g., invocations, which always occur in | |
| 207 // tail position, do not have a hole). Expressions with a hole have a plug | |
| 208 // method. | |
| 209 // | |
| 210 // Conceptually, visiting a statement takes a context as input and returns | |
| 211 // either a new context or else an expression without a hole if all | |
| 212 // control-flow paths through the statement have exited. An expression | |
| 213 // without a hole is represented by a (root, current) pair where root is the | |
| 214 // expression and current is null. | |
| 215 // | |
| 216 // Conceptually again, visiting an expression takes a context as input and | |
| 217 // returns either a pair of a new context and a definition denoting | |
| 218 // the expression's value, or else an expression without a hole if all | |
| 219 // control-flow paths through the expression have exited. | |
| 220 // | |
| 221 // We do not pass contexts as arguments or return them. Rather we use the | |
| 222 // current context (root, current) as the visitor state and mutate current. | |
| 223 // Visiting a statement returns null; visiting an expression returns the | |
| 224 // primitive denoting its value. | |
| 225 | |
| 226 ir.Expression _root = null; | |
| 227 ir.Expression _current = null; | |
| 228 | |
| 229 IrBuilder(ConstantSystem constantSystem, | |
| 230 FunctionElement currentFunction, | |
| 231 Iterable<Entity> closureLocals) | |
| 232 : this.state = new IrBuilderSharedState( | |
| 233 constantSystem, currentFunction, closureLocals), | |
| 234 this.environment = new Environment.empty(); | |
| 235 | |
| 236 /// Construct a delimited visitor for visiting a subtree. | |
| 237 /// | |
| 238 /// The delimited visitor has its own compile-time environment mapping | |
| 239 /// local variables to their values, which is initially a copy of the parent | |
| 240 /// environment. It has its own context for building an IR expression, so | |
| 241 /// the built expression is not plugged into the parent's context. | |
| 242 IrBuilder.delimited(IrBuilder parent) | |
| 243 : this.state = parent.state, | |
| 244 this.environment = new Environment.from(parent.environment); | |
| 245 | |
| 246 /// Construct a visitor for a recursive continuation. | |
| 247 /// | |
| 248 /// The recursive continuation builder has fresh parameters (i.e. SSA phis) | |
| 249 /// for all the local variables in the parent, because the invocation sites | |
| 250 /// of the continuation are not all known when the builder is created. The | |
| 251 /// recursive invocations will be passed values for all the local variables, | |
| 252 /// which may be eliminated later if they are redundant---if they take on | |
| 253 /// the same value at all invocation sites. | |
| 254 IrBuilder.recursive(IrBuilder parent) | |
| 255 : this.state = parent.state, | |
| 256 this.environment = new Environment.empty() { | |
| 257 parent.environment.index2variable.forEach(createParameter); | |
| 258 } | |
| 259 | |
| 260 | |
| 261 bool get isOpen => _root == null || _current != null; | |
| 262 | |
| 263 /// Create a parameter for [parameterElement] and add it to the current | |
| 264 /// environment. | |
| 265 /// | |
| 266 /// [isClosureVariable] marks whether [parameterElement] is accessed from an | |
| 267 /// inner function. | |
| 268 void createParameter(LocalElement parameterElement, | |
| 269 {bool isClosureVariable: false}) { | |
| 270 ir.Parameter parameter = new ir.Parameter(parameterElement); | |
| 271 _parameters.add(parameter); | |
| 272 if (isClosureVariable) { | |
| 273 add(new ir.SetClosureVariable(parameterElement, parameter)); | |
| 274 } else { | |
| 275 environment.extend(parameterElement, parameter); | |
| 276 } | |
| 277 } | |
| 278 | |
| 279 /// Add the constant [variableElement] to the environment with [value] as its | |
| 280 /// constant value. | |
| 281 void declareLocalConstant(LocalVariableElement variableElement, | |
| 282 ConstantExpression value) { | |
| 283 state.localConstants.add(new ConstDeclaration(variableElement, value)); | |
| 284 } | |
| 285 | |
| 286 /// Add [variableElement] to the environment with [initialValue] as its | |
| 287 /// initial value. | |
| 288 /// | |
| 289 /// [isClosureVariable] marks whether [variableElement] is accessed from an | |
| 290 /// inner function. | |
| 291 void declareLocalVariable(LocalVariableElement variableElement, | |
| 292 {ir.Primitive initialValue, | |
| 293 bool isClosureVariable: false}) { | |
| 294 assert(isOpen); | |
| 295 if (initialValue == null) { | |
| 296 // TODO(kmillikin): Consider pooling constants. | |
| 297 // The initial value is null. | |
| 298 initialValue = buildNullLiteral(); | |
| 299 } | |
| 300 if (isClosureVariable) { | |
| 301 add(new ir.SetClosureVariable(variableElement, | |
| 302 initialValue, | |
| 303 isDeclaration: true)); | |
| 304 } else { | |
| 305 // In case a primitive was introduced for the initializer expression, | |
| 306 // use this variable element to help derive a good name for it. | |
| 307 initialValue.useElementAsHint(variableElement); | |
| 308 environment.extend(variableElement, initialValue); | |
| 309 } | |
| 310 } | |
| 311 | |
| 312 // Plug an expression into the 'hole' in the context being accumulated. The | |
| 313 // empty context (just a hole) is represented by root (and current) being | |
| 314 // null. Since the hole in the current context is filled by this function, | |
| 315 // the new hole must be in the newly added expression---which becomes the | |
| 316 // new value of current. | |
| 317 void add(ir.Expression expr) { | |
| 318 assert(isOpen); | |
| 319 if (_root == null) { | |
| 320 _root = _current = expr; | |
| 321 } else { | |
| 322 _current = _current.plug(expr); | |
| 323 } | |
| 324 } | |
| 325 | |
| 326 ir.Primitive continueWithExpression(ir.Expression build(ir.Continuation k)) { | |
| 327 ir.Parameter v = new ir.Parameter(null); | |
| 328 ir.Continuation k = new ir.Continuation([v]); | |
| 329 ir.Expression expression = build(k); | |
| 330 add(new ir.LetCont(k, expression)); | |
| 331 return v; | |
| 332 } | |
| 333 | |
| 334 /// Create a constant literal from [constant]. | |
| 335 ir.Constant buildConstantLiteral(ConstantExpression constant) { | |
| 336 assert(isOpen); | |
| 337 ir.Constant prim = new ir.Constant(constant); | |
| 338 add(new ir.LetPrim(prim)); | |
| 339 return prim; | |
| 340 } | |
| 341 | |
| 342 // Helper for building primitive literals. | |
| 343 ir.Constant _buildPrimitiveConstant(PrimitiveConstantValue constant) { | |
| 344 return buildConstantLiteral(new PrimitiveConstantExpression(constant)); | |
| 345 } | |
| 346 | |
| 347 /// Create an integer literal. | |
| 348 ir.Constant buildIntegerLiteral(int value) { | |
| 349 return _buildPrimitiveConstant(state.constantSystem.createInt(value)); | |
| 350 } | |
| 351 | |
| 352 /// Create an double literal. | |
| 353 ir.Constant buildDoubleLiteral(double value) { | |
| 354 return _buildPrimitiveConstant(state.constantSystem.createDouble(value)); | |
| 355 } | |
| 356 | |
| 357 /// Create an bool literal. | |
| 358 ir.Constant buildBooleanLiteral(bool value) { | |
| 359 return _buildPrimitiveConstant(state.constantSystem.createBool(value)); | |
| 360 } | |
| 361 | |
| 362 /// Create an null literal. | |
| 363 ir.Constant buildNullLiteral() { | |
| 364 return _buildPrimitiveConstant(state.constantSystem.createNull()); | |
| 365 } | |
| 366 | |
| 367 /// Create a string literal. | |
| 368 ir.Constant buildStringLiteral(String value) { | |
| 369 return _buildPrimitiveConstant( | |
| 370 state.constantSystem.createString(new ast.DartString.literal(value))); | |
| 371 } | |
| 372 | |
| 373 /// Creates a conditional expression with the provided [condition] where the | |
| 374 /// then and else expression are created through the [buildThenExpression] and | |
| 375 /// [buildElseExpression] functions, respectively. | |
| 376 ir.Primitive buildConditional( | |
| 377 ir.Primitive condition, | |
| 378 ir.Primitive buildThenExpression(IrBuilder builder), | |
| 379 ir.Primitive buildElseExpression(IrBuilder builder)) { | |
| 380 | |
| 381 assert(isOpen); | |
| 382 | |
| 383 // The then and else expressions are delimited. | |
| 384 IrBuilder thenBuilder = new IrBuilder.delimited(this); | |
| 385 IrBuilder elseBuilder = new IrBuilder.delimited(this); | |
| 386 ir.Primitive thenValue = buildThenExpression(thenBuilder); | |
| 387 ir.Primitive elseValue = buildElseExpression(elseBuilder); | |
| 388 | |
| 389 // Treat the values of the subexpressions as named values in the | |
| 390 // environment, so they will be treated as arguments to the join-point | |
| 391 // continuation. | |
| 392 assert(environment.length == thenBuilder.environment.length); | |
| 393 assert(environment.length == elseBuilder.environment.length); | |
| 394 thenBuilder.environment.extend(null, thenValue); | |
| 395 elseBuilder.environment.extend(null, elseValue); | |
| 396 JumpCollector jumps = new JumpCollector(null); | |
| 397 jumps.addJump(thenBuilder); | |
| 398 jumps.addJump(elseBuilder); | |
| 399 ir.Continuation joinContinuation = | |
| 400 createJoin(environment.length + 1, jumps); | |
| 401 | |
| 402 // Build the term | |
| 403 // let cont join(x, ..., result) = [] in | |
| 404 // let cont then() = [[thenPart]]; join(v, ...) in | |
| 405 // let cont else() = [[elsePart]]; join(v, ...) in | |
| 406 // if condition (then, else) | |
| 407 ir.Continuation thenContinuation = new ir.Continuation([]); | |
| 408 ir.Continuation elseContinuation = new ir.Continuation([]); | |
| 409 thenContinuation.body = thenBuilder._root; | |
| 410 elseContinuation.body = elseBuilder._root; | |
| 411 add(new ir.LetCont(joinContinuation, | |
| 412 new ir.LetCont(thenContinuation, | |
| 413 new ir.LetCont(elseContinuation, | |
| 414 new ir.Branch(new ir.IsTrue(condition), | |
| 415 thenContinuation, | |
| 416 elseContinuation))))); | |
| 417 return (thenValue == elseValue) | |
| 418 ? thenValue | |
| 419 : joinContinuation.parameters.last; | |
| 420 | |
| 421 } | |
| 422 | |
| 423 /// Create a get access of [local]. | |
| 424 ir.Primitive buildLocalGet(Element local) { | |
| 425 assert(isOpen); | |
| 426 return environment.lookup(local); | |
| 427 } | |
| 428 | |
| 429 /// Create a get access of the static [element]. | |
| 430 ir.Primitive buildStaticGet(Element element, Selector selector) { | |
| 431 assert(isOpen); | |
| 432 assert(selector.isGetter); | |
| 433 return continueWithExpression( | |
| 434 (k) => new ir.InvokeStatic( | |
| 435 element, selector, k, const <ir.Definition>[])); | |
| 436 } | |
| 437 | |
| 438 /// Create a dynamic get access on [receiver] where the property is defined | |
| 439 /// by the getter [selector]. | |
| 440 ir.Primitive buildDynamicGet(ir.Primitive receiver, Selector selector) { | |
| 441 assert(isOpen); | |
| 442 assert(selector.isGetter); | |
| 443 return continueWithExpression( | |
| 444 (k) => new ir.InvokeMethod( | |
| 445 receiver, selector, k, const <ir.Definition>[])); | |
| 446 } | |
| 447 | |
| 448 /** | |
| 449 * Add an explicit `return null` for functions that don't have a return | |
| 450 * statement on each branch. This includes functions with an empty body, | |
| 451 * such as `foo(){ }`. | |
| 452 */ | |
| 453 void ensureReturn() { | |
| 454 if (!isOpen) return; | |
| 455 ir.Constant constant = buildNullLiteral(); | |
| 456 add(new ir.InvokeContinuation(state.returnContinuation, [constant])); | |
| 457 _current = null; | |
| 458 } | |
| 459 | |
| 460 /// Create a [ir.FunctionDefinition] for [element] using [_root] as the body. | |
| 461 /// | |
| 462 /// Parameters must be created before the construction of the body using | |
| 463 /// [createParameter]. | |
| 464 ir.FunctionDefinition buildFunctionDefinition( | |
| 465 FunctionElement element, | |
| 466 List<ConstantExpression> defaults) { | |
| 467 if (!element.isAbstract) { | |
| 468 ensureReturn(); | |
| 469 return new ir.FunctionDefinition( | |
| 470 element, state.returnContinuation, _parameters, _root, | |
| 471 state.localConstants, defaults); | |
| 472 } else { | |
| 473 assert(invariant(element, _root == null, | |
| 474 message: "Non-empty body for abstract method $element: $_root")); | |
| 475 assert(invariant(element, state.localConstants.isEmpty, | |
| 476 message: "Local constants for abstract method $element: " | |
| 477 "${state.localConstants}")); | |
| 478 return new ir.FunctionDefinition.abstract( | |
| 479 element, _parameters, defaults); | |
| 480 } | |
| 481 } | |
| 482 | |
| 483 | |
| 484 /// Create a super invocation with method name and arguments structure defined | |
| 485 /// by [selector] and argument values defined by [arguments]. | |
| 486 ir.Primitive buildSuperInvocation(Selector selector, | |
| 487 List<ir.Definition> arguments) { | |
| 488 assert(isOpen); | |
| 489 return continueWithExpression( | |
| 490 (k) => new ir.InvokeSuperMethod(selector, k, arguments)); | |
| 491 | |
| 492 } | |
| 493 | |
| 494 /// Create a dynamic invocation on [receiver] with method name and arguments | |
| 495 /// structure defined by [selector] and argument values defined by | |
| 496 /// [arguments]. | |
| 497 ir.Primitive buildDynamicInvocation(ir.Definition receiver, | |
| 498 Selector selector, | |
| 499 List<ir.Definition> arguments) { | |
| 500 assert(isOpen); | |
| 501 return continueWithExpression( | |
| 502 (k) => new ir.InvokeMethod(receiver, selector, k, arguments)); | |
| 503 } | |
| 504 | |
| 505 /// Create a static invocation of [element] with arguments structure defined | |
| 506 /// by [selector] and argument values defined by [arguments]. | |
| 507 ir.Primitive buildStaticInvocation(Element element, | |
| 508 Selector selector, | |
| 509 List<ir.Definition> arguments) { | |
| 510 return continueWithExpression( | |
| 511 (k) => new ir.InvokeStatic(element, selector, k, arguments)); | |
| 512 } | |
| 513 | |
| 514 /// Creates an if-then-else statement with the provided [condition] where the | |
| 515 /// then and else branches are created through the [buildThenPart] and | |
| 516 /// [buildElsePart] functions, respectively. | |
| 517 /// | |
| 518 /// An if-then statement is created if [buildElsePart] is a no-op. | |
| 519 // TODO(johnniwinther): Unify implementation with [buildConditional] and | |
| 520 // [_buildLogicalOperator]. | |
| 521 void buildIf(ir.Primitive condition, | |
| 522 void buildThenPart(IrBuilder builder), | |
| 523 void buildElsePart(IrBuilder builder)) { | |
| 524 assert(isOpen); | |
| 525 | |
| 526 // The then and else parts are delimited. | |
| 527 IrBuilder thenBuilder = new IrBuilder.delimited(this); | |
| 528 IrBuilder elseBuilder = new IrBuilder.delimited(this); | |
| 529 buildThenPart(thenBuilder); | |
| 530 buildElsePart(elseBuilder); | |
| 531 | |
| 532 // Build the term | |
| 533 // (Result =) let cont then() = [[thenPart]] in | |
| 534 // let cont else() = [[elsePart]] in | |
| 535 // if condition (then, else) | |
| 536 ir.Continuation thenContinuation = new ir.Continuation([]); | |
| 537 ir.Continuation elseContinuation = new ir.Continuation([]); | |
| 538 ir.Expression letElse = | |
| 539 new ir.LetCont(elseContinuation, | |
| 540 new ir.Branch(new ir.IsTrue(condition), | |
| 541 thenContinuation, | |
| 542 elseContinuation)); | |
| 543 ir.Expression letThen = new ir.LetCont(thenContinuation, letElse); | |
| 544 ir.Expression result = letThen; | |
| 545 | |
| 546 ir.Continuation joinContinuation; // Null if there is no join. | |
| 547 if (thenBuilder.isOpen && elseBuilder.isOpen) { | |
| 548 // There is a join-point continuation. Build the term | |
| 549 // 'let cont join(x, ...) = [] in Result' and plug invocations of the | |
| 550 // join-point continuation into the then and else continuations. | |
| 551 JumpCollector jumps = new JumpCollector(null); | |
| 552 jumps.addJump(thenBuilder); | |
| 553 jumps.addJump(elseBuilder); | |
| 554 joinContinuation = createJoin(environment.length, jumps); | |
| 555 result = new ir.LetCont(joinContinuation, result); | |
| 556 } | |
| 557 | |
| 558 // The then or else term root could be null, but not both. If there is | |
| 559 // a join then an InvokeContinuation was just added to both of them. If | |
| 560 // there is no join, then at least one of them is closed and thus has a | |
| 561 // non-null root by the definition of the predicate isClosed. In the | |
| 562 // case that one of them is null, it must be the only one that is open | |
| 563 // and thus contains the new hole in the context. This case is handled | |
| 564 // after the branch is plugged into the current hole. | |
| 565 thenContinuation.body = thenBuilder._root; | |
| 566 elseContinuation.body = elseBuilder._root; | |
| 567 | |
| 568 add(result); | |
| 569 if (joinContinuation == null) { | |
| 570 // At least one subexpression is closed. | |
| 571 if (thenBuilder.isOpen) { | |
| 572 _current = | |
| 573 (thenBuilder._root == null) ? letThen : thenBuilder._current; | |
| 574 environment = thenBuilder.environment; | |
| 575 } else if (elseBuilder.isOpen) { | |
| 576 _current = | |
| 577 (elseBuilder._root == null) ? letElse : elseBuilder._current; | |
| 578 environment = elseBuilder.environment; | |
| 579 } else { | |
| 580 _current = null; | |
| 581 } | |
| 582 } | |
| 583 } | |
| 584 | |
| 585 /// Create a return statement `return value;` or `return;` if [value] is | |
| 586 /// null. | |
| 587 void buildReturn([ir.Primitive value]) { | |
| 588 // Build(Return(e), C) = C'[InvokeContinuation(return, x)] | |
| 589 // where (C', x) = Build(e, C) | |
| 590 // | |
| 591 // Return without a subexpression is translated as if it were return null. | |
| 592 assert(isOpen); | |
| 593 if (value == null) { | |
| 594 value = buildNullLiteral(); | |
| 595 } | |
| 596 add(new ir.InvokeContinuation(state.returnContinuation, [value])); | |
| 597 _current = null; | |
| 598 } | |
| 599 | |
| 600 /// Create a blocks of [statements] by applying [build] to all reachable | |
| 601 /// statements. | |
| 602 // TODO(johnniwinther): Type [statements] as `Iterable` when `NodeList` uses | |
| 603 // `List` instead of `Link`. | |
| 604 void buildBlock(var statements, build(statement)) { | |
| 605 // Build(Block(stamements), C) = C' | |
| 606 // where C' = statements.fold(Build, C) | |
| 607 assert(isOpen); | |
| 608 for (var statement in statements) { | |
| 609 build(statement); | |
| 610 if (!isOpen) return; | |
| 611 } | |
| 612 } | |
| 613 | |
| 614 | |
| 615 // Build(BreakStatement L, C) = C[InvokeContinuation(...)] | |
| 616 // | |
| 617 // The continuation and arguments are filled in later after translating | |
| 618 // the body containing the break. | |
| 619 bool buildBreak(JumpTarget target) { | |
| 620 return buildJumpInternal(target, state.breakCollectors); | |
| 621 } | |
| 622 | |
| 623 // Build(ContinueStatement L, C) = C[InvokeContinuation(...)] | |
| 624 // | |
| 625 // The continuation and arguments are filled in later after translating | |
| 626 // the body containing the continue. | |
| 627 bool buildContinue(JumpTarget target) { | |
| 628 return buildJumpInternal(target, state.continueCollectors); | |
| 629 } | |
| 630 | |
| 631 bool buildJumpInternal(JumpTarget target, | |
| 632 Iterable<JumpCollector> collectors) { | |
| 633 assert(isOpen); | |
| 634 for (JumpCollector collector in collectors) { | |
| 635 if (target == collector.target) { | |
| 636 collector.addJump(this); | |
| 637 return true; | |
| 638 } | |
| 639 } | |
| 640 return false; | |
| 641 } | |
| 642 | |
| 643 /// Create a negation of [condition]. | |
| 644 ir.Primitive buildNegation(ir.Primitive condition) { | |
| 645 // ! e is translated as e ? false : true | |
| 646 | |
| 647 // Add a continuation parameter for the result of the expression. | |
| 648 ir.Parameter resultParameter = new ir.Parameter(null); | |
| 649 | |
| 650 ir.Continuation joinContinuation = new ir.Continuation([resultParameter]); | |
| 651 ir.Continuation thenContinuation = new ir.Continuation([]); | |
| 652 ir.Continuation elseContinuation = new ir.Continuation([]); | |
| 653 | |
| 654 ir.Constant makeBoolConstant(bool value) { | |
| 655 return new ir.Constant(new PrimitiveConstantExpression( | |
| 656 state.constantSystem.createBool(value))); | |
| 657 } | |
| 658 | |
| 659 ir.Constant trueConstant = makeBoolConstant(true); | |
| 660 ir.Constant falseConstant = makeBoolConstant(false); | |
| 661 | |
| 662 thenContinuation.body = new ir.LetPrim(falseConstant) | |
| 663 ..plug(new ir.InvokeContinuation(joinContinuation, [falseConstant])); | |
| 664 elseContinuation.body = new ir.LetPrim(trueConstant) | |
| 665 ..plug(new ir.InvokeContinuation(joinContinuation, [trueConstant])); | |
| 666 | |
| 667 add(new ir.LetCont(joinContinuation, | |
| 668 new ir.LetCont(thenContinuation, | |
| 669 new ir.LetCont(elseContinuation, | |
| 670 new ir.Branch(new ir.IsTrue(condition), | |
| 671 thenContinuation, | |
| 672 elseContinuation))))); | |
| 673 return resultParameter; | |
| 674 } | |
| 675 | |
| 676 /// Create a lazy and/or expression. [leftValue] is the value of the left | |
| 677 /// operand and [buildRightValue] is called to process the value of the right | |
| 678 /// operand in the context of its own [IrBuilder]. | |
| 679 ir.Primitive buildLogicalOperator( | |
| 680 ir.Primitive leftValue, | |
| 681 ir.Primitive buildRightValue(IrBuilder builder), | |
| 682 {bool isLazyOr: false}) { | |
| 683 // e0 && e1 is translated as if e0 ? (e1 == true) : false. | |
| 684 // e0 || e1 is translated as if e0 ? true : (e1 == true). | |
| 685 // The translation must convert both e0 and e1 to booleans and handle | |
| 686 // local variable assignments in e1. | |
| 687 | |
| 688 IrBuilder rightBuilder = new IrBuilder.delimited(this); | |
| 689 ir.Primitive rightValue = buildRightValue(rightBuilder); | |
| 690 // A dummy empty target for the branch on the left subexpression branch. | |
| 691 // This enables using the same infrastructure for join-point continuations | |
| 692 // as in visitIf and visitConditional. It will hold a definition of the | |
| 693 // appropriate constant and an invocation of the join-point continuation. | |
| 694 IrBuilder emptyBuilder = new IrBuilder.delimited(this); | |
| 695 // Dummy empty targets for right true and right false. They hold | |
| 696 // definitions of the appropriate constant and an invocation of the | |
| 697 // join-point continuation. | |
| 698 IrBuilder rightTrueBuilder = new IrBuilder.delimited(rightBuilder); | |
| 699 IrBuilder rightFalseBuilder = new IrBuilder.delimited(rightBuilder); | |
| 700 | |
| 701 // If we don't evaluate the right subexpression, the value of the whole | |
| 702 // expression is this constant. | |
| 703 ir.Constant leftBool = emptyBuilder.buildBooleanLiteral(isLazyOr); | |
| 704 // If we do evaluate the right subexpression, the value of the expression | |
| 705 // is a true or false constant. | |
| 706 ir.Constant rightTrue = rightTrueBuilder.buildBooleanLiteral(true); | |
| 707 ir.Constant rightFalse = rightFalseBuilder.buildBooleanLiteral(false); | |
| 708 | |
| 709 // Treat the result values as named values in the environment, so they | |
| 710 // will be treated as arguments to the join-point continuation. | |
| 711 assert(environment.length == emptyBuilder.environment.length); | |
| 712 assert(environment.length == rightTrueBuilder.environment.length); | |
| 713 assert(environment.length == rightFalseBuilder.environment.length); | |
| 714 emptyBuilder.environment.extend(null, leftBool); | |
| 715 rightTrueBuilder.environment.extend(null, rightTrue); | |
| 716 rightFalseBuilder.environment.extend(null, rightFalse); | |
| 717 | |
| 718 // Wire up two continuations for the left subexpression, two continuations | |
| 719 // for the right subexpression, and a three-way join continuation. | |
| 720 JumpCollector jumps = new JumpCollector(null); | |
| 721 jumps.addJump(emptyBuilder); | |
| 722 jumps.addJump(rightTrueBuilder); | |
| 723 jumps.addJump(rightFalseBuilder); | |
| 724 ir.Continuation joinContinuation = | |
| 725 createJoin(environment.length + 1, jumps); | |
| 726 ir.Continuation leftTrueContinuation = new ir.Continuation([]); | |
| 727 ir.Continuation leftFalseContinuation = new ir.Continuation([]); | |
| 728 ir.Continuation rightTrueContinuation = new ir.Continuation([]); | |
| 729 ir.Continuation rightFalseContinuation = new ir.Continuation([]); | |
| 730 rightTrueContinuation.body = rightTrueBuilder._root; | |
| 731 rightFalseContinuation.body = rightFalseBuilder._root; | |
| 732 // The right subexpression has two continuations. | |
| 733 rightBuilder.add( | |
| 734 new ir.LetCont(rightTrueContinuation, | |
| 735 new ir.LetCont(rightFalseContinuation, | |
| 736 new ir.Branch(new ir.IsTrue(rightValue), | |
| 737 rightTrueContinuation, | |
| 738 rightFalseContinuation)))); | |
| 739 // Depending on the operator, the left subexpression's continuations are | |
| 740 // either the right subexpression or an invocation of the join-point | |
| 741 // continuation. | |
| 742 if (isLazyOr) { | |
| 743 leftTrueContinuation.body = emptyBuilder._root; | |
| 744 leftFalseContinuation.body = rightBuilder._root; | |
| 745 } else { | |
| 746 leftTrueContinuation.body = rightBuilder._root; | |
| 747 leftFalseContinuation.body = emptyBuilder._root; | |
| 748 } | |
| 749 | |
| 750 add(new ir.LetCont(joinContinuation, | |
| 751 new ir.LetCont(leftTrueContinuation, | |
| 752 new ir.LetCont(leftFalseContinuation, | |
| 753 new ir.Branch(new ir.IsTrue(leftValue), | |
| 754 leftTrueContinuation, | |
| 755 leftFalseContinuation))))); | |
| 756 // There is always a join parameter for the result value, because it | |
| 757 // is different on at least two paths. | |
| 758 return joinContinuation.parameters.last; | |
| 759 } | |
| 760 | |
| 761 /// Create a non-recursive join-point continuation. | |
| 762 /// | |
| 763 /// Given the environment length at the join point and a list of | |
| 764 /// jumps that should reach the join point, create a join-point | |
| 765 /// continuation. The join-point continuation has a parameter for each | |
| 766 /// variable that has different values reaching on different paths. | |
| 767 /// | |
| 768 /// The jumps are uninitialized [ir.InvokeContinuation] expressions. | |
| 769 /// They are filled in with the target continuation and appropriate | |
| 770 /// arguments. | |
| 771 /// | |
| 772 /// As a side effect, the environment of this builder is updated to include | |
| 773 /// the join-point continuation parameters. | |
| 774 ir.Continuation createJoin(int environmentLength, JumpCollector jumps) { | |
| 775 assert(jumps.length >= 2); | |
| 776 | |
| 777 // Compute which values are identical on all paths reaching the join. | |
| 778 // Handle the common case of a pair of contexts efficiently. | |
| 779 Environment first = jumps.environments[0]; | |
| 780 Environment second = jumps.environments[1]; | |
| 781 assert(environmentLength <= first.length); | |
| 782 assert(environmentLength <= second.length); | |
| 783 assert(first.sameDomain(environmentLength, second)); | |
| 784 // A running count of the join-point parameters. | |
| 785 int parameterCount = 0; | |
| 786 // The null elements of common correspond to required parameters of the | |
| 787 // join-point continuation. | |
| 788 List<ir.Primitive> common = | |
| 789 new List<ir.Primitive>.generate(environmentLength, | |
| 790 (i) { | |
| 791 ir.Primitive candidate = first[i]; | |
| 792 if (second[i] == candidate) { | |
| 793 return candidate; | |
| 794 } else { | |
| 795 ++parameterCount; | |
| 796 return null; | |
| 797 } | |
| 798 }); | |
| 799 // If there is already a parameter for each variable, the other | |
| 800 // environments do not need to be considered. | |
| 801 if (parameterCount < environmentLength) { | |
| 802 for (int i = 0; i < environmentLength; ++i) { | |
| 803 ir.Primitive candidate = common[i]; | |
| 804 if (candidate == null) continue; | |
| 805 for (Environment current in jumps.environments.skip(2)) { | |
| 806 assert(environmentLength <= current.length); | |
| 807 assert(first.sameDomain(environmentLength, current)); | |
| 808 if (candidate != current[i]) { | |
| 809 common[i] = null; | |
| 810 ++parameterCount; | |
| 811 break; | |
| 812 } | |
| 813 } | |
| 814 if (parameterCount >= environmentLength) break; | |
| 815 } | |
| 816 } | |
| 817 | |
| 818 // Create the join point continuation. | |
| 819 List<ir.Parameter> parameters = <ir.Parameter>[]; | |
| 820 parameters.length = parameterCount; | |
| 821 int index = 0; | |
| 822 for (int i = 0; i < environmentLength; ++i) { | |
| 823 if (common[i] == null) { | |
| 824 parameters[index++] = new ir.Parameter(first.index2variable[i]); | |
| 825 } | |
| 826 } | |
| 827 assert(index == parameterCount); | |
| 828 ir.Continuation join = new ir.Continuation(parameters); | |
| 829 | |
| 830 // Fill in all the continuation invocations. | |
| 831 for (int i = 0; i < jumps.length; ++i) { | |
| 832 Environment currentEnvironment = jumps.environments[i]; | |
| 833 ir.InvokeContinuation invoke = jumps.invocations[i]; | |
| 834 // Sharing this.environment with one of the invocations will not do | |
| 835 // the right thing (this.environment has already been mutated). | |
| 836 List<ir.Reference> arguments = <ir.Reference>[]; | |
| 837 arguments.length = parameterCount; | |
| 838 int index = 0; | |
| 839 for (int i = 0; i < environmentLength; ++i) { | |
| 840 if (common[i] == null) { | |
| 841 arguments[index++] = new ir.Reference(currentEnvironment[i]); | |
| 842 } | |
| 843 } | |
| 844 invoke.continuation = new ir.Reference(join); | |
| 845 invoke.arguments = arguments; | |
| 846 } | |
| 847 | |
| 848 // Mutate this.environment to be the environment at the join point. Do | |
| 849 // this after adding the continuation invocations, because this.environment | |
| 850 // might be collected by the jump collector and so the old environment | |
| 851 // values are needed for the continuation invocation. | |
| 852 // | |
| 853 // Iterate to environment.length because environmentLength includes values | |
| 854 // outside the environment which are 'phantom' variables used for the | |
| 855 // values of expressions like &&, ||, and ?:. | |
| 856 index = 0; | |
| 857 for (int i = 0; i < environment.length; ++i) { | |
| 858 if (common[i] == null) { | |
| 859 environment.index2value[i] = parameters[index++]; | |
| 860 } | |
| 861 } | |
| 862 | |
| 863 return join; | |
| 864 } | |
| 865 } | |
| OLD | NEW |