OLD | NEW |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, 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 import '../compiler.dart' show Compiler; | 5 import '../compiler.dart' show Compiler; |
6 import '../elements/elements.dart'; | 6 import '../elements/elements.dart'; |
7 import '../js_backend/js_backend.dart'; | 7 import '../js_backend/js_backend.dart'; |
8 import '../types/types.dart'; | 8 import '../types/types.dart'; |
9 import '../universe/selector.dart' show Selector; | 9 import '../universe/selector.dart' show Selector; |
10 import '../world.dart' show ClassWorld, World; | 10 import '../world.dart' show ClassWorld, World; |
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
91 } | 91 } |
92 } | 92 } |
93 // While processing the optimizable arithmetic instructions, we | 93 // While processing the optimizable arithmetic instructions, we |
94 // may discover better type information for dominated users of | 94 // may discover better type information for dominated users of |
95 // replaced operands, so we may need to take another stab at | 95 // replaced operands, so we may need to take another stab at |
96 // emptying the worklist afterwards. | 96 // emptying the worklist afterwards. |
97 processPendingOptimizations(); | 97 processPendingOptimizations(); |
98 } while (!worklist.isEmpty); | 98 } while (!worklist.isEmpty); |
99 } | 99 } |
100 | 100 |
101 | |
102 void addToWorkList(HInstruction instruction) { | 101 void addToWorkList(HInstruction instruction) { |
103 final int id = instruction.id; | 102 final int id = instruction.id; |
104 | 103 |
105 if (!workmap.containsKey(id)) { | 104 if (!workmap.containsKey(id)) { |
106 worklist.add(id); | 105 worklist.add(id); |
107 workmap[id] = instruction; | 106 workmap[id] = instruction; |
108 } | 107 } |
109 } | 108 } |
110 | 109 |
111 TypeMask visitBinaryArithmetic(HBinaryArithmetic instruction) { | 110 TypeMask visitBinaryArithmetic(HBinaryArithmetic instruction) { |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
168 } | 167 } |
169 | 168 |
170 TypeMask visitTypeConversion(HTypeConversion instruction) { | 169 TypeMask visitTypeConversion(HTypeConversion instruction) { |
171 HInstruction input = instruction.checkedInput; | 170 HInstruction input = instruction.checkedInput; |
172 TypeMask inputType = input.instructionType; | 171 TypeMask inputType = input.instructionType; |
173 TypeMask checkedType = instruction.checkedType; | 172 TypeMask checkedType = instruction.checkedType; |
174 if (instruction.isArgumentTypeCheck || instruction.isReceiverTypeCheck) { | 173 if (instruction.isArgumentTypeCheck || instruction.isReceiverTypeCheck) { |
175 // We must make sure a type conversion for receiver or argument check | 174 // We must make sure a type conversion for receiver or argument check |
176 // does not try to do an int check, because an int check is not enough. | 175 // does not try to do an int check, because an int check is not enough. |
177 // We only do an int check if the input is integer or null. | 176 // We only do an int check if the input is integer or null. |
178 if (checkedType.containsOnlyNum(classWorld) | 177 if (checkedType.containsOnlyNum(classWorld) && |
179 && !checkedType.containsOnlyDouble(classWorld) | 178 !checkedType.containsOnlyDouble(classWorld) && |
180 && input.isIntegerOrNull(compiler)) { | 179 input.isIntegerOrNull(compiler)) { |
181 instruction.checkedType = backend.intType; | 180 instruction.checkedType = backend.intType; |
182 } else if (checkedType.containsOnlyInt(classWorld) | 181 } else if (checkedType.containsOnlyInt(classWorld) && |
183 && !input.isIntegerOrNull(compiler)) { | 182 !input.isIntegerOrNull(compiler)) { |
184 instruction.checkedType = backend.numType; | 183 instruction.checkedType = backend.numType; |
185 } | 184 } |
186 } | 185 } |
187 | 186 |
188 TypeMask outputType = checkedType.intersection(inputType, classWorld); | 187 TypeMask outputType = checkedType.intersection(inputType, classWorld); |
189 if (outputType.isEmpty) { | 188 if (outputType.isEmpty) { |
190 // Intersection of double and integer conflicts (is empty), but JS numbers | 189 // Intersection of double and integer conflicts (is empty), but JS numbers |
191 // can be both int and double at the same time. For example, the input | 190 // can be both int and double at the same time. For example, the input |
192 // can be a literal double '8.0' that is marked as an integer (because 'is | 191 // can be a literal double '8.0' that is marked as an integer (because 'is |
193 // int' will return 'true'). What we really need to do is make the | 192 // int' will return 'true'). What we really need to do is make the |
194 // overlap between int and double values explicit in the TypeMask system. | 193 // overlap between int and double values explicit in the TypeMask system. |
195 if (inputType.containsOnlyInt(classWorld) | 194 if (inputType.containsOnlyInt(classWorld) && |
196 && checkedType.containsOnlyDouble(classWorld)) { | 195 checkedType.containsOnlyDouble(classWorld)) { |
197 if (inputType.isNullable && checkedType.isNullable) { | 196 if (inputType.isNullable && checkedType.isNullable) { |
198 outputType = backend.doubleType.nullable(); | 197 outputType = backend.doubleType.nullable(); |
199 } else { | 198 } else { |
200 outputType = backend.doubleType; | 199 outputType = backend.doubleType; |
201 } | 200 } |
202 } | 201 } |
203 } | 202 } |
204 if (inputType != outputType) { | 203 if (inputType != outputType) { |
205 input.replaceAllUsersDominatedBy(instruction.next, instruction); | 204 input.replaceAllUsersDominatedBy(instruction.next, instruction); |
206 } | 205 } |
207 return outputType; | 206 return outputType; |
208 } | 207 } |
209 | 208 |
210 TypeMask visitTypeKnown(HTypeKnown instruction) { | 209 TypeMask visitTypeKnown(HTypeKnown instruction) { |
211 HInstruction input = instruction.checkedInput; | 210 HInstruction input = instruction.checkedInput; |
212 TypeMask inputType = input.instructionType; | 211 TypeMask inputType = input.instructionType; |
213 TypeMask outputType = | 212 TypeMask outputType = |
214 instruction.knownType.intersection(inputType, classWorld); | 213 instruction.knownType.intersection(inputType, classWorld); |
215 if (inputType != outputType) { | 214 if (inputType != outputType) { |
216 input.replaceAllUsersDominatedBy(instruction.next, instruction); | 215 input.replaceAllUsersDominatedBy(instruction.next, instruction); |
217 } | 216 } |
218 return outputType; | 217 return outputType; |
219 } | 218 } |
220 | 219 |
221 void convertInput(HInvokeDynamic instruction, | 220 void convertInput( |
222 HInstruction input, | 221 HInvokeDynamic instruction, HInstruction input, TypeMask type, int kind) { |
223 TypeMask type, | |
224 int kind) { | |
225 Selector selector = (kind == HTypeConversion.RECEIVER_TYPE_CHECK) | 222 Selector selector = (kind == HTypeConversion.RECEIVER_TYPE_CHECK) |
226 ? instruction.selector | 223 ? instruction.selector |
227 : null; | 224 : null; |
228 HTypeConversion converted = new HTypeConversion( | 225 HTypeConversion converted = |
229 null, kind, type, input, selector) | 226 new HTypeConversion(null, kind, type, input, selector) |
230 ..sourceInformation = instruction.sourceInformation; | 227 ..sourceInformation = instruction.sourceInformation; |
231 instruction.block.addBefore(instruction, converted); | 228 instruction.block.addBefore(instruction, converted); |
232 input.replaceAllUsersDominatedBy(instruction, converted); | 229 input.replaceAllUsersDominatedBy(instruction, converted); |
233 } | 230 } |
234 | 231 |
235 bool isCheckEnoughForNsmOrAe(HInstruction instruction, | 232 bool isCheckEnoughForNsmOrAe(HInstruction instruction, TypeMask type) { |
236 TypeMask type) { | |
237 // In some cases, we want the receiver to be an integer, | 233 // In some cases, we want the receiver to be an integer, |
238 // but that does not mean we will get a NoSuchMethodError | 234 // but that does not mean we will get a NoSuchMethodError |
239 // if it's not: the receiver could be a double. | 235 // if it's not: the receiver could be a double. |
240 if (type.containsOnlyInt(classWorld)) { | 236 if (type.containsOnlyInt(classWorld)) { |
241 // If the instruction's type is integer or null, the codegen | 237 // If the instruction's type is integer or null, the codegen |
242 // will emit a null check, which is enough to know if it will | 238 // will emit a null check, which is enough to know if it will |
243 // hit a noSuchMethod. | 239 // hit a noSuchMethod. |
244 return instruction.isIntegerOrNull(compiler); | 240 return instruction.isIntegerOrNull(compiler); |
245 } | 241 } |
246 return true; | 242 return true; |
247 } | 243 } |
248 | 244 |
249 // Add a receiver type check when the call can only hit | 245 // Add a receiver type check when the call can only hit |
250 // [noSuchMethod] if the receiver is not of a specific type. | 246 // [noSuchMethod] if the receiver is not of a specific type. |
251 // Return true if the receiver type check was added. | 247 // Return true if the receiver type check was added. |
252 bool checkReceiver(HInvokeDynamic instruction) { | 248 bool checkReceiver(HInvokeDynamic instruction) { |
253 assert(instruction.isInterceptedCall); | 249 assert(instruction.isInterceptedCall); |
254 HInstruction receiver = instruction.inputs[1]; | 250 HInstruction receiver = instruction.inputs[1]; |
255 if (receiver.isNumber(compiler)) return false; | 251 if (receiver.isNumber(compiler)) return false; |
256 if (receiver.isNumberOrNull(compiler)) { | 252 if (receiver.isNumberOrNull(compiler)) { |
257 convertInput(instruction, | 253 convertInput( |
258 receiver, | 254 instruction, |
259 receiver.instructionType.nonNullable(), | 255 receiver, |
260 HTypeConversion.RECEIVER_TYPE_CHECK); | 256 receiver.instructionType.nonNullable(), |
| 257 HTypeConversion.RECEIVER_TYPE_CHECK); |
261 return true; | 258 return true; |
262 } else if (instruction.element == null) { | 259 } else if (instruction.element == null) { |
263 Iterable<Element> targets = | 260 Iterable<Element> targets = compiler.world.allFunctions |
264 compiler.world.allFunctions.filter( | 261 .filter(instruction.selector, instruction.mask); |
265 instruction.selector, instruction.mask); | |
266 if (targets.length == 1) { | 262 if (targets.length == 1) { |
267 Element target = targets.first; | 263 Element target = targets.first; |
268 ClassElement cls = target.enclosingClass; | 264 ClassElement cls = target.enclosingClass; |
269 TypeMask type = new TypeMask.nonNullSubclass( | 265 TypeMask type = |
270 cls.declaration, classWorld); | 266 new TypeMask.nonNullSubclass(cls.declaration, classWorld); |
271 // TODO(ngeoffray): We currently only optimize on primitive | 267 // TODO(ngeoffray): We currently only optimize on primitive |
272 // types. | 268 // types. |
273 if (!type.satisfies(backend.helpers.jsIndexableClass, classWorld) && | 269 if (!type.satisfies(backend.helpers.jsIndexableClass, classWorld) && |
274 !type.containsOnlyNum(classWorld) && | 270 !type.containsOnlyNum(classWorld) && |
275 !type.containsOnlyBool(classWorld)) { | 271 !type.containsOnlyBool(classWorld)) { |
276 return false; | 272 return false; |
277 } | 273 } |
278 if (!isCheckEnoughForNsmOrAe(receiver, type)) return false; | 274 if (!isCheckEnoughForNsmOrAe(receiver, type)) return false; |
279 instruction.element = target; | 275 instruction.element = target; |
280 convertInput(instruction, | 276 convertInput( |
281 receiver, | 277 instruction, receiver, type, HTypeConversion.RECEIVER_TYPE_CHECK); |
282 type, | |
283 HTypeConversion.RECEIVER_TYPE_CHECK); | |
284 return true; | 278 return true; |
285 } | 279 } |
286 } | 280 } |
287 return false; | 281 return false; |
288 } | 282 } |
289 | 283 |
290 // Add an argument type check if the argument is not of a type | 284 // Add an argument type check if the argument is not of a type |
291 // expected by the call. | 285 // expected by the call. |
292 // Return true if the argument type check was added. | 286 // Return true if the argument type check was added. |
293 bool checkArgument(HInvokeDynamic instruction) { | 287 bool checkArgument(HInvokeDynamic instruction) { |
294 // We want the right error in checked mode. | 288 // We want the right error in checked mode. |
295 if (compiler.options.enableTypeAssertions) return false; | 289 if (compiler.options.enableTypeAssertions) return false; |
296 HInstruction left = instruction.inputs[1]; | 290 HInstruction left = instruction.inputs[1]; |
297 HInstruction right = instruction.inputs[2]; | 291 HInstruction right = instruction.inputs[2]; |
298 | 292 |
299 Selector selector = instruction.selector; | 293 Selector selector = instruction.selector; |
300 if (selector.isOperator && left.isNumber(compiler)) { | 294 if (selector.isOperator && left.isNumber(compiler)) { |
301 if (right.isNumber(compiler)) return false; | 295 if (right.isNumber(compiler)) return false; |
302 TypeMask type = right.isIntegerOrNull(compiler) | 296 TypeMask type = right.isIntegerOrNull(compiler) |
303 ? right.instructionType.nonNullable() | 297 ? right.instructionType.nonNullable() |
304 : backend.numType; | 298 : backend.numType; |
305 // TODO(ngeoffray): Some number operations don't have a builtin | 299 // TODO(ngeoffray): Some number operations don't have a builtin |
306 // variant and will do the check in their method anyway. We | 300 // variant and will do the check in their method anyway. We |
307 // still add a check because it allows to GVN these operations, | 301 // still add a check because it allows to GVN these operations, |
308 // but we should find a better way. | 302 // but we should find a better way. |
309 convertInput(instruction, | 303 convertInput( |
310 right, | 304 instruction, right, type, HTypeConversion.ARGUMENT_TYPE_CHECK); |
311 type, | |
312 HTypeConversion.ARGUMENT_TYPE_CHECK); | |
313 return true; | 305 return true; |
314 } | 306 } |
315 return false; | 307 return false; |
316 } | 308 } |
317 | 309 |
318 void processPendingOptimizations() { | 310 void processPendingOptimizations() { |
319 pendingOptimizations.forEach((instruction, action) => action()); | 311 pendingOptimizations.forEach((instruction, action) => action()); |
320 pendingOptimizations.clear(); | 312 pendingOptimizations.clear(); |
321 } | 313 } |
322 | 314 |
323 void addDependentInstructionsToWorkList(HInstruction instruction) { | 315 void addDependentInstructionsToWorkList(HInstruction instruction) { |
324 for (int i = 0, length = instruction.usedBy.length; i < length; i++) { | 316 for (int i = 0, length = instruction.usedBy.length; i < length; i++) { |
325 // The type propagator only propagates types forward. We | 317 // The type propagator only propagates types forward. We |
326 // thus only need to add the users of the [instruction] to the list. | 318 // thus only need to add the users of the [instruction] to the list. |
327 addToWorkList(instruction.usedBy[i]); | 319 addToWorkList(instruction.usedBy[i]); |
328 } | 320 } |
329 } | 321 } |
330 | 322 |
331 void addAllUsersBut(HInvokeDynamic invoke, HInstruction instruction) { | 323 void addAllUsersBut(HInvokeDynamic invoke, HInstruction instruction) { |
332 instruction.usedBy.forEach((HInstruction user) { | 324 instruction.usedBy.forEach((HInstruction user) { |
333 if (user != invoke) addToWorkList(user); | 325 if (user != invoke) addToWorkList(user); |
334 }); | 326 }); |
335 } | 327 } |
336 | 328 |
337 TypeMask visitInvokeDynamic(HInvokeDynamic instruction) { | 329 TypeMask visitInvokeDynamic(HInvokeDynamic instruction) { |
338 if (instruction.isInterceptedCall) { | 330 if (instruction.isInterceptedCall) { |
339 // We cannot do the following optimization now, because we have | 331 // We cannot do the following optimization now, because we have |
340 // to wait for the type propagation to be stable. The receiver | 332 // to wait for the type propagation to be stable. The receiver |
341 // of [instruction] might move from number to dynamic. | 333 // of [instruction] might move from number to dynamic. |
342 pendingOptimizations.putIfAbsent(instruction, () => () { | 334 pendingOptimizations.putIfAbsent( |
343 Selector selector = instruction.selector; | 335 instruction, |
344 if (selector.isOperator && selector.name != '==') { | 336 () => () { |
345 if (checkReceiver(instruction)) { | 337 Selector selector = instruction.selector; |
346 addAllUsersBut(instruction, instruction.inputs[1]); | 338 if (selector.isOperator && selector.name != '==') { |
347 } | 339 if (checkReceiver(instruction)) { |
348 if (!selector.isUnaryOperator && | 340 addAllUsersBut(instruction, instruction.inputs[1]); |
349 checkArgument(instruction)) { | 341 } |
350 addAllUsersBut(instruction, instruction.inputs[2]); | 342 if (!selector.isUnaryOperator && checkArgument(instruction)) { |
351 } | 343 addAllUsersBut(instruction, instruction.inputs[2]); |
352 } | 344 } |
353 }); | 345 } |
| 346 }); |
354 } | 347 } |
355 | 348 |
356 HInstruction receiver = instruction.getDartReceiver(compiler); | 349 HInstruction receiver = instruction.getDartReceiver(compiler); |
357 TypeMask receiverType = receiver.instructionType; | 350 TypeMask receiverType = receiver.instructionType; |
358 instruction.mask = receiverType; | 351 instruction.mask = receiverType; |
359 | 352 |
360 // Try to specialize the receiver after this call. | 353 // Try to specialize the receiver after this call. |
361 if (receiver.dominatedUsers(instruction).length != 1 | 354 if (receiver.dominatedUsers(instruction).length != 1 && |
362 && !instruction.selector.isClosureCall) { | 355 !instruction.selector.isClosureCall) { |
363 TypeMask newType = compiler.world.allFunctions.receiverType( | 356 TypeMask newType = compiler.world.allFunctions |
364 instruction.selector, instruction.mask); | 357 .receiverType(instruction.selector, instruction.mask); |
365 newType = newType.intersection(receiverType, classWorld); | 358 newType = newType.intersection(receiverType, classWorld); |
366 var next = instruction.next; | 359 var next = instruction.next; |
367 if (next is HTypeKnown && next.checkedInput == receiver) { | 360 if (next is HTypeKnown && next.checkedInput == receiver) { |
368 // We already have refined [receiver]. We still update the | 361 // We already have refined [receiver]. We still update the |
369 // type of the [HTypeKnown] instruction because it may have | 362 // type of the [HTypeKnown] instruction because it may have |
370 // been refined with a correct type at the time, but | 363 // been refined with a correct type at the time, but |
371 // incorrect now. | 364 // incorrect now. |
372 if (next.instructionType != newType) { | 365 if (next.instructionType != newType) { |
373 next.knownType = next.instructionType = newType; | 366 next.knownType = next.instructionType = newType; |
374 addDependentInstructionsToWorkList(next); | 367 addDependentInstructionsToWorkList(next); |
375 } | 368 } |
376 } else if (newType != receiverType) { | 369 } else if (newType != receiverType) { |
377 // Insert a refinement node after the call and update all | 370 // Insert a refinement node after the call and update all |
378 // users dominated by the call to use that node instead of | 371 // users dominated by the call to use that node instead of |
379 // [receiver]. | 372 // [receiver]. |
380 HTypeKnown converted = | 373 HTypeKnown converted = |
381 new HTypeKnown.witnessed(newType, receiver, instruction); | 374 new HTypeKnown.witnessed(newType, receiver, instruction); |
382 instruction.block.addBefore(instruction.next, converted); | 375 instruction.block.addBefore(instruction.next, converted); |
383 receiver.replaceAllUsersDominatedBy(converted.next, converted); | 376 receiver.replaceAllUsersDominatedBy(converted.next, converted); |
384 addDependentInstructionsToWorkList(converted); | 377 addDependentInstructionsToWorkList(converted); |
385 } | 378 } |
386 } | 379 } |
387 | 380 |
388 return instruction.specializer.computeTypeFromInputTypes( | 381 return instruction.specializer |
389 instruction, compiler); | 382 .computeTypeFromInputTypes(instruction, compiler); |
390 } | 383 } |
391 } | 384 } |
OLD | NEW |