| 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 part of type_graph_inferrer; | 5 part of type_graph_inferrer; |
| 6 | 6 |
| 7 /** | 7 /** |
| 8 * A set of selector names that [List] implements, that we know do not | 8 * A set of selector names that [List] implements, that we know do not |
| 9 * change the element type of the list, or let the list escape to code | 9 * change the element type of the list, or let the list escape to code |
| 10 * that might change the element type. | 10 * that might change the element type. |
| (...skipping 151 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 162 'shuffle', | 162 'shuffle', |
| 163 '[]=', | 163 '[]=', |
| 164 | 164 |
| 165 // From JSArray. | 165 // From JSArray. |
| 166 'checkMutable', | 166 'checkMutable', |
| 167 'checkGrowable', | 167 'checkGrowable', |
| 168 ]); | 168 ]); |
| 169 | 169 |
| 170 bool _VERBOSE = false; | 170 bool _VERBOSE = false; |
| 171 | 171 |
| 172 class ContainerTracerVisitor implements TypeInformationVisitor { | 172 abstract class TracerVisitor implements TypeInformationVisitor { |
| 173 final ListTypeInformation container; | 173 final TypeInformation tracedType; |
| 174 final TypeGraphInferrerEngine inferrer; | 174 final TypeGraphInferrerEngine inferrer; |
| 175 final Compiler compiler; | 175 final Compiler compiler; |
| 176 | 176 |
| 177 static const int MAX_ANALYSIS_COUNT = 16; |
| 178 final Setlet<Element> analyzedElements = new Setlet<Element>(); |
| 179 |
| 180 TracerVisitor(this.tracedType, inferrer) |
| 181 : this.inferrer = inferrer, this.compiler = inferrer.compiler; |
| 177 | 182 |
| 178 // Work list that gets populated with [TypeInformation] that could | 183 // Work list that gets populated with [TypeInformation] that could |
| 179 // contain the container. | 184 // contain the container. |
| 180 final List<TypeInformation> workList = <TypeInformation>[]; | 185 final List<TypeInformation> workList = <TypeInformation>[]; |
| 181 | 186 |
| 182 // Work list of containers to analyze after analyzing the users of a | 187 // Work list of containers to analyze after analyzing the users of a |
| 183 // [TypeInformation] that may be [container]. We know [container] | 188 // [TypeInformation] that may be [container]. We know [container] |
| 184 // has been stored in these containers and we must check how | 189 // has been stored in these containers and we must check how |
| 185 // [container] escapes from these containers. | 190 // [container] escapes from these containers. |
| 186 final List<ListTypeInformation> containersToAnalyze = | 191 final List<ListTypeInformation> containersToAnalyze = |
| 187 <ListTypeInformation>[]; | 192 <ListTypeInformation>[]; |
| 188 | 193 |
| 194 final Setlet<TypeInformation> flowsInto = new Setlet<TypeInformation>(); |
| 195 |
| 189 // The current [TypeInformation] in the analysis. | 196 // The current [TypeInformation] in the analysis. |
| 190 TypeInformation currentUser; | 197 TypeInformation currentUser; |
| 191 | |
| 192 // The list of found assignments to the container. | |
| 193 final List<TypeInformation> assignments = <TypeInformation>[]; | |
| 194 | |
| 195 bool callsGrowableMethod = false; | |
| 196 bool continueAnalyzing = true; | 198 bool continueAnalyzing = true; |
| 197 | |
| 198 static const int MAX_ANALYSIS_COUNT = 16; | |
| 199 final Setlet<Element> analyzedElements = new Setlet<Element>(); | |
| 200 | |
| 201 ContainerTracerVisitor(this.container, inferrer) | |
| 202 : this.inferrer = inferrer, this.compiler = inferrer.compiler; | |
| 203 | 199 |
| 204 void addNewEscapeInformation(TypeInformation info) { | 200 void addNewEscapeInformation(TypeInformation info) { |
| 205 if (container.flowsInto.contains(info)) return; | 201 if (flowsInto.contains(info)) return; |
| 206 container.flowsInto.add(info); | 202 flowsInto.add(info); |
| 207 workList.add(info); | 203 workList.add(info); |
| 208 } | 204 } |
| 209 | 205 |
| 210 List<TypeInformation> run() { | 206 void analyze() { |
| 211 // Collect the [TypeInformation] where the container can flow in, | 207 // Collect the [TypeInformation] where the container can flow in, |
| 212 // as well as the operations done on all these [TypeInformation]s. | 208 // as well as the operations done on all these [TypeInformation]s. |
| 213 addNewEscapeInformation(container); | 209 addNewEscapeInformation(tracedType); |
| 214 while (!workList.isEmpty) { | 210 while (!workList.isEmpty) { |
| 215 currentUser = workList.removeLast(); | 211 currentUser = workList.removeLast(); |
| 216 currentUser.users.forEach((TypeInformation info) { | 212 currentUser.users.forEach((TypeInformation info) { |
| 217 analyzedElements.add(info.owner); | 213 analyzedElements.add(info.owner); |
| 218 info.accept(this); | 214 info.accept(this); |
| 219 }); | 215 }); |
| 220 while (!containersToAnalyze.isEmpty) { | 216 while (!containersToAnalyze.isEmpty) { |
| 221 analyzeStoredIntoContainer(containersToAnalyze.removeLast()); | 217 analyzeStoredIntoContainer(containersToAnalyze.removeLast()); |
| 222 } | 218 } |
| 223 if (!continueAnalyzing) break; | 219 if (!continueAnalyzing) break; |
| 224 if (analyzedElements.length > MAX_ANALYSIS_COUNT) { | 220 if (analyzedElements.length > MAX_ANALYSIS_COUNT) { |
| 225 bailout('Too many users'); | 221 bailout('Too many users'); |
| 226 break; | 222 break; |
| 227 } | 223 } |
| 228 } | 224 } |
| 229 | |
| 230 if (continueAnalyzing) { | |
| 231 if (!callsGrowableMethod && container.inferredLength == null) { | |
| 232 container.inferredLength = container.originalLength; | |
| 233 } | |
| 234 return assignments; | |
| 235 } | |
| 236 return null; | |
| 237 } | 225 } |
| 238 | 226 |
| 239 void bailout(String reason) { | 227 void bailout(String reason) { |
| 240 if (_VERBOSE) { | 228 if (_VERBOSE) { |
| 241 ContainerTypeMask mask = container.type; | 229 print('Bailing out on $tracedType because: $reason'); |
| 242 print('Bailing out on ${mask.allocationNode} ${mask.allocationElement} ' | |
| 243 'because: $reason'); | |
| 244 } | 230 } |
| 245 continueAnalyzing = false; | 231 continueAnalyzing = false; |
| 246 callsGrowableMethod = true; | |
| 247 } | 232 } |
| 248 | 233 |
| 249 visitNarrowTypeInformation(NarrowTypeInformation info) { | 234 void visitNarrowTypeInformation(NarrowTypeInformation info) { |
| 250 addNewEscapeInformation(info); | 235 addNewEscapeInformation(info); |
| 251 } | 236 } |
| 252 | 237 |
| 253 visitPhiElementTypeInformation(PhiElementTypeInformation info) { | 238 void visitPhiElementTypeInformation(PhiElementTypeInformation info) { |
| 254 addNewEscapeInformation(info); | 239 addNewEscapeInformation(info); |
| 255 } | 240 } |
| 256 | 241 |
| 257 visitElementInContainerTypeInformation( | 242 void visitElementInContainerTypeInformation( |
| 258 ElementInContainerTypeInformation info) { | 243 ElementInContainerTypeInformation info) { |
| 259 addNewEscapeInformation(info); | 244 addNewEscapeInformation(info); |
| 260 } | 245 } |
| 261 | 246 |
| 262 visitListTypeInformation(ListTypeInformation info) { | 247 visitListTypeInformation(ListTypeInformation info) { |
| 263 containersToAnalyze.add(info); | 248 containersToAnalyze.add(info); |
| 264 } | 249 } |
| 265 | 250 |
| 266 visitMapTypeInformation(MapTypeInformation info) { | 251 void visitConcreteTypeInformation(ConcreteTypeInformation info) {} |
| 267 bailout('Stored in a map'); | |
| 268 } | |
| 269 | 252 |
| 270 visitConcreteTypeInformation(ConcreteTypeInformation info) {} | 253 void visitClosureTypeInformation(ConcreteTypeInformation info) {} |
| 271 | 254 |
| 272 visitClosureCallSiteTypeInformation(ClosureCallSiteTypeInformation info) { | 255 void visitClosureCallSiteTypeInformation( |
| 273 bailout('Passed to a closure'); | 256 ClosureCallSiteTypeInformation info) {} |
| 274 } | |
| 275 | 257 |
| 276 visitStaticCallSiteTypeInformation(StaticCallSiteTypeInformation info) { | 258 visitStaticCallSiteTypeInformation(StaticCallSiteTypeInformation info) { |
| 277 Element called = info.calledElement; | 259 Element called = info.calledElement; |
| 278 if (called.isForeign(compiler) && called.name == 'JS') { | |
| 279 bailout('Used in JS ${info.call}'); | |
| 280 } | |
| 281 if (inferrer.types.getInferredTypeOf(called) == currentUser) { | 260 if (inferrer.types.getInferredTypeOf(called) == currentUser) { |
| 282 addNewEscapeInformation(info); | 261 addNewEscapeInformation(info); |
| 283 } | 262 } |
| 284 } | 263 } |
| 285 | 264 |
| 286 void analyzeStoredIntoContainer(ListTypeInformation container) { | 265 void analyzeStoredIntoContainer(ListTypeInformation container) { |
| 287 inferrer.analyzeContainer(container); | 266 inferrer.analyzeContainer(container); |
| 288 if (container.bailedOut) { | 267 if (container.bailedOut) { |
| 289 bailout('Stored in a container that bailed out'); | 268 bailout('Stored in a container that bailed out'); |
| 290 } else { | 269 } else { |
| 291 container.flowsInto.forEach((flow) { | 270 container.flowsInto.forEach((flow) { |
| 292 flow.users.forEach((user) { | 271 flow.users.forEach((user) { |
| 293 if (user is !DynamicCallSiteTypeInformation) return; | 272 if (user is !DynamicCallSiteTypeInformation) return; |
| 294 if (user.receiver != flow) return; | 273 if (user.receiver != flow) return; |
| 295 if (returnsElementTypeSet.contains(user.selector)) { | 274 if (returnsElementTypeSet.contains(user.selector)) { |
| 296 addNewEscapeInformation(user); | 275 addNewEscapeInformation(user); |
| 297 } else if (!doesNotEscapeElementSet.contains(user.selector.name)) { | 276 } else if (!doesNotEscapeElementSet.contains(user.selector.name)) { |
| 298 bailout('Escape from a container'); | 277 bailout('Escape from a container'); |
| 299 } | 278 } |
| 300 }); | 279 }); |
| 301 }); | 280 }); |
| 302 } | 281 } |
| 303 } | 282 } |
| 304 | 283 |
| 305 bool isAddedToContainer(DynamicCallSiteTypeInformation info) { | 284 bool isAddedToContainer(DynamicCallSiteTypeInformation info) { |
| 285 if (info.arguments == null) return false; |
| 306 var receiverType = info.receiver.type; | 286 var receiverType = info.receiver.type; |
| 307 if (!receiverType.isContainer) return false; | 287 if (!receiverType.isContainer) return false; |
| 308 String selectorName = info.selector.name; | 288 String selectorName = info.selector.name; |
| 309 List<TypeInformation> arguments = info.arguments.positional; | 289 List<TypeInformation> arguments = info.arguments.positional; |
| 310 return (selectorName == '[]=' && currentUser == arguments[1]) | 290 return (selectorName == '[]=' && currentUser == arguments[1]) |
| 311 || (selectorName == 'insert' && currentUser == arguments[0]) | 291 || (selectorName == 'insert' && currentUser == arguments[0]) |
| 312 || (selectorName == 'add' && currentUser == arguments[0]); | 292 || (selectorName == 'add' && currentUser == arguments[0]); |
| 313 } | 293 } |
| 314 | 294 |
| 295 void visitDynamicCallSiteTypeInformation( |
| 296 DynamicCallSiteTypeInformation info) { |
| 297 if (isAddedToContainer(info)) { |
| 298 ContainerTypeMask mask = info.receiver.type; |
| 299 if (mask.allocationNode != null) { |
| 300 ListTypeInformation container = |
| 301 inferrer.types.allocatedLists[mask.allocationNode]; |
| 302 containersToAnalyze.add(container); |
| 303 } else { |
| 304 // The [ContainerTypeMask] is a union of two containers, and |
| 305 // we lose track of where these containers have been allocated |
| 306 // at this point. |
| 307 bailout('Stored in too many containers'); |
| 308 } |
| 309 } |
| 310 |
| 311 Iterable<Element> inferredTargetTypes = info.targets.map((element) { |
| 312 return inferrer.types.getInferredTypeOf(element); |
| 313 }); |
| 314 if (inferredTargetTypes.any((user) => user == currentUser)) { |
| 315 addNewEscapeInformation(info); |
| 316 } |
| 317 } |
| 318 |
| 319 bool isParameterOfListAddingMethod(Element element) { |
| 320 if (!element.isParameter()) return false; |
| 321 if (element.getEnclosingClass() != compiler.backend.listImplementation) { |
| 322 return false; |
| 323 } |
| 324 Element method = element.enclosingElement; |
| 325 return (method.name == '[]=') |
| 326 || (method.name == 'add') |
| 327 || (method.name == 'insert'); |
| 328 } |
| 329 |
| 330 void visitElementTypeInformation(ElementTypeInformation info) { |
| 331 if (isParameterOfListAddingMethod(info.element)) { |
| 332 // These elements are being handled in |
| 333 // [visitDynamicCallSiteTypeInformation]. |
| 334 return; |
| 335 } |
| 336 addNewEscapeInformation(info); |
| 337 } |
| 338 } |
| 339 |
| 340 class ContainerTracerVisitor extends TracerVisitor { |
| 341 // The list of found assignments to the container. |
| 342 final List<TypeInformation> assignments = <TypeInformation>[]; |
| 343 bool callsGrowableMethod = false; |
| 344 |
| 345 ContainerTracerVisitor(tracedType, inferrer) : super(tracedType, inferrer); |
| 346 |
| 347 List<TypeInformation> run() { |
| 348 analyze(); |
| 349 ListTypeInformation container = tracedType; |
| 350 if (continueAnalyzing) { |
| 351 if (!callsGrowableMethod && container.inferredLength == null) { |
| 352 container.inferredLength = container.originalLength; |
| 353 } |
| 354 container.flowsInto.addAll(flowsInto); |
| 355 return assignments; |
| 356 } else { |
| 357 callsGrowableMethod = true; |
| 358 return null; |
| 359 } |
| 360 } |
| 361 |
| 362 visitMapTypeInformation(MapTypeInformation info) { |
| 363 bailout('Stored in a map'); |
| 364 } |
| 365 |
| 366 visitClosureCallSiteTypeInformation(ClosureCallSiteTypeInformation info) { |
| 367 bailout('Passed to a closure'); |
| 368 } |
| 369 |
| 370 visitStaticCallSiteTypeInformation(StaticCallSiteTypeInformation info) { |
| 371 super.visitStaticCallSiteTypeInformation(info); |
| 372 Element called = info.calledElement; |
| 373 if (called.isForeign(compiler) && called.name == 'JS') { |
| 374 bailout('Used in JS ${info.call}'); |
| 375 } |
| 376 } |
| 377 |
| 315 visitDynamicCallSiteTypeInformation(DynamicCallSiteTypeInformation info) { | 378 visitDynamicCallSiteTypeInformation(DynamicCallSiteTypeInformation info) { |
| 379 super.visitDynamicCallSiteTypeInformation(info); |
| 316 Selector selector = info.selector; | 380 Selector selector = info.selector; |
| 317 String selectorName = selector.name; | 381 String selectorName = selector.name; |
| 318 if (currentUser == info.receiver) { | 382 if (currentUser == info.receiver) { |
| 319 if (!okSelectorsSet.contains(selectorName)) { | 383 if (!okSelectorsSet.contains(selectorName)) { |
| 320 if (selector.isCall()) { | 384 if (selector.isCall()) { |
| 321 int positionalLength = info.arguments.positional.length; | 385 int positionalLength = info.arguments.positional.length; |
| 322 if (selectorName == 'add') { | 386 if (selectorName == 'add') { |
| 323 if (positionalLength == 1) { | 387 if (positionalLength == 1) { |
| 324 assignments.add(info.arguments.positional[0]); | 388 assignments.add(info.arguments.positional[0]); |
| 325 } | 389 } |
| (...skipping 16 matching lines...) Expand all Loading... |
| 342 callsGrowableMethod = true; | 406 callsGrowableMethod = true; |
| 343 } | 407 } |
| 344 if (selectorName == 'length' && selector.isSetter()) { | 408 if (selectorName == 'length' && selector.isSetter()) { |
| 345 callsGrowableMethod = true; | 409 callsGrowableMethod = true; |
| 346 assignments.add(inferrer.types.nullType); | 410 assignments.add(inferrer.types.nullType); |
| 347 } | 411 } |
| 348 } else if (selector.isCall() | 412 } else if (selector.isCall() |
| 349 && !info.targets.every((element) => element.isFunction())) { | 413 && !info.targets.every((element) => element.isFunction())) { |
| 350 bailout('Passed to a closure'); | 414 bailout('Passed to a closure'); |
| 351 return; | 415 return; |
| 352 } else if (isAddedToContainer(info)) { | |
| 353 ContainerTypeMask mask = info.receiver.type; | |
| 354 if (mask.allocationNode != null) { | |
| 355 ListTypeInformation container = | |
| 356 inferrer.types.allocatedLists[mask.allocationNode]; | |
| 357 containersToAnalyze.add(container); | |
| 358 } else { | |
| 359 // The [ContainerTypeMask] is a union of two containers, and | |
| 360 // we lose track of where these containers have been allocated | |
| 361 // at this point. | |
| 362 bailout('Stored in too many containers'); | |
| 363 } | |
| 364 } | |
| 365 | |
| 366 if (info.targets | |
| 367 .map((element) => inferrer.types.getInferredTypeOf(element)) | |
| 368 .any((other) => other == currentUser)) { | |
| 369 addNewEscapeInformation(info); | |
| 370 } | 416 } |
| 371 } | 417 } |
| 372 | 418 |
| 373 bool isClosure(Element element) { | 419 bool isClosure(Element element) { |
| 374 if (!element.isFunction()) return false; | 420 if (!element.isFunction()) return false; |
| 375 Element outermost = element.getOutermostEnclosingMemberOrTopLevel(); | 421 Element outermost = element.getOutermostEnclosingMemberOrTopLevel(); |
| 376 return outermost.declaration != element.declaration; | 422 return outermost.declaration != element.declaration; |
| 377 } | 423 } |
| 378 | 424 |
| 379 bool isParameterOfListAddingMethod(Element element) { | |
| 380 if (!element.isParameter()) return false; | |
| 381 if (element.getEnclosingClass() != compiler.backend.listImplementation) { | |
| 382 return false; | |
| 383 } | |
| 384 Element method = element.enclosingElement; | |
| 385 return (method.name == '[]=') | |
| 386 || (method.name == 'add') | |
| 387 || (method.name == 'insert'); | |
| 388 } | |
| 389 | |
| 390 visitElementTypeInformation(ElementTypeInformation info) { | 425 visitElementTypeInformation(ElementTypeInformation info) { |
| 426 super.visitElementTypeInformation(info); |
| 391 if (isClosure(info.element)) { | 427 if (isClosure(info.element)) { |
| 392 bailout('Returned from a closure'); | 428 bailout('Returned from a closure'); |
| 393 } | 429 } |
| 394 if (compiler.backend.isNeededForReflection(info.element)) { | 430 if (compiler.backend.isNeededForReflection(info.element)) { |
| 395 bailout('Escape in reflection'); | 431 bailout('Escape in reflection'); |
| 396 } | 432 } |
| 397 if (isParameterOfListAddingMethod(info.element)) { | |
| 398 // These elements are being handled in | |
| 399 // [visitDynamicCallSiteTypeInformation]. | |
| 400 return; | |
| 401 } | |
| 402 addNewEscapeInformation(info); | |
| 403 } | 433 } |
| 404 } | 434 } |
| 435 |
| 436 class ClosureTracerVisitor extends TracerVisitor { |
| 437 ClosureTracerVisitor(tracedType, inferrer) : super(tracedType, inferrer); |
| 438 |
| 439 void run() { |
| 440 ClosureTypeInformation closure = tracedType; |
| 441 FunctionElement element = closure.element; |
| 442 element.functionSignature.forEachParameter((Element parameter) { |
| 443 ElementTypeInformation info = inferrer.types.getInferredTypeOf(parameter); |
| 444 info.abandonInferencing = false; |
| 445 }); |
| 446 analyze(); |
| 447 element.functionSignature.forEachParameter((Element parameter) { |
| 448 ElementTypeInformation info = inferrer.types.getInferredTypeOf(parameter); |
| 449 if (continueAnalyzing) { |
| 450 info.disableHandleSpecialCases = true; |
| 451 } else { |
| 452 info.giveUp(inferrer); |
| 453 } |
| 454 }); |
| 455 } |
| 456 |
| 457 visitMapTypeInformation(MapTypeInformation info) { |
| 458 bailout('Stored in a map'); |
| 459 } |
| 460 |
| 461 void analyzeCall(CallSiteTypeInformation info) { |
| 462 ClosureTypeInformation closure = tracedType; |
| 463 FunctionElement element = closure.element; |
| 464 Selector selector = info.selector; |
| 465 if (!selector.signatureApplies(element, compiler)) return; |
| 466 inferrer.updateParameterAssignments( |
| 467 info, element, info.arguments, selector, remove: false, |
| 468 addToQueue: false); |
| 469 } |
| 470 |
| 471 visitClosureCallSiteTypeInformation(ClosureCallSiteTypeInformation info) { |
| 472 super.visitClosureCallSiteTypeInformation(info); |
| 473 if (info.closure == currentUser) { |
| 474 analyzeCall(info); |
| 475 } else { |
| 476 bailout('Passed to a closure'); |
| 477 } |
| 478 } |
| 479 |
| 480 visitStaticCallSiteTypeInformation(StaticCallSiteTypeInformation info) { |
| 481 super.visitStaticCallSiteTypeInformation(info); |
| 482 Element called = info.calledElement; |
| 483 if (called.isForeign(compiler) && called.name == 'JS') { |
| 484 bailout('Used in JS ${info.call}'); |
| 485 } |
| 486 } |
| 487 |
| 488 bool checkIfCurrentUser(element) { |
| 489 return inferrer.types.getInferredTypeOf(element) == currentUser; |
| 490 } |
| 491 |
| 492 visitDynamicCallSiteTypeInformation(DynamicCallSiteTypeInformation info) { |
| 493 super.visitDynamicCallSiteTypeInformation(info); |
| 494 if (info.selector.isCall()) { |
| 495 if (info.arguments.contains(currentUser) |
| 496 && !info.targets.every((element) => element.isFunction())) { |
| 497 bailout('Passed to a closure'); |
| 498 } else if (info.targets.any((element) => checkIfCurrentUser(element))) { |
| 499 analyzeCall(info); |
| 500 } |
| 501 } |
| 502 } |
| 503 |
| 504 bool isClosure(Element element) { |
| 505 if (!element.isFunction()) return false; |
| 506 Element outermost = element.getOutermostEnclosingMemberOrTopLevel(); |
| 507 return outermost.declaration != element.declaration; |
| 508 } |
| 509 |
| 510 visitElementTypeInformation(ElementTypeInformation info) { |
| 511 super.visitElementTypeInformation(info); |
| 512 if (isClosure(info.element)) { |
| 513 bailout('Returned from a closure'); |
| 514 } |
| 515 if (compiler.backend.isNeededForReflection(info.element)) { |
| 516 bailout('Escape in reflection'); |
| 517 } |
| 518 } |
| 519 } |
| OLD | NEW |