| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 library tree_ir_builder; | |
| 6 | |
| 7 import '../common.dart'; | |
| 8 import '../constants/values.dart'; | |
| 9 import '../cps_ir/cps_ir_nodes.dart' as cps_ir; | |
| 10 import '../elements/elements.dart'; | |
| 11 import '../io/source_information.dart'; | |
| 12 import '../js_backend/codegen/glue.dart'; | |
| 13 import 'tree_ir_nodes.dart'; | |
| 14 | |
| 15 typedef Statement NodeCallback(Statement next); | |
| 16 | |
| 17 /** | |
| 18 * Builder translates from CPS-based IR to direct-style Tree. | |
| 19 * | |
| 20 * A call `Invoke(fun, cont, args)`, where cont is a singly-referenced | |
| 21 * non-exit continuation `Cont(v, body)` is translated into a direct-style call | |
| 22 * whose value is bound in the continuation body: | |
| 23 * | |
| 24 * `LetVal(v, Invoke(fun, args), body)` | |
| 25 * | |
| 26 * and the continuation definition is eliminated. A similar translation is | |
| 27 * applied to continuation invocations where the continuation is | |
| 28 * singly-referenced, though such invocations should not appear in optimized | |
| 29 * IR. | |
| 30 * | |
| 31 * A call `Invoke(fun, cont, args)`, where cont is multiply referenced, is | |
| 32 * translated into a call followed by a jump with an argument: | |
| 33 * | |
| 34 * `Jump L(Invoke(fun, args))` | |
| 35 * | |
| 36 * and the continuation is translated into a named block that takes an | |
| 37 * argument: | |
| 38 * | |
| 39 * `LetLabel(L, v, body)` | |
| 40 * | |
| 41 * Block arguments are later replaced with data flow during the Tree-to-Tree | |
| 42 * translation out of SSA. Jumps are eliminated during the Tree-to-Tree | |
| 43 * control-flow recognition. | |
| 44 * | |
| 45 * Otherwise, the output of Builder looks very much like the input. In | |
| 46 * particular, intermediate values and blocks used for local control flow are | |
| 47 * still all named. | |
| 48 */ | |
| 49 class Builder implements cps_ir.Visitor/*<NodeCallback|Node>*/ { | |
| 50 final InternalErrorFunction internalError; | |
| 51 final Glue glue; | |
| 52 | |
| 53 final Map<cps_ir.Primitive, Variable> primitive2variable = | |
| 54 <cps_ir.Primitive, Variable>{}; | |
| 55 final Map<cps_ir.MutableVariable, Variable> mutable2variable = | |
| 56 <cps_ir.MutableVariable, Variable>{}; | |
| 57 final Set<cps_ir.Constant> inlinedConstants = new Set<cps_ir.Constant>(); | |
| 58 | |
| 59 // Continuations with more than one use are replaced with Tree labels. This | |
| 60 // is the mapping from continuations to labels. | |
| 61 final Map<cps_ir.Continuation, Label> labels = <cps_ir.Continuation, Label>{}; | |
| 62 | |
| 63 ExecutableElement currentElement; | |
| 64 | |
| 65 /// The parameter to be translated to 'this'. This can either be the receiver | |
| 66 /// parameter, the interceptor parameter, or null if the method has neither. | |
| 67 cps_ir.Parameter thisParameter; | |
| 68 cps_ir.Continuation returnContinuation; | |
| 69 | |
| 70 Builder(this.internalError, this.glue); | |
| 71 | |
| 72 /// Variable used in [buildPhiAssignments] as a temporary when swapping | |
| 73 /// variables. | |
| 74 Variable phiTempVar; | |
| 75 | |
| 76 Variable addMutableVariable(cps_ir.MutableVariable irVariable) { | |
| 77 assert(!mutable2variable.containsKey(irVariable)); | |
| 78 Variable variable = new Variable(currentElement, irVariable.hint); | |
| 79 mutable2variable[irVariable] = variable; | |
| 80 return variable; | |
| 81 } | |
| 82 | |
| 83 Variable getMutableVariable(cps_ir.MutableVariable mutableVariable) { | |
| 84 return mutable2variable[mutableVariable]; | |
| 85 } | |
| 86 | |
| 87 VariableUse getMutableVariableUse( | |
| 88 cps_ir.Reference<cps_ir.MutableVariable> reference, | |
| 89 SourceInformation sourceInformation) { | |
| 90 Variable variable = getMutableVariable(reference.definition); | |
| 91 return new VariableUse(variable, sourceInformation: sourceInformation); | |
| 92 } | |
| 93 | |
| 94 /// Obtains the variable representing the given primitive. Returns null for | |
| 95 /// primitives that have no reference and do not need a variable. | |
| 96 Variable getVariable(cps_ir.Primitive primitive) { | |
| 97 primitive = primitive.effectiveDefinition; | |
| 98 return primitive2variable.putIfAbsent( | |
| 99 primitive, () => new Variable(currentElement, primitive.hint)); | |
| 100 } | |
| 101 | |
| 102 /// Obtains a reference to the tree Variable corresponding to the IR primitive | |
| 103 /// referred to by [reference]. | |
| 104 /// This increments the reference count for the given variable, so the | |
| 105 /// returned expression must be used in the tree. | |
| 106 Expression getVariableUse(cps_ir.Reference<cps_ir.Primitive> reference, | |
| 107 {SourceInformation sourceInformation}) { | |
| 108 cps_ir.Primitive prim = reference.definition.effectiveDefinition; | |
| 109 if (prim is cps_ir.Constant && inlinedConstants.contains(prim)) { | |
| 110 return new Constant(prim.value); | |
| 111 } | |
| 112 if (thisParameter != null && prim == thisParameter) { | |
| 113 return new This(); | |
| 114 } | |
| 115 return new VariableUse(getVariable(prim), | |
| 116 sourceInformation: sourceInformation); | |
| 117 } | |
| 118 | |
| 119 Expression getVariableUseOrNull( | |
| 120 cps_ir.Reference<cps_ir.Primitive> reference) { | |
| 121 return reference == null ? null : getVariableUse(reference); | |
| 122 } | |
| 123 | |
| 124 Label getLabel(cps_ir.Continuation cont) { | |
| 125 return labels.putIfAbsent(cont, () => new Label()); | |
| 126 } | |
| 127 | |
| 128 FunctionDefinition buildFunction(cps_ir.FunctionDefinition node) { | |
| 129 currentElement = node.element; | |
| 130 List<Variable> parameters = node.parameters.map(getVariable).toList(); | |
| 131 if (node.interceptorParameter != null) { | |
| 132 parameters.insert(0, getVariable(node.receiverParameter)); | |
| 133 thisParameter = glue.methodUsesReceiverArgument(node.element) | |
| 134 ? node.interceptorParameter | |
| 135 : node.receiverParameter; | |
| 136 } else { | |
| 137 thisParameter = node.receiverParameter; | |
| 138 } | |
| 139 returnContinuation = node.returnContinuation; | |
| 140 phiTempVar = new Variable(node.element, null); | |
| 141 Statement body = translateExpression(node.body); | |
| 142 return new FunctionDefinition(node.element, parameters, body, | |
| 143 sourceInformation: node.sourceInformation); | |
| 144 } | |
| 145 | |
| 146 /// Returns a list of variables corresponding to the arguments to a method | |
| 147 /// call or similar construct. | |
| 148 /// | |
| 149 /// The `readCount` for these variables will be incremented. | |
| 150 /// | |
| 151 /// The list will be typed as a list of [Expression] to allow inplace updates | |
| 152 /// on the list during the rewrite phases. | |
| 153 List<Expression> translateArguments(List<cps_ir.Reference> args) { | |
| 154 return new List<Expression>.generate( | |
| 155 args.length, (int index) => getVariableUse(args[index]), | |
| 156 growable: false); | |
| 157 } | |
| 158 | |
| 159 /// Simultaneously assigns each argument to the corresponding parameter, | |
| 160 /// then continues at the statement created by [buildRest]. | |
| 161 Statement buildPhiAssignments(List<cps_ir.Parameter> parameters, | |
| 162 List<Expression> arguments, Statement buildRest()) { | |
| 163 assert(parameters.length == arguments.length); | |
| 164 // We want a parallel assignment to all parameters simultaneously. | |
| 165 // Since we do not have parallel assignments in dart_tree, we must linearize | |
| 166 // the assignments without attempting to read a previously-overwritten | |
| 167 // value. For example {x,y = y,x} cannot be linearized to {x = y; y = x}, | |
| 168 // for this we must introduce a temporary variable: {t = x; x = y; y = t}. | |
| 169 | |
| 170 // [rightHand] is the inverse of [arguments], that is, it maps variables | |
| 171 // to the assignments on which is occurs as the right-hand side. | |
| 172 Map<Variable, List<int>> rightHand = <Variable, List<int>>{}; | |
| 173 for (int i = 0; i < parameters.length; i++) { | |
| 174 Variable param = getVariable(parameters[i]); | |
| 175 Expression arg = arguments[i]; | |
| 176 if (arg is VariableUse) { | |
| 177 if (param == null || param == arg.variable) { | |
| 178 // No assignment necessary. | |
| 179 --arg.variable.readCount; | |
| 180 continue; | |
| 181 } | |
| 182 // v1 = v0 | |
| 183 List<int> list = rightHand[arg.variable]; | |
| 184 if (list == null) { | |
| 185 rightHand[arg.variable] = list = <int>[]; | |
| 186 } | |
| 187 list.add(i); | |
| 188 } else { | |
| 189 // v1 = this; | |
| 190 } | |
| 191 } | |
| 192 | |
| 193 Statement first, current; | |
| 194 void addAssignment(Variable dst, Expression src) { | |
| 195 if (first == null) { | |
| 196 first = current = Assign.makeStatement(dst, src); | |
| 197 } else { | |
| 198 current = current.next = Assign.makeStatement(dst, src); | |
| 199 } | |
| 200 } | |
| 201 | |
| 202 List<Expression> assignmentSrc = new List<Expression>(parameters.length); | |
| 203 List<bool> done = new List<bool>.filled(parameters.length, false); | |
| 204 void visitAssignment(int i) { | |
| 205 if (done[i]) { | |
| 206 return; | |
| 207 } | |
| 208 Variable param = getVariable(parameters[i]); | |
| 209 Expression arg = arguments[i]; | |
| 210 if (param == null || (arg is VariableUse && param == arg.variable)) { | |
| 211 return; // No assignment necessary. | |
| 212 } | |
| 213 if (assignmentSrc[i] != null) { | |
| 214 // Cycle found; store argument in a temporary variable. | |
| 215 // The temporary will then be used as right-hand side when the | |
| 216 // assignment gets added. | |
| 217 VariableUse source = assignmentSrc[i]; | |
| 218 if (source.variable != phiTempVar) { | |
| 219 // Only move to temporary once. | |
| 220 assignmentSrc[i] = new VariableUse(phiTempVar); | |
| 221 addAssignment(phiTempVar, arg); | |
| 222 } | |
| 223 return; | |
| 224 } | |
| 225 assignmentSrc[i] = arg; | |
| 226 List<int> paramUses = rightHand[param]; | |
| 227 if (paramUses != null) { | |
| 228 for (int useIndex in paramUses) { | |
| 229 visitAssignment(useIndex); | |
| 230 } | |
| 231 } | |
| 232 addAssignment(param, assignmentSrc[i]); | |
| 233 done[i] = true; | |
| 234 } | |
| 235 | |
| 236 for (int i = 0; i < parameters.length; i++) { | |
| 237 if (!done[i]) { | |
| 238 visitAssignment(i); | |
| 239 } | |
| 240 } | |
| 241 | |
| 242 if (first == null) { | |
| 243 first = buildRest(); | |
| 244 } else { | |
| 245 current.next = buildRest(); | |
| 246 } | |
| 247 return first; | |
| 248 } | |
| 249 | |
| 250 visit(cps_ir.Node node) => throw 'Use translateXXX instead of visit'; | |
| 251 | |
| 252 /// Translates a CPS expression into a tree statement. | |
| 253 /// | |
| 254 /// To avoid deep recursion, we traverse each basic blocks without | |
| 255 /// recursion. | |
| 256 /// | |
| 257 /// Non-tail expressions evaluate to a callback to be invoked once the | |
| 258 /// successor statement has been constructed. These callbacks are stored | |
| 259 /// in a stack until the block's tail expression has been translated. | |
| 260 Statement translateExpression(cps_ir.Expression node) { | |
| 261 List<NodeCallback> stack = <NodeCallback>[]; | |
| 262 while (node is! cps_ir.TailExpression) { | |
| 263 stack.add(node.accept(this)); | |
| 264 node = node.next; | |
| 265 } | |
| 266 Statement result = node.accept(this); // Translate the tail expression. | |
| 267 for (NodeCallback fun in stack.reversed) { | |
| 268 result = fun(result); | |
| 269 } | |
| 270 return result; | |
| 271 } | |
| 272 | |
| 273 /// Translates a CPS primitive to a tree expression. | |
| 274 /// | |
| 275 /// This simply calls the visit method for the primitive. | |
| 276 translatePrimitive(cps_ir.Primitive prim) { | |
| 277 return prim.accept(this); | |
| 278 } | |
| 279 | |
| 280 /************************ CONSTANT COPYING *****************************/ | |
| 281 | |
| 282 /// Estimate of the number of characters needed to emit a use of the given | |
| 283 /// constant. | |
| 284 int constantSize(PrimitiveConstantValue value) { | |
| 285 // TODO(asgerf): We could interface with the emitter to get the exact size. | |
| 286 if (value is StringConstantValue) { | |
| 287 // Account for the quotes, but ignore the cost of encoding non-ASCII | |
| 288 // characters to avoid traversing the string and depending on encoding. | |
| 289 return value.length + 2; | |
| 290 } else if (value is BoolConstantValue) { | |
| 291 return 2; // Printed as !0 and !1 when minified | |
| 292 } else { | |
| 293 // TODO(asgerf): Get the exact length of numbers using '1e10' notation. | |
| 294 return '${value.primitiveValue}'.length; | |
| 295 } | |
| 296 } | |
| 297 | |
| 298 /// The number of uses [prim] has, or `-1` if it is used in a phi assignment. | |
| 299 int countNonPhiUses(cps_ir.Primitive prim) { | |
| 300 int count = 0; | |
| 301 for (cps_ir.Reference ref = prim.firstRef; ref != null; ref = ref.next) { | |
| 302 cps_ir.Node use = ref.parent; | |
| 303 if (use is cps_ir.InvokeContinuation) { | |
| 304 return -1; | |
| 305 } | |
| 306 count++; | |
| 307 } | |
| 308 return count; | |
| 309 } | |
| 310 | |
| 311 /// True if the given [constant] should be copied to every use site. | |
| 312 bool shouldCopyToUses(cps_ir.Constant constant) { | |
| 313 if (!constant.value.isPrimitive) return false; | |
| 314 if (constant.hasAtMostOneUse) return true; | |
| 315 int uses = countNonPhiUses(constant); | |
| 316 if (uses == -1) return false; // Copying might prevent elimination of a phi. | |
| 317 int size = constantSize(constant.value); | |
| 318 // Compare the expected code size output of copying vs sharing. | |
| 319 const int USE = 2; // Minified locals usually have length 2. | |
| 320 const int ASSIGN = USE + 2; // Variable and '=' and ';' | |
| 321 const int BIAS = 2; // Artificial bias to slightly favor copying. | |
| 322 int costOfSharing = USE * uses + size + ASSIGN + BIAS; | |
| 323 int costOfCopying = size * uses; | |
| 324 return costOfCopying <= costOfSharing; | |
| 325 } | |
| 326 | |
| 327 /************************ INTERIOR EXPRESSIONS ************************/ | |
| 328 // | |
| 329 // Visit methods for interior expressions must return a function: | |
| 330 // | |
| 331 // (Statement next) => <result statement> | |
| 332 // | |
| 333 | |
| 334 NodeCallback visitLetPrim(cps_ir.LetPrim node) { | |
| 335 if (node.primitive is cps_ir.Constant && shouldCopyToUses(node.primitive)) { | |
| 336 inlinedConstants.add(node.primitive); | |
| 337 return (Statement next) => next; | |
| 338 } | |
| 339 Variable variable = getVariable(node.primitive); | |
| 340 var value = translatePrimitive(node.primitive); | |
| 341 if (value is Expression) { | |
| 342 if (node.primitive.hasAtLeastOneUse) { | |
| 343 return (Statement next) => Assign.makeStatement(variable, value, next); | |
| 344 } else { | |
| 345 return (Statement next) => new ExpressionStatement(value, next); | |
| 346 } | |
| 347 } else { | |
| 348 assert(value is NodeCallback); | |
| 349 return value; | |
| 350 } | |
| 351 } | |
| 352 | |
| 353 // Continuations are bound at the same level, but they have to be | |
| 354 // translated as if nested. This is because the body can invoke any | |
| 355 // of them from anywhere, so it must be nested inside all of them. | |
| 356 // | |
| 357 // The continuation bodies are not always translated directly here because | |
| 358 // they may have been already translated: | |
| 359 // * For singly-used continuations, the continuation's body is | |
| 360 // translated at the site of the continuation invocation. | |
| 361 // * For recursive continuations, there is a single non-recursive | |
| 362 // invocation. The continuation's body is translated at the site | |
| 363 // of the non-recursive continuation invocation. | |
| 364 // See [visitInvokeContinuation] for the implementation. | |
| 365 NodeCallback visitLetCont(cps_ir.LetCont node) => (Statement next) { | |
| 366 for (cps_ir.Continuation continuation in node.continuations) { | |
| 367 // This happens after the body of the LetCont has been translated. | |
| 368 // Labels are created on-demand if the continuation could not be inlin
ed, | |
| 369 // so the existence of the label indicates if a labeled statement shou
ld | |
| 370 // be emitted. | |
| 371 Label label = labels[continuation]; | |
| 372 if (label != null && !continuation.isRecursive) { | |
| 373 // Recursively build the body. We only do this for join continuation
s, | |
| 374 // so we should not risk overly deep recursion. | |
| 375 next = new LabeledStatement( | |
| 376 label, next, translateExpression(continuation.body)); | |
| 377 } | |
| 378 } | |
| 379 return next; | |
| 380 }; | |
| 381 | |
| 382 NodeCallback visitLetHandler(cps_ir.LetHandler node) => (Statement next) { | |
| 383 List<Variable> catchParameters = | |
| 384 node.handler.parameters.map(getVariable).toList(); | |
| 385 Statement catchBody = translateExpression(node.handler.body); | |
| 386 return new Try(next, catchParameters, catchBody); | |
| 387 }; | |
| 388 | |
| 389 NodeCallback visitLetMutable(cps_ir.LetMutable node) { | |
| 390 Variable variable = addMutableVariable(node.variable); | |
| 391 Expression value = getVariableUse(node.valueRef); | |
| 392 return (Statement next) => Assign.makeStatement(variable, value, next); | |
| 393 } | |
| 394 | |
| 395 /************************** TAIL EXPRESSIONS **************************/ | |
| 396 // | |
| 397 // Visit methods for tail expressions must return a statement directly | |
| 398 // (not a function like interior and call expressions). | |
| 399 | |
| 400 Statement visitThrow(cps_ir.Throw node) { | |
| 401 Expression value = getVariableUse(node.valueRef); | |
| 402 return new Throw(value); | |
| 403 } | |
| 404 | |
| 405 Statement visitUnreachable(cps_ir.Unreachable node) { | |
| 406 return new Unreachable(); | |
| 407 } | |
| 408 | |
| 409 Statement visitInvokeContinuation(cps_ir.InvokeContinuation node) { | |
| 410 // Invocations of the return continuation are translated to returns. | |
| 411 // Other continuation invocations are replaced with assignments of the | |
| 412 // arguments to formal parameter variables, followed by the body if | |
| 413 // the continuation is singly reference or a break if it is multiply | |
| 414 // referenced. | |
| 415 cps_ir.Continuation cont = node.continuation; | |
| 416 if (cont == returnContinuation) { | |
| 417 assert(node.argumentRefs.length == 1); | |
| 418 return new Return(getVariableUse(node.argumentRefs.single), | |
| 419 sourceInformation: node.sourceInformation); | |
| 420 } else { | |
| 421 List<Expression> arguments = translateArguments(node.argumentRefs); | |
| 422 return buildPhiAssignments(cont.parameters, arguments, () { | |
| 423 // Translate invocations of recursive and non-recursive | |
| 424 // continuations differently. | |
| 425 // * Non-recursive continuations | |
| 426 // - If there is one use, translate the continuation body | |
| 427 // inline at the invocation site. | |
| 428 // - If there are multiple uses, translate to Break. | |
| 429 // * Recursive continuations | |
| 430 // - There is a single non-recursive invocation. Translate | |
| 431 // the continuation body inline as a labeled loop at the | |
| 432 // invocation site. | |
| 433 // - Translate the recursive invocations to Continue. | |
| 434 if (cont.isRecursive) { | |
| 435 return node.isRecursive | |
| 436 ? new Continue(getLabel(cont)) | |
| 437 : new WhileTrue(getLabel(cont), translateExpression(cont.body)); | |
| 438 } else { | |
| 439 return cont.hasExactlyOneUse && !node.isEscapingTry | |
| 440 ? translateExpression(cont.body) | |
| 441 : new Break(getLabel(cont)); | |
| 442 } | |
| 443 }); | |
| 444 } | |
| 445 } | |
| 446 | |
| 447 /// Translates a branch condition to a tree expression. | |
| 448 Expression translateCondition(cps_ir.Branch branch) { | |
| 449 Expression value = getVariableUse(branch.conditionRef, | |
| 450 sourceInformation: branch.sourceInformation); | |
| 451 if (branch.isStrictCheck) { | |
| 452 return new ApplyBuiltinOperator( | |
| 453 BuiltinOperator.StrictEq, | |
| 454 <Expression>[value, new Constant(new TrueConstantValue())], | |
| 455 branch.sourceInformation); | |
| 456 } else { | |
| 457 return value; | |
| 458 } | |
| 459 } | |
| 460 | |
| 461 Statement visitBranch(cps_ir.Branch node) { | |
| 462 Expression condition = translateCondition(node); | |
| 463 Statement thenStatement, elseStatement; | |
| 464 cps_ir.Continuation cont = node.trueContinuation; | |
| 465 assert(cont.parameters.isEmpty); | |
| 466 thenStatement = cont.hasExactlyOneUse | |
| 467 ? translateExpression(cont.body) | |
| 468 : new Break(labels[cont]); | |
| 469 cont = node.falseContinuation; | |
| 470 assert(cont.parameters.isEmpty); | |
| 471 elseStatement = cont.hasExactlyOneUse | |
| 472 ? translateExpression(cont.body) | |
| 473 : new Break(labels[cont]); | |
| 474 return new If( | |
| 475 condition, thenStatement, elseStatement, node.sourceInformation); | |
| 476 } | |
| 477 | |
| 478 /************************** PRIMITIVES **************************/ | |
| 479 // | |
| 480 // Visit methods for primitives must return an expression. | |
| 481 // | |
| 482 | |
| 483 Expression visitSetField(cps_ir.SetField node) { | |
| 484 return new SetField(getVariableUse(node.objectRef), node.field, | |
| 485 getVariableUse(node.valueRef), node.sourceInformation); | |
| 486 } | |
| 487 | |
| 488 Expression visitInterceptor(cps_ir.Interceptor node) { | |
| 489 return new Interceptor(getVariableUse(node.inputRef), | |
| 490 node.interceptedClasses, node.sourceInformation); | |
| 491 } | |
| 492 | |
| 493 Expression visitCreateInstance(cps_ir.CreateInstance node) { | |
| 494 return new CreateInstance( | |
| 495 node.classElement, | |
| 496 translateArguments(node.argumentRefs), | |
| 497 getVariableUseOrNull(node.typeInformationRef), | |
| 498 node.sourceInformation); | |
| 499 } | |
| 500 | |
| 501 Expression visitGetField(cps_ir.GetField node) { | |
| 502 return new GetField( | |
| 503 getVariableUse(node.objectRef), node.field, node.sourceInformation, | |
| 504 objectIsNotNull: !node.object.type.isNullable); | |
| 505 } | |
| 506 | |
| 507 Expression visitCreateBox(cps_ir.CreateBox node) { | |
| 508 return new CreateBox(); | |
| 509 } | |
| 510 | |
| 511 Expression visitCreateInvocationMirror(cps_ir.CreateInvocationMirror node) { | |
| 512 return new CreateInvocationMirror( | |
| 513 node.selector, translateArguments(node.argumentRefs)); | |
| 514 } | |
| 515 | |
| 516 Expression visitGetMutable(cps_ir.GetMutable node) { | |
| 517 return getMutableVariableUse(node.variableRef, node.sourceInformation); | |
| 518 } | |
| 519 | |
| 520 Expression visitSetMutable(cps_ir.SetMutable node) { | |
| 521 Variable variable = getMutableVariable(node.variable); | |
| 522 Expression value = getVariableUse(node.valueRef); | |
| 523 return new Assign(variable, value, | |
| 524 sourceInformation: node.sourceInformation); | |
| 525 } | |
| 526 | |
| 527 Expression visitConstant(cps_ir.Constant node) { | |
| 528 return new Constant(node.value, sourceInformation: node.sourceInformation); | |
| 529 } | |
| 530 | |
| 531 Expression visitLiteralList(cps_ir.LiteralList node) { | |
| 532 return new LiteralList(node.dartType, translateArguments(node.valueRefs)); | |
| 533 } | |
| 534 | |
| 535 Expression visitReifyRuntimeType(cps_ir.ReifyRuntimeType node) { | |
| 536 return new ReifyRuntimeType( | |
| 537 getVariableUse(node.valueRef), node.sourceInformation); | |
| 538 } | |
| 539 | |
| 540 Expression visitReadTypeVariable(cps_ir.ReadTypeVariable node) { | |
| 541 return new ReadTypeVariable( | |
| 542 node.variable, getVariableUse(node.targetRef), node.sourceInformation); | |
| 543 } | |
| 544 | |
| 545 Expression visitTypeExpression(cps_ir.TypeExpression node) { | |
| 546 return new TypeExpression(node.kind, node.dartType, | |
| 547 node.argumentRefs.map(getVariableUse).toList()); | |
| 548 } | |
| 549 | |
| 550 Expression visitTypeTest(cps_ir.TypeTest node) { | |
| 551 Expression value = getVariableUse(node.valueRef); | |
| 552 List<Expression> typeArgs = translateArguments(node.typeArgumentRefs); | |
| 553 return new TypeOperator(value, node.dartType, typeArgs, isTypeTest: true); | |
| 554 } | |
| 555 | |
| 556 Expression visitTypeTestViaFlag(cps_ir.TypeTestViaFlag node) { | |
| 557 Expression value = getVariableUse(node.interceptorRef); | |
| 558 // TODO(sra): Move !! to cps_ir level. | |
| 559 return new Not(new Not(new GetTypeTestProperty(value, node.dartType))); | |
| 560 } | |
| 561 | |
| 562 Expression visitGetStatic(cps_ir.GetStatic node) { | |
| 563 return new GetStatic(node.element, node.sourceInformation); | |
| 564 } | |
| 565 | |
| 566 Expression visitSetStatic(cps_ir.SetStatic node) { | |
| 567 return new SetStatic( | |
| 568 node.element, getVariableUse(node.valueRef), node.sourceInformation); | |
| 569 } | |
| 570 | |
| 571 Expression visitApplyBuiltinOperator(cps_ir.ApplyBuiltinOperator node) { | |
| 572 if (node.operator == BuiltinOperator.IsFalsy) { | |
| 573 return new Not(getVariableUse(node.argumentRefs.single)); | |
| 574 } | |
| 575 return new ApplyBuiltinOperator(node.operator, | |
| 576 translateArguments(node.argumentRefs), node.sourceInformation); | |
| 577 } | |
| 578 | |
| 579 Expression visitApplyBuiltinMethod(cps_ir.ApplyBuiltinMethod node) { | |
| 580 return new ApplyBuiltinMethod(node.method, getVariableUse(node.receiverRef), | |
| 581 translateArguments(node.argumentRefs), | |
| 582 receiverIsNotNull: !node.receiver.type.isNullable); | |
| 583 } | |
| 584 | |
| 585 Expression visitGetLength(cps_ir.GetLength node) { | |
| 586 return new GetLength(getVariableUse(node.objectRef)); | |
| 587 } | |
| 588 | |
| 589 Expression visitGetIndex(cps_ir.GetIndex node) { | |
| 590 return new GetIndex( | |
| 591 getVariableUse(node.objectRef), getVariableUse(node.indexRef)); | |
| 592 } | |
| 593 | |
| 594 Expression visitSetIndex(cps_ir.SetIndex node) { | |
| 595 return new SetIndex(getVariableUse(node.objectRef), | |
| 596 getVariableUse(node.indexRef), getVariableUse(node.valueRef)); | |
| 597 } | |
| 598 | |
| 599 Expression visitInvokeStatic(cps_ir.InvokeStatic node) { | |
| 600 List<Expression> arguments = translateArguments(node.argumentRefs); | |
| 601 return new InvokeStatic( | |
| 602 node.target, node.selector, arguments, node.sourceInformation); | |
| 603 } | |
| 604 | |
| 605 List<Expression> insertReceiverArgument( | |
| 606 Expression receiver, List<Expression> arguments) { | |
| 607 return new List<Expression>.generate( | |
| 608 arguments.length + 1, (n) => n == 0 ? receiver : arguments[n - 1], | |
| 609 growable: false); | |
| 610 } | |
| 611 | |
| 612 Expression visitInvokeMethod(cps_ir.InvokeMethod node) { | |
| 613 switch (node.callingConvention) { | |
| 614 case cps_ir.CallingConvention.Normal: | |
| 615 InvokeMethod invoke = new InvokeMethod( | |
| 616 getVariableUse(node.receiverRef), | |
| 617 node.selector, | |
| 618 node.mask, | |
| 619 translateArguments(node.argumentRefs), | |
| 620 node.sourceInformation); | |
| 621 invoke.receiverIsNotNull = !node.receiver.type.isNullable; | |
| 622 return invoke; | |
| 623 | |
| 624 case cps_ir.CallingConvention.Intercepted: | |
| 625 List<Expression> arguments = insertReceiverArgument( | |
| 626 getVariableUse(node.receiverRef), | |
| 627 translateArguments(node.argumentRefs)); | |
| 628 InvokeMethod invoke = new InvokeMethod( | |
| 629 getVariableUse(node.interceptorRef), | |
| 630 node.selector, | |
| 631 node.mask, | |
| 632 arguments, | |
| 633 node.sourceInformation); | |
| 634 // Sometimes we know the Dart receiver is non-null because it has been | |
| 635 // refined, which implies that the JS receiver also can not be null at | |
| 636 // the use-site. Interceptors are not refined, so this information is | |
| 637 // not always available on the JS receiver. | |
| 638 // Also check the JS receiver's type, however, because sometimes we know | |
| 639 // an interceptor is non-null because it intercepts JSNull. | |
| 640 invoke.receiverIsNotNull = | |
| 641 !node.receiver.type.isNullable || !node.interceptor.type.isNullable; | |
| 642 return invoke; | |
| 643 | |
| 644 case cps_ir.CallingConvention.DummyIntercepted: | |
| 645 List<Expression> arguments = insertReceiverArgument( | |
| 646 new Constant(new IntConstantValue(0)), | |
| 647 translateArguments(node.argumentRefs)); | |
| 648 InvokeMethod invoke = new InvokeMethod(getVariableUse(node.receiverRef), | |
| 649 node.selector, node.mask, arguments, node.sourceInformation); | |
| 650 invoke.receiverIsNotNull = !node.receiver.type.isNullable; | |
| 651 return invoke; | |
| 652 | |
| 653 case cps_ir.CallingConvention.OneShotIntercepted: | |
| 654 List<Expression> arguments = insertReceiverArgument( | |
| 655 getVariableUse(node.receiverRef), | |
| 656 translateArguments(node.argumentRefs)); | |
| 657 return new OneShotInterceptor( | |
| 658 node.selector, node.mask, arguments, node.sourceInformation); | |
| 659 } | |
| 660 } | |
| 661 | |
| 662 Expression visitInvokeMethodDirectly(cps_ir.InvokeMethodDirectly node) { | |
| 663 if (node.interceptorRef != null) { | |
| 664 return new InvokeMethodDirectly( | |
| 665 getVariableUse(node.interceptorRef), | |
| 666 node.target, | |
| 667 node.selector, | |
| 668 insertReceiverArgument(getVariableUse(node.receiverRef), | |
| 669 translateArguments(node.argumentRefs)), | |
| 670 node.sourceInformation); | |
| 671 } else { | |
| 672 return new InvokeMethodDirectly( | |
| 673 getVariableUse(node.receiverRef), | |
| 674 node.target, | |
| 675 node.selector, | |
| 676 translateArguments(node.argumentRefs), | |
| 677 node.sourceInformation); | |
| 678 } | |
| 679 } | |
| 680 | |
| 681 Expression visitTypeCast(cps_ir.TypeCast node) { | |
| 682 Expression value = getVariableUse(node.valueRef); | |
| 683 List<Expression> typeArgs = translateArguments(node.typeArgumentRefs); | |
| 684 return new TypeOperator(value, node.dartType, typeArgs, isTypeTest: false); | |
| 685 } | |
| 686 | |
| 687 Expression visitInvokeConstructor(cps_ir.InvokeConstructor node) { | |
| 688 List<Expression> arguments = translateArguments(node.argumentRefs); | |
| 689 return new InvokeConstructor(node.dartType, node.target, node.selector, | |
| 690 arguments, node.sourceInformation); | |
| 691 } | |
| 692 | |
| 693 visitForeignCode(cps_ir.ForeignCode node) { | |
| 694 List<Expression> arguments = | |
| 695 node.argumentRefs.map(getVariableUse).toList(growable: false); | |
| 696 List<bool> nullableArguments = node.argumentRefs | |
| 697 .map((argument) => argument.definition.type.isNullable) | |
| 698 .toList(growable: false); | |
| 699 if (node.codeTemplate.isExpression) { | |
| 700 return new ForeignExpression( | |
| 701 node.codeTemplate, | |
| 702 node.type, | |
| 703 arguments, | |
| 704 node.nativeBehavior, | |
| 705 nullableArguments, | |
| 706 node.dependency, | |
| 707 node.sourceInformation); | |
| 708 } else { | |
| 709 return (Statement next) { | |
| 710 assert(next is Unreachable); // We are not using the `next` statement. | |
| 711 return new ForeignStatement( | |
| 712 node.codeTemplate, | |
| 713 node.type, | |
| 714 arguments, | |
| 715 node.nativeBehavior, | |
| 716 nullableArguments, | |
| 717 node.dependency, | |
| 718 node.sourceInformation); | |
| 719 }; | |
| 720 } | |
| 721 } | |
| 722 | |
| 723 visitReceiverCheck(cps_ir.ReceiverCheck node) => (Statement next) { | |
| 724 // The CPS IR uses 'isNullCheck' because the semantics are important. | |
| 725 // In the Tree IR, syntax is more important, so the receiver check uses | |
| 726 // "useInvoke" to denote if an invocation should be emitted. | |
| 727 return new ReceiverCheck( | |
| 728 condition: getVariableUseOrNull(node.conditionRef), | |
| 729 value: getVariableUse(node.valueRef), | |
| 730 selector: node.selector, | |
| 731 useSelector: node.useSelector, | |
| 732 useInvoke: !node.isNullCheck, | |
| 733 next: next, | |
| 734 sourceInformation: node.sourceInformation); | |
| 735 }; | |
| 736 | |
| 737 Expression visitGetLazyStatic(cps_ir.GetLazyStatic node) { | |
| 738 return new GetStatic.lazy(node.element, node.sourceInformation); | |
| 739 } | |
| 740 | |
| 741 @override | |
| 742 NodeCallback visitYield(cps_ir.Yield node) { | |
| 743 return (Statement next) { | |
| 744 return new Yield(getVariableUse(node.inputRef), node.hasStar, next); | |
| 745 }; | |
| 746 } | |
| 747 | |
| 748 @override | |
| 749 Expression visitAwait(cps_ir.Await node) { | |
| 750 return new Await(getVariableUse(node.inputRef)); | |
| 751 } | |
| 752 | |
| 753 @override | |
| 754 visitRefinement(cps_ir.Refinement node) { | |
| 755 return (Statement next) => next; // Compile to nothing. | |
| 756 } | |
| 757 | |
| 758 /********** UNUSED VISIT METHODS *************/ | |
| 759 | |
| 760 unexpectedNode(cps_ir.Node node) { | |
| 761 internalError(CURRENT_ELEMENT_SPANNABLE, 'Unexpected IR node: $node'); | |
| 762 } | |
| 763 | |
| 764 visitFunctionDefinition(cps_ir.FunctionDefinition node) { | |
| 765 unexpectedNode(node); | |
| 766 } | |
| 767 | |
| 768 visitParameter(cps_ir.Parameter node) => unexpectedNode(node); | |
| 769 visitContinuation(cps_ir.Continuation node) => unexpectedNode(node); | |
| 770 visitMutableVariable(cps_ir.MutableVariable node) => unexpectedNode(node); | |
| 771 visitRethrow(cps_ir.Rethrow node) => unexpectedNode(node); | |
| 772 visitBoundsCheck(cps_ir.BoundsCheck node) => unexpectedNode(node); | |
| 773 } | |
| OLD | NEW |