| OLD | NEW |
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 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 | 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 // IrNodes are kept in a separate library to have precise control over their | 5 // IrNodes are kept in a separate library to have precise control over their |
| 6 // dependencies on other parts of the system. | 6 // dependencies on other parts of the system. |
| 7 library dart2js.ir_nodes; | 7 library dart2js.ir_nodes; |
| 8 | 8 |
| 9 import '../constants/expressions.dart'; | 9 import '../constants/expressions.dart'; |
| 10 import '../constants/values.dart' as values show ConstantValue; | 10 import '../constants/values.dart' as values show ConstantValue; |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 58 /// All primitives are named using the identity of the [Primitive] object. | 58 /// All primitives are named using the identity of the [Primitive] object. |
| 59 /// | 59 /// |
| 60 /// Primitives may allocate objects, this is not considered side-effect here. | 60 /// Primitives may allocate objects, this is not considered side-effect here. |
| 61 /// | 61 /// |
| 62 /// Although primitives may not mutate state, they may depend on state. | 62 /// Although primitives may not mutate state, they may depend on state. |
| 63 abstract class Primitive extends Definition<Primitive> { | 63 abstract class Primitive extends Definition<Primitive> { |
| 64 /// The [VariableElement] or [ParameterElement] from which the primitive | 64 /// The [VariableElement] or [ParameterElement] from which the primitive |
| 65 /// binding originated. | 65 /// binding originated. |
| 66 Entity hint; | 66 Entity hint; |
| 67 | 67 |
| 68 /// Register in which the variable binding this primitive can be allocated. | |
| 69 /// Separate register spaces are used for primitives with different [element]. | |
| 70 /// Assigned by [RegisterAllocator], is null before that phase. | |
| 71 int registerIndex; | |
| 72 | |
| 73 /// Use the given element as a hint for naming this primitive. | 68 /// Use the given element as a hint for naming this primitive. |
| 74 /// | 69 /// |
| 75 /// Has no effect if this primitive already has a non-null [element]. | 70 /// Has no effect if this primitive already has a non-null [element]. |
| 76 void useElementAsHint(Entity hint) { | 71 void useElementAsHint(Entity hint) { |
| 77 if (this.hint == null) { | 72 if (this.hint == null) { |
| 78 this.hint = hint; | 73 this.hint = hint; |
| 79 } | 74 } |
| 80 } | 75 } |
| 81 } | 76 } |
| 82 | 77 |
| (...skipping 910 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 993 node.parameters.forEach(visit); | 988 node.parameters.forEach(visit); |
| 994 if (!node.isAbstract) { | 989 if (!node.isAbstract) { |
| 995 visit(node.body); | 990 visit(node.body); |
| 996 } | 991 } |
| 997 } | 992 } |
| 998 | 993 |
| 999 processConstructorDefinition(ConstructorDefinition node) {} | 994 processConstructorDefinition(ConstructorDefinition node) {} |
| 1000 visitConstructorDefinition(ConstructorDefinition node) { | 995 visitConstructorDefinition(ConstructorDefinition node) { |
| 1001 processConstructorDefinition(node); | 996 processConstructorDefinition(node); |
| 1002 node.parameters.forEach(visit); | 997 node.parameters.forEach(visit); |
| 1003 node.initializers.forEach(visit); | 998 if (!node.isAbstract) { |
| 1004 visit(node.body); | 999 node.initializers.forEach(visit); |
| 1000 visit(node.body); |
| 1001 } |
| 1005 } | 1002 } |
| 1006 | 1003 |
| 1007 processFieldInitializer(FieldInitializer node) {} | 1004 processFieldInitializer(FieldInitializer node) {} |
| 1008 visitFieldInitializer(FieldInitializer node) { | 1005 visitFieldInitializer(FieldInitializer node) { |
| 1009 processFieldInitializer(node); | 1006 processFieldInitializer(node); |
| 1010 visit(node.body); | 1007 visit(node.body); |
| 1011 } | 1008 } |
| 1012 | 1009 |
| 1013 processSuperInitializer(SuperInitializer node) {} | 1010 processSuperInitializer(SuperInitializer node) {} |
| 1014 visitSuperInitializer(SuperInitializer node) { | 1011 visitSuperInitializer(SuperInitializer node) { |
| (...skipping 212 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1227 processReifyRuntimeType(node); | 1224 processReifyRuntimeType(node); |
| 1228 processReference(node.value); | 1225 processReference(node.value); |
| 1229 } | 1226 } |
| 1230 | 1227 |
| 1231 processReadTypeVariable(ReadTypeVariable node) {} | 1228 processReadTypeVariable(ReadTypeVariable node) {} |
| 1232 visitReadTypeVariable(ReadTypeVariable node) { | 1229 visitReadTypeVariable(ReadTypeVariable node) { |
| 1233 processReadTypeVariable(node); | 1230 processReadTypeVariable(node); |
| 1234 processReference(node.target); | 1231 processReference(node.target); |
| 1235 } | 1232 } |
| 1236 } | 1233 } |
| 1237 | |
| 1238 /// Keeps track of currently unused register indices. | |
| 1239 class RegisterArray { | |
| 1240 int nextIndex = 0; | |
| 1241 final List<int> freeStack = <int>[]; | |
| 1242 | |
| 1243 /// Returns an index that is currently unused. | |
| 1244 int makeIndex() { | |
| 1245 if (freeStack.isEmpty) { | |
| 1246 return nextIndex++; | |
| 1247 } else { | |
| 1248 return freeStack.removeLast(); | |
| 1249 } | |
| 1250 } | |
| 1251 | |
| 1252 void releaseIndex(int index) { | |
| 1253 freeStack.add(index); | |
| 1254 } | |
| 1255 } | |
| 1256 | |
| 1257 /// Assigns indices to each primitive in the IR such that primitives that are | |
| 1258 /// live simultaneously never get assigned the same index. | |
| 1259 /// This information is used by the dart tree builder to generate fewer | |
| 1260 /// redundant variables. | |
| 1261 /// Currently, the liveness analysis is very simple and is often inadequate | |
| 1262 /// for removing all of the redundant variables. | |
| 1263 class RegisterAllocator implements Visitor { | |
| 1264 final dart2js.InternalErrorFunction internalError; | |
| 1265 | |
| 1266 /// Separate register spaces for each source-level variable/parameter. | |
| 1267 /// Note that null is used as key for primitives without hints. | |
| 1268 final Map<Local, RegisterArray> elementRegisters = <Local, RegisterArray>{}; | |
| 1269 | |
| 1270 RegisterAllocator(this.internalError); | |
| 1271 | |
| 1272 RegisterArray getRegisterArray(Local local) { | |
| 1273 RegisterArray registers = elementRegisters[local]; | |
| 1274 if (registers == null) { | |
| 1275 registers = new RegisterArray(); | |
| 1276 elementRegisters[local] = registers; | |
| 1277 } | |
| 1278 return registers; | |
| 1279 } | |
| 1280 | |
| 1281 void allocate(Primitive primitive) { | |
| 1282 if (primitive.registerIndex == null) { | |
| 1283 primitive.registerIndex = getRegisterArray(primitive.hint).makeIndex(); | |
| 1284 } | |
| 1285 } | |
| 1286 | |
| 1287 void release(Primitive primitive) { | |
| 1288 // Do not share indices for temporaries as this may obstruct inlining. | |
| 1289 if (primitive.hint == null) return; | |
| 1290 if (primitive.registerIndex != null) { | |
| 1291 getRegisterArray(primitive.hint).releaseIndex(primitive.registerIndex); | |
| 1292 } | |
| 1293 } | |
| 1294 | |
| 1295 void visit(Node node) => node.accept(this); | |
| 1296 | |
| 1297 void visitReference(Reference reference) { | |
| 1298 allocate(reference.definition); | |
| 1299 } | |
| 1300 | |
| 1301 void visitFieldDefinition(FieldDefinition node) { | |
| 1302 if (node.hasInitializer) { | |
| 1303 visit(node.body); | |
| 1304 } | |
| 1305 } | |
| 1306 | |
| 1307 void visitRunnableBody(RunnableBody node) { | |
| 1308 visit(node.body); | |
| 1309 } | |
| 1310 | |
| 1311 void visitFunctionDefinition(FunctionDefinition node) { | |
| 1312 if (!node.isAbstract) { | |
| 1313 visit(node.body); | |
| 1314 } | |
| 1315 // Assign indices to unused parameters. | |
| 1316 for (Definition param in node.parameters) { | |
| 1317 if (param is Primitive) { | |
| 1318 allocate(param); | |
| 1319 } | |
| 1320 } | |
| 1321 } | |
| 1322 | |
| 1323 void visitConstructorDefinition(ConstructorDefinition node) { | |
| 1324 if (!node.isAbstract) { | |
| 1325 node.initializers.forEach(visit); | |
| 1326 visit(node.body); | |
| 1327 } | |
| 1328 // Assign indices to unused parameters. | |
| 1329 for (Definition param in node.parameters) { | |
| 1330 if (param is Primitive) { | |
| 1331 allocate(param); | |
| 1332 } | |
| 1333 } | |
| 1334 } | |
| 1335 | |
| 1336 void visitFieldInitializer(FieldInitializer node) { | |
| 1337 visit(node.body.body); | |
| 1338 } | |
| 1339 | |
| 1340 void visitSuperInitializer(SuperInitializer node) { | |
| 1341 node.arguments.forEach(visit); | |
| 1342 } | |
| 1343 | |
| 1344 void visitLetPrim(LetPrim node) { | |
| 1345 visit(node.body); | |
| 1346 release(node.primitive); | |
| 1347 visit(node.primitive); | |
| 1348 } | |
| 1349 | |
| 1350 void visitLetCont(LetCont node) { | |
| 1351 node.continuations.forEach(visit); | |
| 1352 visit(node.body); | |
| 1353 } | |
| 1354 | |
| 1355 void visitLetHandler(LetHandler node) { | |
| 1356 visit(node.handler); | |
| 1357 // Handler parameters that were not used in the handler body will not have | |
| 1358 // had register indexes assigned. Assign them here, otherwise they will | |
| 1359 // be eliminated later and they should not be (i.e., a catch clause that | |
| 1360 // does not use the exception parameter should not have the exception | |
| 1361 // parameter eliminated, because it would not be well-formed anymore). | |
| 1362 // In any case release the parameter indexes because the parameters are | |
| 1363 // not live in the try block. | |
| 1364 node.handler.parameters.forEach((Parameter parameter) { | |
| 1365 allocate(parameter); | |
| 1366 release(parameter); | |
| 1367 }); | |
| 1368 visit(node.body); | |
| 1369 } | |
| 1370 | |
| 1371 void visitLetMutable(LetMutable node) { | |
| 1372 visit(node.body); | |
| 1373 visitReference(node.value); | |
| 1374 } | |
| 1375 | |
| 1376 void visitInvokeStatic(InvokeStatic node) { | |
| 1377 node.arguments.forEach(visitReference); | |
| 1378 } | |
| 1379 | |
| 1380 void visitInvokeContinuation(InvokeContinuation node) { | |
| 1381 node.arguments.forEach(visitReference); | |
| 1382 } | |
| 1383 | |
| 1384 void visitInvokeMethod(InvokeMethod node) { | |
| 1385 visitReference(node.receiver); | |
| 1386 node.arguments.forEach(visitReference); | |
| 1387 } | |
| 1388 | |
| 1389 void visitInvokeMethodDirectly(InvokeMethodDirectly node) { | |
| 1390 visitReference(node.receiver); | |
| 1391 node.arguments.forEach(visitReference); | |
| 1392 } | |
| 1393 | |
| 1394 void visitInvokeConstructor(InvokeConstructor node) { | |
| 1395 node.arguments.forEach(visitReference); | |
| 1396 } | |
| 1397 | |
| 1398 void visitConcatenateStrings(ConcatenateStrings node) { | |
| 1399 node.arguments.forEach(visitReference); | |
| 1400 } | |
| 1401 | |
| 1402 void visitBranch(Branch node) { | |
| 1403 visit(node.condition); | |
| 1404 } | |
| 1405 | |
| 1406 void visitLiteralList(LiteralList node) { | |
| 1407 node.values.forEach(visitReference); | |
| 1408 } | |
| 1409 | |
| 1410 void visitLiteralMap(LiteralMap node) { | |
| 1411 for (LiteralMapEntry entry in node.entries) { | |
| 1412 visitReference(entry.key); | |
| 1413 visitReference(entry.value); | |
| 1414 } | |
| 1415 } | |
| 1416 | |
| 1417 void visitTypeOperator(TypeOperator node) { | |
| 1418 visitReference(node.receiver); | |
| 1419 } | |
| 1420 | |
| 1421 void visitConstant(Constant node) { | |
| 1422 } | |
| 1423 | |
| 1424 void visitThis(This node) { | |
| 1425 } | |
| 1426 | |
| 1427 void visitReifyTypeVar(ReifyTypeVar node) { | |
| 1428 } | |
| 1429 | |
| 1430 void visitCreateFunction(CreateFunction node) { | |
| 1431 new RegisterAllocator(internalError).visit(node.definition); | |
| 1432 } | |
| 1433 | |
| 1434 void visitGetMutableVariable(GetMutableVariable node) { | |
| 1435 } | |
| 1436 | |
| 1437 void visitSetMutableVariable(SetMutableVariable node) { | |
| 1438 visit(node.body); | |
| 1439 visitReference(node.value); | |
| 1440 } | |
| 1441 | |
| 1442 void visitDeclareFunction(DeclareFunction node) { | |
| 1443 new RegisterAllocator(internalError).visit(node.definition); | |
| 1444 visit(node.body); | |
| 1445 } | |
| 1446 | |
| 1447 void visitParameter(Parameter node) { | |
| 1448 // Parameters are handled differently depending on whether they are | |
| 1449 // function parameters, continuation parameters, exception handler | |
| 1450 // parameters, etc. Thus we do not call visitParameter directly and | |
| 1451 // handle them explicitly in their parent IR node. | |
| 1452 internalError(dart2js.CURRENT_ELEMENT_SPANNABLE, | |
| 1453 'tried to allocate a parameter'); | |
| 1454 } | |
| 1455 | |
| 1456 void visitMutableVariable(MutableVariable node) {} | |
| 1457 | |
| 1458 void visitContinuation(Continuation node) { | |
| 1459 visit(node.body); | |
| 1460 | |
| 1461 // Arguments get allocated left-to-right, so we release parameters | |
| 1462 // right-to-left. This increases the likelihood that arguments can be | |
| 1463 // transferred without intermediate assignments. | |
| 1464 for (int i = node.parameters.length - 1; i >= 0; --i) { | |
| 1465 release(node.parameters[i]); | |
| 1466 } | |
| 1467 } | |
| 1468 | |
| 1469 void visitIsTrue(IsTrue node) { | |
| 1470 visitReference(node.value); | |
| 1471 } | |
| 1472 | |
| 1473 // JavaScript specific nodes. | |
| 1474 | |
| 1475 void visitSetField(SetField node) { | |
| 1476 visit(node.body); | |
| 1477 visitReference(node.value); | |
| 1478 visitReference(node.object); | |
| 1479 } | |
| 1480 | |
| 1481 void visitGetField(GetField node) { | |
| 1482 visitReference(node.object); | |
| 1483 } | |
| 1484 | |
| 1485 void visitCreateBox(CreateBox node) { | |
| 1486 } | |
| 1487 | |
| 1488 void visitCreateInstance(CreateInstance node) { | |
| 1489 node.arguments.forEach(visitReference); | |
| 1490 } | |
| 1491 | |
| 1492 void visitIdentical(Identical node) { | |
| 1493 visitReference(node.left); | |
| 1494 visitReference(node.right); | |
| 1495 } | |
| 1496 | |
| 1497 void visitInterceptor(Interceptor node) { | |
| 1498 visitReference(node.input); | |
| 1499 } | |
| 1500 | |
| 1501 void visitReifyRuntimeType(ReifyRuntimeType node) { | |
| 1502 visitReference(node.value); | |
| 1503 } | |
| 1504 | |
| 1505 void visitReadTypeVariable(ReadTypeVariable node) { | |
| 1506 visitReference(node.target); | |
| 1507 } | |
| 1508 } | |
| OLD | NEW |