OLD | NEW |
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 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 | 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 library dart2js.cps_ir.type_propagation; | 4 library dart2js.cps_ir.type_propagation; |
5 | 5 |
6 import 'optimizers.dart'; | 6 import 'optimizers.dart'; |
7 | 7 |
8 import '../closure.dart' show | 8 import '../closure.dart' show ClosureClassElement; |
9 ClosureClassElement; | |
10 import '../common.dart'; | 9 import '../common.dart'; |
11 import '../common/names.dart' show | 10 import '../common/names.dart' show Identifiers, Selectors; |
12 Identifiers, | 11 import '../compiler.dart' as dart2js show Compiler; |
13 Selectors; | |
14 import '../compiler.dart' as dart2js show | |
15 Compiler; | |
16 import '../constants/constant_system.dart'; | 12 import '../constants/constant_system.dart'; |
17 import '../constants/values.dart'; | 13 import '../constants/values.dart'; |
18 import '../dart_types.dart' as types; | 14 import '../dart_types.dart' as types; |
19 import '../elements/elements.dart'; | 15 import '../elements/elements.dart'; |
20 import '../io/source_information.dart' show | 16 import '../io/source_information.dart' show SourceInformation; |
21 SourceInformation; | 17 import '../js_backend/backend_helpers.dart' show BackendHelpers; |
22 import '../js_backend/backend_helpers.dart' show | 18 import '../js_backend/js_backend.dart' show JavaScriptBackend; |
23 BackendHelpers; | 19 import '../js_backend/codegen/task.dart' show CpsFunctionCompiler; |
24 import '../js_backend/js_backend.dart' show | |
25 JavaScriptBackend; | |
26 import '../js_backend/codegen/task.dart' show | |
27 CpsFunctionCompiler; | |
28 import '../resolution/operators.dart'; | 20 import '../resolution/operators.dart'; |
29 import '../tree/tree.dart' as ast; | 21 import '../tree/tree.dart' as ast; |
30 import '../types/types.dart'; | 22 import '../types/types.dart'; |
31 import '../types/abstract_value_domain.dart' show | 23 import '../types/abstract_value_domain.dart' show AbstractBool; |
32 AbstractBool; | 24 import '../universe/selector.dart' show Selector; |
33 import '../universe/selector.dart' show | |
34 Selector; | |
35 import '../world.dart' show World; | 25 import '../world.dart' show World; |
36 import 'cps_fragment.dart'; | 26 import 'cps_fragment.dart'; |
37 import 'cps_ir_nodes.dart'; | 27 import 'cps_ir_nodes.dart'; |
38 import 'type_mask_system.dart'; | 28 import 'type_mask_system.dart'; |
39 import 'effects.dart'; | 29 import 'effects.dart'; |
40 | 30 |
41 class ConstantPropagationLattice { | 31 class ConstantPropagationLattice { |
42 final TypeMaskSystem typeSystem; | 32 final TypeMaskSystem typeSystem; |
43 final ConstantSystem constantSystem; | 33 final ConstantSystem constantSystem; |
44 final types.DartTypes dartTypes; | 34 final types.DartTypes dartTypes; |
45 final AbstractConstantValue anything; | 35 final AbstractConstantValue anything; |
46 final AbstractConstantValue nothing = new AbstractConstantValue.nothing(); | 36 final AbstractConstantValue nothing = new AbstractConstantValue.nothing(); |
47 final AbstractConstantValue nullValue; | 37 final AbstractConstantValue nullValue; |
48 final AbstractConstantValue trueValue; | 38 final AbstractConstantValue trueValue; |
49 final AbstractConstantValue falseValue; | 39 final AbstractConstantValue falseValue; |
50 | 40 |
51 ConstantPropagationLattice(CpsFunctionCompiler functionCompiler) | 41 ConstantPropagationLattice(CpsFunctionCompiler functionCompiler) |
52 : typeSystem = functionCompiler.typeSystem, | 42 : typeSystem = functionCompiler.typeSystem, |
53 constantSystem = functionCompiler.compiler.backend.constantSystem, | 43 constantSystem = functionCompiler.compiler.backend.constantSystem, |
54 dartTypes = functionCompiler.compiler.types, | 44 dartTypes = functionCompiler.compiler.types, |
55 anything = new AbstractConstantValue.nonConstant( | 45 anything = new AbstractConstantValue.nonConstant( |
56 functionCompiler.typeSystem.dynamicType), | 46 functionCompiler.typeSystem.dynamicType), |
57 nullValue = new AbstractConstantValue.constantValue( | 47 nullValue = new AbstractConstantValue.constantValue( |
58 new NullConstantValue(), new TypeMask.empty()), | 48 new NullConstantValue(), new TypeMask.empty()), |
59 trueValue = new AbstractConstantValue.constantValue( | 49 trueValue = new AbstractConstantValue.constantValue( |
60 new TrueConstantValue(), functionCompiler.typeSystem.boolType), | 50 new TrueConstantValue(), functionCompiler.typeSystem.boolType), |
61 falseValue = new AbstractConstantValue.constantValue( | 51 falseValue = new AbstractConstantValue.constantValue( |
62 new FalseConstantValue(), functionCompiler.typeSystem.boolType); | 52 new FalseConstantValue(), functionCompiler.typeSystem.boolType); |
63 | 53 |
64 AbstractConstantValue constant(ConstantValue value, [TypeMask type]) { | 54 AbstractConstantValue constant(ConstantValue value, [TypeMask type]) { |
65 if (type == null) type = typeSystem.getTypeOf(value); | 55 if (type == null) type = typeSystem.getTypeOf(value); |
66 return new AbstractConstantValue.constantValue(value, type); | 56 return new AbstractConstantValue.constantValue(value, type); |
67 } | 57 } |
68 | 58 |
69 AbstractConstantValue nonConstant([TypeMask type]) { | 59 AbstractConstantValue nonConstant([TypeMask type]) { |
70 if (type == null) type = typeSystem.dynamicType; | 60 if (type == null) type = typeSystem.dynamicType; |
71 return new AbstractConstantValue.nonConstant(type); | 61 return new AbstractConstantValue.nonConstant(type); |
72 } | 62 } |
(...skipping 11 matching lines...) Expand all Loading... |
84 return x; | 74 return x; |
85 } else { | 75 } else { |
86 return new AbstractConstantValue.nonConstant( | 76 return new AbstractConstantValue.nonConstant( |
87 typeSystem.join(x.type, y.type)); | 77 typeSystem.join(x.type, y.type)); |
88 } | 78 } |
89 } | 79 } |
90 | 80 |
91 /// True if all members of this value are booleans. | 81 /// True if all members of this value are booleans. |
92 bool isDefinitelyBool(AbstractConstantValue value, {bool allowNull: false}) { | 82 bool isDefinitelyBool(AbstractConstantValue value, {bool allowNull: false}) { |
93 return value.isNothing || | 83 return value.isNothing || |
94 typeSystem.isDefinitelyBool(value.type, allowNull: allowNull); | 84 typeSystem.isDefinitelyBool(value.type, allowNull: allowNull); |
95 } | 85 } |
96 | 86 |
97 /// True if all members of this value are numbers. | 87 /// True if all members of this value are numbers. |
98 bool isDefinitelyNum(AbstractConstantValue value, {bool allowNull: false}) { | 88 bool isDefinitelyNum(AbstractConstantValue value, {bool allowNull: false}) { |
99 return value.isNothing || | 89 return value.isNothing || |
100 typeSystem.isDefinitelyNum(value.type, allowNull: allowNull); | 90 typeSystem.isDefinitelyNum(value.type, allowNull: allowNull); |
101 } | 91 } |
102 | 92 |
103 /// True if all members of this value are strings. | 93 /// True if all members of this value are strings. |
104 bool isDefinitelyString(AbstractConstantValue value, | 94 bool isDefinitelyString(AbstractConstantValue value, |
105 {bool allowNull: false}) { | 95 {bool allowNull: false}) { |
106 return value.isNothing || | 96 return value.isNothing || |
107 typeSystem.isDefinitelyString(value.type, allowNull: allowNull); | 97 typeSystem.isDefinitelyString(value.type, allowNull: allowNull); |
108 } | 98 } |
109 | 99 |
110 /// True if all members of this value are numbers, strings, or booleans. | 100 /// True if all members of this value are numbers, strings, or booleans. |
111 bool isDefinitelyNumStringBool(AbstractConstantValue value, | 101 bool isDefinitelyNumStringBool(AbstractConstantValue value, |
112 {bool allowNull: false}) { | 102 {bool allowNull: false}) { |
113 return value.isNothing || | 103 return value.isNothing || |
114 typeSystem.isDefinitelyNumStringBool(value.type, allowNull: allowNull); | 104 typeSystem.isDefinitelyNumStringBool(value.type, allowNull: allowNull); |
115 } | 105 } |
116 | 106 |
117 /// True if this value cannot be a string, number, or boolean. | 107 /// True if this value cannot be a string, number, or boolean. |
118 bool isDefinitelyNotNumStringBool(AbstractConstantValue value) { | 108 bool isDefinitelyNotNumStringBool(AbstractConstantValue value) { |
119 return value.isNothing || | 109 return value.isNothing || |
120 typeSystem.isDefinitelyNotNumStringBool(value.type); | 110 typeSystem.isDefinitelyNotNumStringBool(value.type); |
121 } | 111 } |
122 | 112 |
123 /// True if this value cannot be a non-integer double. | 113 /// True if this value cannot be a non-integer double. |
124 /// | 114 /// |
125 /// In other words, if true is returned, and the value is a number, then | 115 /// In other words, if true is returned, and the value is a number, then |
126 /// it is a whole number and is not NaN, Infinity, or minus Infinity. | 116 /// it is a whole number and is not NaN, Infinity, or minus Infinity. |
127 bool isDefinitelyNotNonIntegerDouble(AbstractConstantValue value) { | 117 bool isDefinitelyNotNonIntegerDouble(AbstractConstantValue value) { |
128 return value.isNothing || | 118 return value.isNothing || |
129 value.isConstant && !value.constant.isDouble || | 119 value.isConstant && !value.constant.isDouble || |
130 typeSystem.isDefinitelyNotNonIntegerDouble(value.type); | 120 typeSystem.isDefinitelyNotNonIntegerDouble(value.type); |
131 } | 121 } |
132 | 122 |
133 bool isDefinitelyInt(AbstractConstantValue value, | 123 bool isDefinitelyInt(AbstractConstantValue value, {bool allowNull: false}) { |
134 {bool allowNull: false}) { | |
135 return value.isNothing || | 124 return value.isNothing || |
136 typeSystem.isDefinitelyInt(value.type, allowNull: allowNull); | 125 typeSystem.isDefinitelyInt(value.type, allowNull: allowNull); |
137 } | 126 } |
138 | 127 |
139 bool isDefinitelyUint31(AbstractConstantValue value, | 128 bool isDefinitelyUint31(AbstractConstantValue value, |
140 {bool allowNull: false}) { | 129 {bool allowNull: false}) { |
141 return value.isNothing || | 130 return value.isNothing || |
142 typeSystem.isDefinitelyUint31(value.type, allowNull: allowNull); | 131 typeSystem.isDefinitelyUint31(value.type, allowNull: allowNull); |
143 } | 132 } |
144 | 133 |
145 bool isDefinitelyUint32(AbstractConstantValue value, | 134 bool isDefinitelyUint32(AbstractConstantValue value, |
146 {bool allowNull: false}) { | 135 {bool allowNull: false}) { |
147 return value.isNothing || | 136 return value.isNothing || |
148 typeSystem.isDefinitelyUint32(value.type, allowNull: allowNull); | 137 typeSystem.isDefinitelyUint32(value.type, allowNull: allowNull); |
149 } | 138 } |
150 | 139 |
151 bool isDefinitelyUint(AbstractConstantValue value, | 140 bool isDefinitelyUint(AbstractConstantValue value, {bool allowNull: false}) { |
152 {bool allowNull: false}) { | |
153 return value.isNothing || | 141 return value.isNothing || |
154 typeSystem.isDefinitelyUint(value.type, allowNull: allowNull); | 142 typeSystem.isDefinitelyUint(value.type, allowNull: allowNull); |
155 } | 143 } |
156 | 144 |
157 bool isDefinitelyArray(AbstractConstantValue value, | 145 bool isDefinitelyArray(AbstractConstantValue value, {bool allowNull: false}) { |
158 {bool allowNull: false}) { | |
159 return value.isNothing || | 146 return value.isNothing || |
160 typeSystem.isDefinitelyArray(value.type, allowNull: allowNull); | 147 typeSystem.isDefinitelyArray(value.type, allowNull: allowNull); |
161 } | 148 } |
162 | 149 |
163 bool isDefinitelyMutableArray(AbstractConstantValue value, | 150 bool isDefinitelyMutableArray(AbstractConstantValue value, |
164 {bool allowNull: false}) { | 151 {bool allowNull: false}) { |
165 return value.isNothing || | 152 return value.isNothing || |
166 typeSystem.isDefinitelyMutableArray(value.type, | 153 typeSystem.isDefinitelyMutableArray(value.type, allowNull: allowNull); |
167 allowNull: allowNull); | |
168 } | 154 } |
169 | 155 |
170 bool isDefinitelyFixedArray(AbstractConstantValue value, | 156 bool isDefinitelyFixedArray(AbstractConstantValue value, |
171 {bool allowNull: false}) { | 157 {bool allowNull: false}) { |
172 return value.isNothing || | 158 return value.isNothing || |
173 typeSystem.isDefinitelyFixedArray(value.type, | 159 typeSystem.isDefinitelyFixedArray(value.type, allowNull: allowNull); |
174 allowNull: allowNull); | |
175 } | 160 } |
176 | 161 |
177 bool isDefinitelyExtendableArray(AbstractConstantValue value, | 162 bool isDefinitelyExtendableArray(AbstractConstantValue value, |
178 {bool allowNull: false}) { | 163 {bool allowNull: false}) { |
179 return value.isNothing || | 164 return value.isNothing || |
180 typeSystem.isDefinitelyExtendableArray(value.type, | 165 typeSystem.isDefinitelyExtendableArray(value.type, |
181 allowNull: allowNull); | 166 allowNull: allowNull); |
182 } | 167 } |
183 | 168 |
184 bool isDefinitelyIndexable(AbstractConstantValue value, | 169 bool isDefinitelyIndexable(AbstractConstantValue value, |
185 {bool allowNull: false}) { | 170 {bool allowNull: false}) { |
186 return value.isNothing || | 171 return value.isNothing || |
187 typeSystem.isDefinitelyIndexable(value.type, allowNull: allowNull); | 172 typeSystem.isDefinitelyIndexable(value.type, allowNull: allowNull); |
188 } | 173 } |
189 | 174 |
190 /// Returns `true` if [value] represents an int value that must be in the | 175 /// Returns `true` if [value] represents an int value that must be in the |
191 /// inclusive range. | 176 /// inclusive range. |
192 bool isDefinitelyIntInRange(AbstractConstantValue value, {int min, int max}) { | 177 bool isDefinitelyIntInRange(AbstractConstantValue value, {int min, int max}) { |
193 if (value.isNothing) return true; | 178 if (value.isNothing) return true; |
194 if (!isDefinitelyInt(value)) return false; | 179 if (!isDefinitelyInt(value)) return false; |
195 PrimitiveConstantValue constant = value.constant; | 180 PrimitiveConstantValue constant = value.constant; |
196 if (constant == null) return false; | 181 if (constant == null) return false; |
197 if (!constant.isInt) return false; | 182 if (!constant.isInt) return false; |
198 if (min != null && constant.primitiveValue < min) return false; | 183 if (min != null && constant.primitiveValue < min) return false; |
199 if (max != null && constant.primitiveValue > max) return false; | 184 if (max != null && constant.primitiveValue > max) return false; |
200 return true; | 185 return true; |
201 } | 186 } |
202 | 187 |
203 /// Returns whether the given [value] is an instance of [type]. | 188 /// Returns whether the given [value] is an instance of [type]. |
204 /// | 189 /// |
205 /// Since [value] and [type] are not always known, [AbstractBool.Maybe] is | 190 /// Since [value] and [type] are not always known, [AbstractBool.Maybe] is |
206 /// returned if the answer is not known. | 191 /// returned if the answer is not known. |
207 /// | 192 /// |
208 /// [AbstractBool.Nothing] is returned if [value] is nothing. | 193 /// [AbstractBool.Nothing] is returned if [value] is nothing. |
209 /// | 194 /// |
210 /// If [allowNull] is true, `null` is considered an instance of anything, | 195 /// If [allowNull] is true, `null` is considered an instance of anything, |
211 /// otherwise it is only considered an instance of [Object], [dynamic], and | 196 /// otherwise it is only considered an instance of [Object], [dynamic], and |
212 /// [Null]. | 197 /// [Null]. |
213 AbstractBool isSubtypeOf(AbstractConstantValue value, | 198 AbstractBool isSubtypeOf(AbstractConstantValue value, types.DartType type, |
214 types.DartType type, | 199 {bool allowNull}) { |
215 {bool allowNull}) { | |
216 assert(allowNull != null); | 200 assert(allowNull != null); |
217 if (value.isNothing) { | 201 if (value.isNothing) { |
218 return AbstractBool.Nothing; | 202 return AbstractBool.Nothing; |
219 } | 203 } |
220 if (value.isConstant) { | 204 if (value.isConstant) { |
221 if (value.constant.isNull) { | 205 if (value.constant.isNull) { |
222 if (allowNull || | 206 if (allowNull || |
223 type.isObject || | 207 type.isObject || |
224 type.isDynamic || | 208 type.isDynamic || |
225 type == dartTypes.coreTypes.nullType) { | 209 type == dartTypes.coreTypes.nullType) { |
226 return AbstractBool.True; | 210 return AbstractBool.True; |
227 } | 211 } |
228 if (type is types.TypeVariableType) { | 212 if (type is types.TypeVariableType) { |
229 return AbstractBool.Maybe; | 213 return AbstractBool.Maybe; |
230 } | 214 } |
231 return AbstractBool.False; | 215 return AbstractBool.False; |
232 } | 216 } |
233 if (type == dartTypes.coreTypes.intType) { | 217 if (type == dartTypes.coreTypes.intType) { |
234 return constantSystem.isInt(value.constant) | 218 return constantSystem.isInt(value.constant) |
235 ? AbstractBool.True | 219 ? AbstractBool.True |
236 : AbstractBool.False; | 220 : AbstractBool.False; |
237 } | 221 } |
238 types.DartType valueType = value.constant.getType(dartTypes.coreTypes); | 222 types.DartType valueType = value.constant.getType(dartTypes.coreTypes); |
239 if (constantSystem.isSubtype(dartTypes, valueType, type)) { | 223 if (constantSystem.isSubtype(dartTypes, valueType, type)) { |
240 return AbstractBool.True; | 224 return AbstractBool.True; |
241 } | 225 } |
242 if (!dartTypes.isPotentialSubtype(valueType, type)) { | 226 if (!dartTypes.isPotentialSubtype(valueType, type)) { |
243 return AbstractBool.False; | 227 return AbstractBool.False; |
244 } | 228 } |
245 return AbstractBool.Maybe; | 229 return AbstractBool.Maybe; |
246 } | 230 } |
247 return typeSystem.isSubtypeOf(value.type, type, allowNull: allowNull); | 231 return typeSystem.isSubtypeOf(value.type, type, allowNull: allowNull); |
248 } | 232 } |
249 | 233 |
250 /// Returns the possible results of applying [operator] to [value], | 234 /// Returns the possible results of applying [operator] to [value], |
251 /// assuming the operation does not throw. | 235 /// assuming the operation does not throw. |
252 /// | 236 /// |
253 /// Because we do not explicitly track thrown values, we currently use the | 237 /// Because we do not explicitly track thrown values, we currently use the |
254 /// convention that constant values are returned from this method only | 238 /// convention that constant values are returned from this method only |
255 /// if the operation is known not to throw. | 239 /// if the operation is known not to throw. |
256 /// | 240 /// |
257 /// This method returns `null` if a good result could not be found. In that | 241 /// This method returns `null` if a good result could not be found. In that |
258 /// case, it is best to fall back on interprocedural type information. | 242 /// case, it is best to fall back on interprocedural type information. |
259 AbstractConstantValue unaryOp(UnaryOperator operator, | 243 AbstractConstantValue unaryOp( |
260 AbstractConstantValue value) { | 244 UnaryOperator operator, AbstractConstantValue value) { |
261 switch (operator.kind) { | 245 switch (operator.kind) { |
262 case UnaryOperatorKind.COMPLEMENT: | 246 case UnaryOperatorKind.COMPLEMENT: |
263 return bitNotSpecial(value); | 247 return bitNotSpecial(value); |
264 case UnaryOperatorKind.NEGATE: | 248 case UnaryOperatorKind.NEGATE: |
265 return negateSpecial(value); | 249 return negateSpecial(value); |
266 default: | 250 default: |
267 break; | 251 break; |
268 } | 252 } |
269 // TODO(asgerf): Also return information about whether this can throw? | 253 // TODO(asgerf): Also return information about whether this can throw? |
270 if (value.isNothing) { | 254 if (value.isNothing) { |
(...skipping 11 matching lines...) Expand all Loading... |
282 /// Returns the possible results of applying [operator] to [left], [right], | 266 /// Returns the possible results of applying [operator] to [left], [right], |
283 /// assuming the operation does not throw. | 267 /// assuming the operation does not throw. |
284 /// | 268 /// |
285 /// Because we do not explicitly track thrown values, we currently use the | 269 /// Because we do not explicitly track thrown values, we currently use the |
286 /// convention that constant values are returned from this method only | 270 /// convention that constant values are returned from this method only |
287 /// if the operation is known not to throw. | 271 /// if the operation is known not to throw. |
288 /// | 272 /// |
289 /// This method returns `null` if a good result could not be found. In that | 273 /// This method returns `null` if a good result could not be found. In that |
290 /// case, it is best to fall back on interprocedural type information. | 274 /// case, it is best to fall back on interprocedural type information. |
291 AbstractConstantValue binaryOp(BinaryOperator operator, | 275 AbstractConstantValue binaryOp(BinaryOperator operator, |
292 AbstractConstantValue left, | 276 AbstractConstantValue left, AbstractConstantValue right) { |
293 AbstractConstantValue right) { | |
294 switch (operator.kind) { | 277 switch (operator.kind) { |
295 case BinaryOperatorKind.ADD: | 278 case BinaryOperatorKind.ADD: |
296 return addSpecial(left, right); | 279 return addSpecial(left, right); |
297 | 280 |
298 case BinaryOperatorKind.SUB: | 281 case BinaryOperatorKind.SUB: |
299 return subtractSpecial(left, right); | 282 return subtractSpecial(left, right); |
300 | 283 |
301 case BinaryOperatorKind.MUL: | 284 case BinaryOperatorKind.MUL: |
302 return multiplySpecial(left, right); | 285 return multiplySpecial(left, right); |
303 | 286 |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
348 return nothing; | 331 return nothing; |
349 } | 332 } |
350 if (left.isConstant && right.isConstant) { | 333 if (left.isConstant && right.isConstant) { |
351 BinaryOperation operation = constantSystem.lookupBinary(operator); | 334 BinaryOperation operation = constantSystem.lookupBinary(operator); |
352 ConstantValue result = operation.fold(left.constant, right.constant); | 335 ConstantValue result = operation.fold(left.constant, right.constant); |
353 if (result != null) return constant(result); | 336 if (result != null) return constant(result); |
354 } | 337 } |
355 return null; // The caller will use return type from type inference. | 338 return null; // The caller will use return type from type inference. |
356 } | 339 } |
357 | 340 |
358 AbstractConstantValue foldUnary(UnaryOperation operation, | 341 AbstractConstantValue foldUnary( |
359 AbstractConstantValue value) { | 342 UnaryOperation operation, AbstractConstantValue value) { |
360 if (value.isNothing) return nothing; | 343 if (value.isNothing) return nothing; |
361 if (value.isConstant) { | 344 if (value.isConstant) { |
362 ConstantValue result = operation.fold(value.constant); | 345 ConstantValue result = operation.fold(value.constant); |
363 if (result != null) return constant(result); | 346 if (result != null) return constant(result); |
364 } | 347 } |
365 return null; | 348 return null; |
366 } | 349 } |
367 | 350 |
368 AbstractConstantValue bitNotSpecial(AbstractConstantValue value) { | 351 AbstractConstantValue bitNotSpecial(AbstractConstantValue value) { |
369 return foldUnary(constantSystem.bitNot, value); | 352 return foldUnary(constantSystem.bitNot, value); |
370 } | 353 } |
371 | 354 |
372 AbstractConstantValue negateSpecial(AbstractConstantValue value) { | 355 AbstractConstantValue negateSpecial(AbstractConstantValue value) { |
373 AbstractConstantValue folded = foldUnary(constantSystem.negate, value); | 356 AbstractConstantValue folded = foldUnary(constantSystem.negate, value); |
374 if (folded != null) return folded; | 357 if (folded != null) return folded; |
375 if (isDefinitelyInt(value, allowNull: true)) { | 358 if (isDefinitelyInt(value, allowNull: true)) { |
376 return nonConstant(typeSystem.intType); | 359 return nonConstant(typeSystem.intType); |
377 } | 360 } |
378 return null; | 361 return null; |
379 } | 362 } |
380 | 363 |
381 | |
382 AbstractConstantValue foldBinary(BinaryOperation operation, | 364 AbstractConstantValue foldBinary(BinaryOperation operation, |
383 AbstractConstantValue left, AbstractConstantValue right) { | 365 AbstractConstantValue left, AbstractConstantValue right) { |
384 if (left.isNothing || right.isNothing) return nothing; | 366 if (left.isNothing || right.isNothing) return nothing; |
385 if (left.isConstant && right.isConstant) { | 367 if (left.isConstant && right.isConstant) { |
386 ConstantValue result = operation.fold(left.constant, right.constant); | 368 ConstantValue result = operation.fold(left.constant, right.constant); |
387 if (result != null) return constant(result); | 369 if (result != null) return constant(result); |
388 } | 370 } |
389 return null; | 371 return null; |
390 } | 372 } |
391 | 373 |
392 AbstractConstantValue closedOnInt(AbstractConstantValue left, | 374 AbstractConstantValue closedOnInt( |
393 AbstractConstantValue right) { | 375 AbstractConstantValue left, AbstractConstantValue right) { |
394 if (isDefinitelyInt(left, allowNull: true) && | 376 if (isDefinitelyInt(left, allowNull: true) && |
395 isDefinitelyInt(right, allowNull: true)) { | 377 isDefinitelyInt(right, allowNull: true)) { |
396 return nonConstant(typeSystem.intType); | 378 return nonConstant(typeSystem.intType); |
397 } | 379 } |
398 return null; | 380 return null; |
399 } | 381 } |
400 | 382 |
401 AbstractConstantValue closedOnUint(AbstractConstantValue left, | 383 AbstractConstantValue closedOnUint( |
402 AbstractConstantValue right) { | 384 AbstractConstantValue left, AbstractConstantValue right) { |
403 if (isDefinitelyUint(left, allowNull: true) && | 385 if (isDefinitelyUint(left, allowNull: true) && |
404 isDefinitelyUint(right, allowNull: true)) { | 386 isDefinitelyUint(right, allowNull: true)) { |
405 return nonConstant(typeSystem.uintType); | 387 return nonConstant(typeSystem.uintType); |
406 } | 388 } |
407 return null; | 389 return null; |
408 } | 390 } |
409 | 391 |
410 AbstractConstantValue closedOnUint31(AbstractConstantValue left, | 392 AbstractConstantValue closedOnUint31( |
411 AbstractConstantValue right) { | 393 AbstractConstantValue left, AbstractConstantValue right) { |
412 if (isDefinitelyUint31(left, allowNull: true) && | 394 if (isDefinitelyUint31(left, allowNull: true) && |
413 isDefinitelyUint31(right, allowNull: true)) { | 395 isDefinitelyUint31(right, allowNull: true)) { |
414 return nonConstant(typeSystem.uint31Type); | 396 return nonConstant(typeSystem.uint31Type); |
415 } | 397 } |
416 return null; | 398 return null; |
417 } | 399 } |
418 | 400 |
419 AbstractConstantValue addSpecial(AbstractConstantValue left, | 401 AbstractConstantValue addSpecial( |
420 AbstractConstantValue right) { | 402 AbstractConstantValue left, AbstractConstantValue right) { |
421 AbstractConstantValue folded = foldBinary(constantSystem.add, left, right); | 403 AbstractConstantValue folded = foldBinary(constantSystem.add, left, right); |
422 if (folded != null) return folded; | 404 if (folded != null) return folded; |
423 if (isDefinitelyNum(left, allowNull: true)) { | 405 if (isDefinitelyNum(left, allowNull: true)) { |
424 if (isDefinitelyUint31(left, allowNull: true) && | 406 if (isDefinitelyUint31(left, allowNull: true) && |
425 isDefinitelyUint31(right, allowNull: true)) { | 407 isDefinitelyUint31(right, allowNull: true)) { |
426 return nonConstant(typeSystem.uint32Type); | 408 return nonConstant(typeSystem.uint32Type); |
427 } | 409 } |
428 return closedOnUint(left, right) ?? closedOnInt(left, right); | 410 return closedOnUint(left, right) ?? closedOnInt(left, right); |
429 } | 411 } |
430 return null; | 412 return null; |
431 } | 413 } |
432 | 414 |
433 AbstractConstantValue subtractSpecial(AbstractConstantValue left, | 415 AbstractConstantValue subtractSpecial( |
434 AbstractConstantValue right) { | 416 AbstractConstantValue left, AbstractConstantValue right) { |
435 AbstractConstantValue folded = | 417 AbstractConstantValue folded = |
436 foldBinary(constantSystem.subtract, left, right); | 418 foldBinary(constantSystem.subtract, left, right); |
437 return folded ?? closedOnInt(left, right); | 419 return folded ?? closedOnInt(left, right); |
438 } | 420 } |
439 | 421 |
440 AbstractConstantValue multiplySpecial(AbstractConstantValue left, | 422 AbstractConstantValue multiplySpecial( |
441 AbstractConstantValue right) { | 423 AbstractConstantValue left, AbstractConstantValue right) { |
442 AbstractConstantValue folded = | 424 AbstractConstantValue folded = |
443 foldBinary(constantSystem.multiply, left, right); | 425 foldBinary(constantSystem.multiply, left, right); |
444 return folded ?? closedOnUint(left, right) ?? closedOnInt(left, right); | 426 return folded ?? closedOnUint(left, right) ?? closedOnInt(left, right); |
445 } | 427 } |
446 | 428 |
447 AbstractConstantValue divideSpecial(AbstractConstantValue left, | 429 AbstractConstantValue divideSpecial( |
448 AbstractConstantValue right) { | 430 AbstractConstantValue left, AbstractConstantValue right) { |
449 return foldBinary(constantSystem.divide, left, right); | 431 return foldBinary(constantSystem.divide, left, right); |
450 } | 432 } |
451 | 433 |
452 AbstractConstantValue truncatingDivideSpecial( | 434 AbstractConstantValue truncatingDivideSpecial( |
453 AbstractConstantValue left, AbstractConstantValue right) { | 435 AbstractConstantValue left, AbstractConstantValue right) { |
454 AbstractConstantValue folded = | 436 AbstractConstantValue folded = |
455 foldBinary(constantSystem.truncatingDivide, left, right); | 437 foldBinary(constantSystem.truncatingDivide, left, right); |
456 if (folded != null) return folded; | 438 if (folded != null) return folded; |
457 if (isDefinitelyNum(left, allowNull: true)) { | 439 if (isDefinitelyNum(left, allowNull: true)) { |
458 if (isDefinitelyUint32(left, allowNull: true) && | 440 if (isDefinitelyUint32(left, allowNull: true) && |
(...skipping 10 matching lines...) Expand all Loading... |
469 } | 451 } |
470 if (isDefinitelyUint(left, allowNull: true)) { | 452 if (isDefinitelyUint(left, allowNull: true)) { |
471 return nonConstant(typeSystem.uintType); | 453 return nonConstant(typeSystem.uintType); |
472 } | 454 } |
473 } | 455 } |
474 return nonConstant(typeSystem.intType); | 456 return nonConstant(typeSystem.intType); |
475 } | 457 } |
476 return null; | 458 return null; |
477 } | 459 } |
478 | 460 |
479 AbstractConstantValue moduloSpecial(AbstractConstantValue left, | 461 AbstractConstantValue moduloSpecial( |
480 AbstractConstantValue right) { | 462 AbstractConstantValue left, AbstractConstantValue right) { |
481 AbstractConstantValue folded = | 463 AbstractConstantValue folded = |
482 foldBinary(constantSystem.modulo, left, right); | 464 foldBinary(constantSystem.modulo, left, right); |
483 return folded ?? closedOnUint(left, right) ?? closedOnInt(left, right); | 465 return folded ?? closedOnUint(left, right) ?? closedOnInt(left, right); |
484 } | 466 } |
485 | 467 |
486 AbstractConstantValue remainderSpecial(AbstractConstantValue left, | 468 AbstractConstantValue remainderSpecial( |
487 AbstractConstantValue right) { | 469 AbstractConstantValue left, AbstractConstantValue right) { |
488 if (left.isNothing || right.isNothing) return nothing; | 470 if (left.isNothing || right.isNothing) return nothing; |
489 AbstractConstantValue folded = null; // Remainder not in constant system. | 471 AbstractConstantValue folded = null; // Remainder not in constant system. |
490 return folded ?? closedOnUint(left, right) ?? closedOnInt(left, right); | 472 return folded ?? closedOnUint(left, right) ?? closedOnInt(left, right); |
491 } | 473 } |
492 | 474 |
493 AbstractConstantValue codeUnitAtSpecial(AbstractConstantValue left, | 475 AbstractConstantValue codeUnitAtSpecial( |
494 AbstractConstantValue right) { | 476 AbstractConstantValue left, AbstractConstantValue right) { |
495 return foldBinary(constantSystem.codeUnitAt, left, right); | 477 return foldBinary(constantSystem.codeUnitAt, left, right); |
496 } | 478 } |
497 | 479 |
498 AbstractConstantValue equalSpecial(AbstractConstantValue left, | 480 AbstractConstantValue equalSpecial( |
499 AbstractConstantValue right) { | 481 AbstractConstantValue left, AbstractConstantValue right) { |
500 AbstractConstantValue folded = | 482 AbstractConstantValue folded = |
501 foldBinary(constantSystem.equal, left, right); | 483 foldBinary(constantSystem.equal, left, right); |
502 if (folded != null) return folded; | 484 if (folded != null) return folded; |
503 bool behavesLikeIdentity = | 485 bool behavesLikeIdentity = |
504 isDefinitelyNumStringBool(left, allowNull: true) || | 486 isDefinitelyNumStringBool(left, allowNull: true) || |
505 right.isNullConstant; | 487 right.isNullConstant; |
506 if (behavesLikeIdentity && | 488 if (behavesLikeIdentity && typeSystem.areDisjoint(left.type, right.type)) { |
507 typeSystem.areDisjoint(left.type, right.type)) { | |
508 return constant(new FalseConstantValue()); | 489 return constant(new FalseConstantValue()); |
509 } | 490 } |
510 return null; | 491 return null; |
511 } | 492 } |
512 | 493 |
513 AbstractConstantValue andSpecial(AbstractConstantValue left, | 494 AbstractConstantValue andSpecial( |
514 AbstractConstantValue right) { | 495 AbstractConstantValue left, AbstractConstantValue right) { |
515 AbstractConstantValue folded = | 496 AbstractConstantValue folded = |
516 foldBinary(constantSystem.bitAnd, left, right); | 497 foldBinary(constantSystem.bitAnd, left, right); |
517 if (folded != null) return folded; | 498 if (folded != null) return folded; |
518 if (isDefinitelyNum(left, allowNull: true)) { | 499 if (isDefinitelyNum(left, allowNull: true)) { |
519 if (isDefinitelyUint31(left, allowNull: true) || | 500 if (isDefinitelyUint31(left, allowNull: true) || |
520 isDefinitelyUint31(right, allowNull: true)) { | 501 isDefinitelyUint31(right, allowNull: true)) { |
521 // Either 31-bit argument will truncate the other. | 502 // Either 31-bit argument will truncate the other. |
522 return nonConstant(typeSystem.uint31Type); | 503 return nonConstant(typeSystem.uint31Type); |
523 } | 504 } |
524 } | 505 } |
525 return null; | 506 return null; |
526 } | 507 } |
527 | 508 |
528 AbstractConstantValue orSpecial(AbstractConstantValue left, | 509 AbstractConstantValue orSpecial( |
529 AbstractConstantValue right) { | 510 AbstractConstantValue left, AbstractConstantValue right) { |
530 AbstractConstantValue folded = | 511 AbstractConstantValue folded = |
531 foldBinary(constantSystem.bitOr, left, right); | 512 foldBinary(constantSystem.bitOr, left, right); |
532 return folded ?? closedOnUint31(left, right); | 513 return folded ?? closedOnUint31(left, right); |
533 } | 514 } |
534 | 515 |
535 AbstractConstantValue xorSpecial(AbstractConstantValue left, | 516 AbstractConstantValue xorSpecial( |
536 AbstractConstantValue right) { | 517 AbstractConstantValue left, AbstractConstantValue right) { |
537 AbstractConstantValue folded = | 518 AbstractConstantValue folded = |
538 foldBinary(constantSystem.bitXor, left, right); | 519 foldBinary(constantSystem.bitXor, left, right); |
539 return folded ?? closedOnUint31(left, right); | 520 return folded ?? closedOnUint31(left, right); |
540 } | 521 } |
541 | 522 |
542 AbstractConstantValue shiftLeftSpecial(AbstractConstantValue left, | 523 AbstractConstantValue shiftLeftSpecial( |
543 AbstractConstantValue right) { | 524 AbstractConstantValue left, AbstractConstantValue right) { |
544 return foldBinary(constantSystem.shiftLeft, left, right); | 525 return foldBinary(constantSystem.shiftLeft, left, right); |
545 } | 526 } |
546 | 527 |
547 AbstractConstantValue shiftRightSpecial(AbstractConstantValue left, | 528 AbstractConstantValue shiftRightSpecial( |
548 AbstractConstantValue right) { | 529 AbstractConstantValue left, AbstractConstantValue right) { |
549 AbstractConstantValue folded = | 530 AbstractConstantValue folded = |
550 foldBinary(constantSystem.shiftRight, left, right); | 531 foldBinary(constantSystem.shiftRight, left, right); |
551 if (folded != null) return folded; | 532 if (folded != null) return folded; |
552 if (isDefinitelyUint31(left, allowNull: true)) { | 533 if (isDefinitelyUint31(left, allowNull: true)) { |
553 return nonConstant(typeSystem.uint31Type); | 534 return nonConstant(typeSystem.uint31Type); |
554 } else if (isDefinitelyUint32(left, allowNull: true)) { | 535 } else if (isDefinitelyUint32(left, allowNull: true)) { |
555 if (isDefinitelyIntInRange(right, min: 1, max: 31)) { | 536 if (isDefinitelyIntInRange(right, min: 1, max: 31)) { |
556 // A zero will be shifted into the 'sign' bit. | 537 // A zero will be shifted into the 'sign' bit. |
557 return nonConstant(typeSystem.uint31Type); | 538 return nonConstant(typeSystem.uint31Type); |
558 } | 539 } |
559 return nonConstant(typeSystem.uint32Type); | 540 return nonConstant(typeSystem.uint32Type); |
560 } | 541 } |
561 return null; | 542 return null; |
562 } | 543 } |
563 | 544 |
564 AbstractConstantValue lessSpecial(AbstractConstantValue left, | 545 AbstractConstantValue lessSpecial( |
565 AbstractConstantValue right) { | 546 AbstractConstantValue left, AbstractConstantValue right) { |
566 if (isDefinitelyUint(left) && right.isZeroOrNegativeConstant) { | 547 if (isDefinitelyUint(left) && right.isZeroOrNegativeConstant) { |
567 return falseValue; // "uint < 0" is false. | 548 return falseValue; // "uint < 0" is false. |
568 } else if (left.isNegativeConstant && isDefinitelyUint(right)) { | 549 } else if (left.isNegativeConstant && isDefinitelyUint(right)) { |
569 return trueValue; // "-1 < uint" is true. | 550 return trueValue; // "-1 < uint" is true. |
570 } | 551 } |
571 return foldBinary(constantSystem.less, left, right); | 552 return foldBinary(constantSystem.less, left, right); |
572 } | 553 } |
573 | 554 |
574 AbstractConstantValue lessEqualSpecial(AbstractConstantValue left, | 555 AbstractConstantValue lessEqualSpecial( |
575 AbstractConstantValue right) { | 556 AbstractConstantValue left, AbstractConstantValue right) { |
576 if (isDefinitelyUint(left) && right.isNegativeConstant) { | 557 if (isDefinitelyUint(left) && right.isNegativeConstant) { |
577 return falseValue; // "uint <= -1" is false. | 558 return falseValue; // "uint <= -1" is false. |
578 } else if (left.isZeroOrNegativeConstant && isDefinitelyUint(right)) { | 559 } else if (left.isZeroOrNegativeConstant && isDefinitelyUint(right)) { |
579 return trueValue; // "0 <= uint" is true. | 560 return trueValue; // "0 <= uint" is true. |
580 } | 561 } |
581 return foldBinary(constantSystem.lessEqual, left, right); | 562 return foldBinary(constantSystem.lessEqual, left, right); |
582 } | 563 } |
583 | 564 |
584 AbstractConstantValue greaterSpecial(AbstractConstantValue left, | 565 AbstractConstantValue greaterSpecial( |
585 AbstractConstantValue right) { | 566 AbstractConstantValue left, AbstractConstantValue right) { |
586 if (left.isZeroOrNegativeConstant && isDefinitelyUint(right)) { | 567 if (left.isZeroOrNegativeConstant && isDefinitelyUint(right)) { |
587 return falseValue; // "0 > uint" is false | 568 return falseValue; // "0 > uint" is false |
588 } else if (isDefinitelyUint(left) && right.isNegativeConstant) { | 569 } else if (isDefinitelyUint(left) && right.isNegativeConstant) { |
589 return trueValue; // "uint > -1" is true | 570 return trueValue; // "uint > -1" is true |
590 } | 571 } |
591 return foldBinary(constantSystem.greater, left, right); | 572 return foldBinary(constantSystem.greater, left, right); |
592 } | 573 } |
593 | 574 |
594 AbstractConstantValue greaterEqualSpecial(AbstractConstantValue left, | 575 AbstractConstantValue greaterEqualSpecial( |
595 AbstractConstantValue right) { | 576 AbstractConstantValue left, AbstractConstantValue right) { |
596 if (left.isNegativeConstant && isDefinitelyUint(right)) { | 577 if (left.isNegativeConstant && isDefinitelyUint(right)) { |
597 return falseValue; // "-1 >= uint" is false | 578 return falseValue; // "-1 >= uint" is false |
598 } else if (isDefinitelyUint(left) && right.isZeroOrNegativeConstant) { | 579 } else if (isDefinitelyUint(left) && right.isZeroOrNegativeConstant) { |
599 return trueValue; // "uint >= 0" is true | 580 return trueValue; // "uint >= 0" is true |
600 } | 581 } |
601 return foldBinary(constantSystem.greaterEqual, left, right); | 582 return foldBinary(constantSystem.greaterEqual, left, right); |
602 } | 583 } |
603 | 584 |
604 AbstractConstantValue intConstant(int value) { | 585 AbstractConstantValue intConstant(int value) { |
605 return constant(new IntConstantValue(value)); | 586 return constant(new IntConstantValue(value)); |
606 } | 587 } |
607 | 588 |
608 AbstractConstantValue lengthSpecial(AbstractConstantValue input) { | 589 AbstractConstantValue lengthSpecial(AbstractConstantValue input) { |
609 if (input.isConstant) { | 590 if (input.isConstant) { |
610 ConstantValue constant = input.constant; | 591 ConstantValue constant = input.constant; |
611 if (constant is StringConstantValue) { | 592 if (constant is StringConstantValue) { |
612 return intConstant(constant.length); | 593 return intConstant(constant.length); |
613 } else if (constant is ListConstantValue) { | 594 } else if (constant is ListConstantValue) { |
614 return intConstant(constant.length); | 595 return intConstant(constant.length); |
615 } | 596 } |
616 } | 597 } |
617 int length = typeSystem.getContainerLength(input.type); | 598 int length = typeSystem.getContainerLength(input.type); |
618 if (length != null) { | 599 if (length != null) { |
619 return intConstant(length); | 600 return intConstant(length); |
620 } | 601 } |
621 return null; // The caller will use return type from type inference. | 602 return null; // The caller will use return type from type inference. |
622 } | 603 } |
623 | 604 |
624 AbstractConstantValue stringConstant(String value) { | 605 AbstractConstantValue stringConstant(String value) { |
625 return constant(new StringConstantValue(new ast.DartString.literal(value))); | 606 return constant(new StringConstantValue(new ast.DartString.literal(value))); |
626 } | 607 } |
627 | 608 |
628 AbstractConstantValue indexSpecial(AbstractConstantValue left, | 609 AbstractConstantValue indexSpecial( |
629 AbstractConstantValue right) { | 610 AbstractConstantValue left, AbstractConstantValue right) { |
630 if (left.isNothing || right.isNothing) return nothing; | 611 if (left.isNothing || right.isNothing) return nothing; |
631 if (right.isConstant) { | 612 if (right.isConstant) { |
632 ConstantValue index = right.constant; | 613 ConstantValue index = right.constant; |
633 if (left.isConstant) { | 614 if (left.isConstant) { |
634 ConstantValue receiver = left.constant; | 615 ConstantValue receiver = left.constant; |
635 if (receiver is StringConstantValue) { | 616 if (receiver is StringConstantValue) { |
636 if (index is IntConstantValue) { | 617 if (index is IntConstantValue) { |
637 String stringValue = receiver.primitiveValue.slowToString(); | 618 String stringValue = receiver.primitiveValue.slowToString(); |
638 int indexValue = index.primitiveValue; | 619 int indexValue = index.primitiveValue; |
639 if (0 <= indexValue && indexValue < stringValue.length) { | 620 if (0 <= indexValue && indexValue < stringValue.length) { |
640 return stringConstant(stringValue[indexValue]); | 621 return stringConstant(stringValue[indexValue]); |
641 } else { | 622 } else { |
642 return nothing; // Will throw. | 623 return nothing; // Will throw. |
643 } | 624 } |
644 } | 625 } |
645 } else if (receiver is ListConstantValue) { | 626 } else if (receiver is ListConstantValue) { |
646 if (index is IntConstantValue) { | 627 if (index is IntConstantValue) { |
647 int indexValue = index.primitiveValue; | 628 int indexValue = index.primitiveValue; |
648 if (0 <= indexValue && indexValue < receiver.length) { | 629 if (0 <= indexValue && indexValue < receiver.length) { |
649 return constant(receiver.entries[indexValue]); | 630 return constant(receiver.entries[indexValue]); |
650 } else { | 631 } else { |
651 return nothing; // Will throw. | 632 return nothing; // Will throw. |
652 } | 633 } |
653 } | 634 } |
654 } else if (receiver is MapConstantValue) { | 635 } else if (receiver is MapConstantValue) { |
655 ConstantValue result = receiver.lookup(index); | 636 ConstantValue result = receiver.lookup(index); |
656 if (result != null) { | 637 if (result != null) { |
657 return constant(result); | 638 return constant(result); |
658 } | 639 } |
659 return constant(new NullConstantValue()); | 640 return constant(new NullConstantValue()); |
660 } | 641 } |
661 } | 642 } |
662 TypeMask type = typeSystem.indexWithConstant(left.type, index); | 643 TypeMask type = typeSystem.indexWithConstant(left.type, index); |
663 if (type != null) return nonConstant(type); | 644 if (type != null) return nonConstant(type); |
664 } | 645 } |
665 // TODO(asgerf): Handle case where 'left' is a List or Map constant but | 646 // TODO(asgerf): Handle case where 'left' is a List or Map constant but |
666 // the index is unknown. | 647 // the index is unknown. |
667 return null; // The caller will use return type from type inference. | 648 return null; // The caller will use return type from type inference. |
668 } | 649 } |
669 | 650 |
670 AbstractConstantValue stringify(AbstractConstantValue value) { | 651 AbstractConstantValue stringify(AbstractConstantValue value) { |
671 if (value.isNothing) return nothing; | 652 if (value.isNothing) return nothing; |
672 if (value.isNonConst) return nonConstant(typeSystem.stringType); | 653 if (value.isNonConst) return nonConstant(typeSystem.stringType); |
673 ConstantValue constantValue = value.constant; | 654 ConstantValue constantValue = value.constant; |
674 if (constantValue is StringConstantValue) { | 655 if (constantValue is StringConstantValue) { |
675 return value; | 656 return value; |
676 } else if (constantValue is PrimitiveConstantValue) { | 657 } else if (constantValue is PrimitiveConstantValue) { |
677 // Note: The primitiveValue for a StringConstantValue is not suitable | 658 // Note: The primitiveValue for a StringConstantValue is not suitable |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
719 if (constantValue != null) return constant(constantValue, mask); | 700 if (constantValue != null) return constant(constantValue, mask); |
720 return nonConstant(mask); | 701 return nonConstant(mask); |
721 } | 702 } |
722 | 703 |
723 AbstractConstantValue nonNullable(AbstractConstantValue value) { | 704 AbstractConstantValue nonNullable(AbstractConstantValue value) { |
724 if (value.isNullConstant) return nothing; | 705 if (value.isNullConstant) return nothing; |
725 if (!value.isNullable) return value; | 706 if (!value.isNullable) return value; |
726 return nonConstant(value.type.nonNullable()); | 707 return nonConstant(value.type.nonNullable()); |
727 } | 708 } |
728 | 709 |
729 AbstractConstantValue intersectWithType(AbstractConstantValue value, | 710 AbstractConstantValue intersectWithType( |
730 TypeMask type) { | 711 AbstractConstantValue value, TypeMask type) { |
731 if (value.isNothing || typeSystem.areDisjoint(value.type, type)) { | 712 if (value.isNothing || typeSystem.areDisjoint(value.type, type)) { |
732 return nothing; | 713 return nothing; |
733 } else if (value.isConstant) { | 714 } else if (value.isConstant) { |
734 return value; | 715 return value; |
735 } else { | 716 } else { |
736 return nonConstant(typeSystem.intersection(value.type, type)); | 717 return nonConstant(typeSystem.intersection(value.type, type)); |
737 } | 718 } |
738 } | 719 } |
739 | 720 |
740 /// If [value] is an integer constant, returns its value, otherwise `null`. | 721 /// If [value] is an integer constant, returns its value, otherwise `null`. |
(...skipping 13 matching lines...) Expand all Loading... |
754 * | 735 * |
755 * Should be followed by the [ShrinkingReducer] pass. | 736 * Should be followed by the [ShrinkingReducer] pass. |
756 * | 737 * |
757 * Implemented according to 'Constant Propagation with Conditional Branches' | 738 * Implemented according to 'Constant Propagation with Conditional Branches' |
758 * by Wegman, Zadeck. | 739 * by Wegman, Zadeck. |
759 */ | 740 */ |
760 class TypePropagator extends Pass { | 741 class TypePropagator extends Pass { |
761 String get passName => 'Type propagation'; | 742 String get passName => 'Type propagation'; |
762 | 743 |
763 final CpsFunctionCompiler _functionCompiler; | 744 final CpsFunctionCompiler _functionCompiler; |
764 final Map<Variable, ConstantValue> _values= <Variable, ConstantValue>{}; | 745 final Map<Variable, ConstantValue> _values = <Variable, ConstantValue>{}; |
765 final ConstantPropagationLattice _lattice; | 746 final ConstantPropagationLattice _lattice; |
766 final bool recomputeAll; | 747 final bool recomputeAll; |
767 | 748 |
768 TypePropagator(CpsFunctionCompiler functionCompiler, | 749 TypePropagator(CpsFunctionCompiler functionCompiler, |
769 {this.recomputeAll: false}) | 750 {this.recomputeAll: false}) |
770 : _functionCompiler = functionCompiler, | 751 : _functionCompiler = functionCompiler, |
771 _lattice = new ConstantPropagationLattice(functionCompiler); | 752 _lattice = new ConstantPropagationLattice(functionCompiler); |
772 | 753 |
773 dart2js.Compiler get _compiler => _functionCompiler.compiler; | 754 dart2js.Compiler get _compiler => _functionCompiler.compiler; |
774 InternalErrorFunction get _internalError => _compiler.reporter.internalError; | 755 InternalErrorFunction get _internalError => _compiler.reporter.internalError; |
775 | 756 |
776 @override | 757 @override |
777 void rewrite(FunctionDefinition root) { | 758 void rewrite(FunctionDefinition root) { |
778 // Analyze. In this phase, the entire term is analyzed for reachability | 759 // Analyze. In this phase, the entire term is analyzed for reachability |
779 // and the abstract value of each expression. | 760 // and the abstract value of each expression. |
780 TypePropagationVisitor analyzer = new TypePropagationVisitor( | 761 TypePropagationVisitor analyzer = |
781 _lattice, | 762 new TypePropagationVisitor(_lattice, _values, _internalError); |
782 _values, | |
783 _internalError); | |
784 | 763 |
785 analyzer.analyze(root, recomputeAll); | 764 analyzer.analyze(root, recomputeAll); |
786 | 765 |
787 // Transform. Uses the data acquired in the previous analysis phase to | 766 // Transform. Uses the data acquired in the previous analysis phase to |
788 // replace branches with fixed targets and side-effect-free expressions | 767 // replace branches with fixed targets and side-effect-free expressions |
789 // with constant results or existing values that are in scope. | 768 // with constant results or existing values that are in scope. |
790 TransformingVisitor transformer = new TransformingVisitor( | 769 TransformingVisitor transformer = new TransformingVisitor( |
791 _compiler, | 770 _compiler, _functionCompiler, _lattice, analyzer, _internalError); |
792 _functionCompiler, | |
793 _lattice, | |
794 analyzer, | |
795 _internalError); | |
796 transformer.transform(root); | 771 transformer.transform(root); |
797 } | 772 } |
798 } | 773 } |
799 | 774 |
800 final Map<String, BuiltinOperator> NumBinaryBuiltins = | 775 final Map<String, BuiltinOperator> NumBinaryBuiltins = |
801 const <String, BuiltinOperator>{ | 776 const <String, BuiltinOperator>{ |
802 '+': BuiltinOperator.NumAdd, | 777 '+': BuiltinOperator.NumAdd, |
803 '-': BuiltinOperator.NumSubtract, | 778 '-': BuiltinOperator.NumSubtract, |
804 '*': BuiltinOperator.NumMultiply, | 779 '*': BuiltinOperator.NumMultiply, |
805 '/': BuiltinOperator.NumDivide, | 780 '/': BuiltinOperator.NumDivide, |
806 '&': BuiltinOperator.NumAnd, | 781 '&': BuiltinOperator.NumAnd, |
807 '|': BuiltinOperator.NumOr, | 782 '|': BuiltinOperator.NumOr, |
808 '^': BuiltinOperator.NumXor, | 783 '^': BuiltinOperator.NumXor, |
809 '<': BuiltinOperator.NumLt, | 784 '<': BuiltinOperator.NumLt, |
810 '<=': BuiltinOperator.NumLe, | 785 '<=': BuiltinOperator.NumLe, |
811 '>': BuiltinOperator.NumGt, | 786 '>': BuiltinOperator.NumGt, |
812 '>=': BuiltinOperator.NumGe | 787 '>=': BuiltinOperator.NumGe |
813 }; | 788 }; |
814 | 789 |
815 /** | 790 /** |
816 * Uses the information from a preceding analysis pass in order to perform the | 791 * Uses the information from a preceding analysis pass in order to perform the |
817 * actual transformations on the CPS graph. | 792 * actual transformations on the CPS graph. |
818 */ | 793 */ |
819 class TransformingVisitor extends DeepRecursiveVisitor { | 794 class TransformingVisitor extends DeepRecursiveVisitor { |
820 final TypePropagationVisitor analyzer; | 795 final TypePropagationVisitor analyzer; |
821 final ConstantPropagationLattice lattice; | 796 final ConstantPropagationLattice lattice; |
822 final dart2js.Compiler compiler; | 797 final dart2js.Compiler compiler; |
823 final CpsFunctionCompiler functionCompiler; | 798 final CpsFunctionCompiler functionCompiler; |
824 | 799 |
825 JavaScriptBackend get backend => compiler.backend; | 800 JavaScriptBackend get backend => compiler.backend; |
826 BackendHelpers get helpers => backend.helpers; | 801 BackendHelpers get helpers => backend.helpers; |
827 TypeMaskSystem get typeSystem => lattice.typeSystem; | 802 TypeMaskSystem get typeSystem => lattice.typeSystem; |
828 types.DartTypes get dartTypes => lattice.dartTypes; | 803 types.DartTypes get dartTypes => lattice.dartTypes; |
829 World get classWorld => typeSystem.classWorld; | 804 World get classWorld => typeSystem.classWorld; |
830 Map<Variable, ConstantValue> get values => analyzer.values; | 805 Map<Variable, ConstantValue> get values => analyzer.values; |
831 | 806 |
832 final InternalErrorFunction internalError; | 807 final InternalErrorFunction internalError; |
833 | 808 |
834 final List<Node> stack = <Node>[]; | 809 final List<Node> stack = <Node>[]; |
835 | 810 |
836 TypeCheckOperator checkIsNumber; | 811 TypeCheckOperator checkIsNumber; |
837 | 812 |
838 TransformingVisitor(this.compiler, | 813 TransformingVisitor(this.compiler, this.functionCompiler, this.lattice, |
839 this.functionCompiler, | 814 this.analyzer, this.internalError) { |
840 this.lattice, | |
841 this.analyzer, | |
842 this.internalError) { | |
843 checkIsNumber = new ClassTypeCheckOperator( | 815 checkIsNumber = new ClassTypeCheckOperator( |
844 helpers.jsNumberClass, | 816 helpers.jsNumberClass, BuiltinOperator.IsNotNumber); |
845 BuiltinOperator.IsNotNumber); | |
846 } | 817 } |
847 | 818 |
848 void transform(FunctionDefinition root) { | 819 void transform(FunctionDefinition root) { |
849 // If one of the parameters has no value, the function is unreachable. | 820 // If one of the parameters has no value, the function is unreachable. |
850 // We assume all values in scope have a non-empty type (otherwise the | 821 // We assume all values in scope have a non-empty type (otherwise the |
851 // scope is unreachable), so this optimization is required. | 822 // scope is unreachable), so this optimization is required. |
852 // TODO(asgerf): Can we avoid emitting the function is the first place? | 823 // TODO(asgerf): Can we avoid emitting the function is the first place? |
853 for (Parameter param in root.parameters) { | 824 for (Parameter param in root.parameters) { |
854 if (getValue(param).isNothing) { | 825 if (getValue(param).isNothing) { |
855 // Replace with `throw "Unreachable";` | 826 // Replace with `throw "Unreachable";` |
856 CpsFragment cps = new CpsFragment(null); | 827 CpsFragment cps = new CpsFragment(null); |
857 Primitive message = cps.makeConstant( | 828 Primitive message = |
858 new StringConstantValue.fromString("Unreachable")); | 829 cps.makeConstant(new StringConstantValue.fromString("Unreachable")); |
859 cps.put(new Throw(message)); | 830 cps.put(new Throw(message)); |
860 replaceSubtree(root.body, cps.result); | 831 replaceSubtree(root.body, cps.result); |
861 return; | 832 return; |
862 } | 833 } |
863 } | 834 } |
864 push(root.body); | 835 push(root.body); |
865 while (stack.isNotEmpty) { | 836 while (stack.isNotEmpty) { |
866 visit(stack.removeLast()); | 837 visit(stack.removeLast()); |
867 } | 838 } |
868 } | 839 } |
(...skipping 292 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1161 if (node.argumentRefs.length > 1) return null; | 1132 if (node.argumentRefs.length > 1) return null; |
1162 if (node.callingConvention == CallingConvention.OneShotIntercepted) { | 1133 if (node.callingConvention == CallingConvention.OneShotIntercepted) { |
1163 return null; | 1134 return null; |
1164 } | 1135 } |
1165 | 1136 |
1166 bool trustPrimitives = compiler.options.trustPrimitives; | 1137 bool trustPrimitives = compiler.options.trustPrimitives; |
1167 | 1138 |
1168 /// Check that the receiver and argument satisfy the given type checks, and | 1139 /// Check that the receiver and argument satisfy the given type checks, and |
1169 /// throw a [NoSuchMethodError] or [ArgumentError] if the check fails. | 1140 /// throw a [NoSuchMethodError] or [ArgumentError] if the check fails. |
1170 CpsFragment makeGuard(TypeCheckOperator receiverGuard, | 1141 CpsFragment makeGuard(TypeCheckOperator receiverGuard, |
1171 [TypeCheckOperator argumentGuard]) { | 1142 [TypeCheckOperator argumentGuard]) { |
1172 CpsFragment cps = new CpsFragment(node.sourceInformation); | 1143 CpsFragment cps = new CpsFragment(node.sourceInformation); |
1173 | 1144 |
1174 // Make no guards if trusting primitives. | 1145 // Make no guards if trusting primitives. |
1175 if (trustPrimitives) return cps; | 1146 if (trustPrimitives) return cps; |
1176 | 1147 |
1177 // Determine which guards are needed. | 1148 // Determine which guards are needed. |
1178 ChecksNeeded receiverChecks = | 1149 ChecksNeeded receiverChecks = |
1179 receiverGuard.getChecksNeeded(node.receiver, classWorld); | 1150 receiverGuard.getChecksNeeded(node.receiver, classWorld); |
1180 bool needReceiverGuard = receiverChecks != ChecksNeeded.None; | 1151 bool needReceiverGuard = receiverChecks != ChecksNeeded.None; |
1181 bool needArgumentGuard = | 1152 bool needArgumentGuard = argumentGuard != null && |
1182 argumentGuard != null && | |
1183 argumentGuard.needsCheck(node.argument(0), classWorld); | 1153 argumentGuard.needsCheck(node.argument(0), classWorld); |
1184 | 1154 |
1185 if (!needReceiverGuard && !needArgumentGuard) return cps; | 1155 if (!needReceiverGuard && !needArgumentGuard) return cps; |
1186 | 1156 |
1187 // If we only need the receiver check, emit the specialized receiver | 1157 // If we only need the receiver check, emit the specialized receiver |
1188 // check instruction. Examples: | 1158 // check instruction. Examples: |
1189 // | 1159 // |
1190 // if (typeof receiver !== "number") return receiver.$lt; | 1160 // if (typeof receiver !== "number") return receiver.$lt; |
1191 // if (typeof receiver !== "number") return receiver.$lt(); | 1161 // if (typeof receiver !== "number") return receiver.$lt(); |
1192 // | 1162 // |
1193 if (!needArgumentGuard) { | 1163 if (!needArgumentGuard) { |
1194 Primitive condition = receiverGuard.makeCheck(cps, node.receiver); | 1164 Primitive condition = receiverGuard.makeCheck(cps, node.receiver); |
1195 cps.letPrim(new ReceiverCheck( | 1165 cps.letPrim(new ReceiverCheck( |
1196 node.receiver, | 1166 node.receiver, node.selector, node.sourceInformation, |
1197 node.selector, | |
1198 node.sourceInformation, | |
1199 condition: condition, | 1167 condition: condition, |
1200 useSelector: true, | 1168 useSelector: true, |
1201 isNullCheck: receiverChecks == ChecksNeeded.Null | 1169 isNullCheck: receiverChecks == ChecksNeeded.Null)); |
1202 )); | |
1203 return cps; | 1170 return cps; |
1204 } | 1171 } |
1205 | 1172 |
1206 // TODO(asgerf): We should consider specialized instructions for | 1173 // TODO(asgerf): We should consider specialized instructions for |
1207 // argument checks and receiver+argument checks, to avoid breaking up | 1174 // argument checks and receiver+argument checks, to avoid breaking up |
1208 // basic blocks. | 1175 // basic blocks. |
1209 | 1176 |
1210 // Emit as `H.iae(x)` if only the argument check may fail. For example: | 1177 // Emit as `H.iae(x)` if only the argument check may fail. For example: |
1211 // | 1178 // |
1212 // if (typeof argument !== "number") return H.iae(argument); | 1179 // if (typeof argument !== "number") return H.iae(argument); |
1213 // | 1180 // |
1214 if (!needReceiverGuard) { | 1181 if (!needReceiverGuard) { |
1215 cps.ifTruthy(argumentGuard.makeCheck(cps, node.argument(0))) | 1182 cps |
1216 .invokeStaticThrower(helpers.throwIllegalArgumentException, | 1183 .ifTruthy(argumentGuard.makeCheck(cps, node.argument(0))) |
1217 [node.argument(0)]); | 1184 .invokeStaticThrower( |
| 1185 helpers.throwIllegalArgumentException, [node.argument(0)]); |
1218 return cps; | 1186 return cps; |
1219 } | 1187 } |
1220 | 1188 |
1221 // Both receiver and argument check is needed. Emit as a combined check | 1189 // Both receiver and argument check is needed. Emit as a combined check |
1222 // using a one-shot interceptor to produce the exact error message in | 1190 // using a one-shot interceptor to produce the exact error message in |
1223 // the error case. For example: | 1191 // the error case. For example: |
1224 // | 1192 // |
1225 // if (typeof receiver !== "number" || typeof argument !== "number") | 1193 // if (typeof receiver !== "number" || typeof argument !== "number") |
1226 // return J.$lt(receiver, argument); | 1194 // return J.$lt(receiver, argument); |
1227 // | 1195 // |
1228 Continuation fail = cps.letCont(); | 1196 Continuation fail = cps.letCont(); |
1229 cps.ifTruthy(receiverGuard.makeCheck(cps, node.receiver)) | 1197 cps |
1230 .invokeContinuation(fail); | 1198 .ifTruthy(receiverGuard.makeCheck(cps, node.receiver)) |
1231 cps.ifTruthy(argumentGuard.makeCheck(cps, node.argument(0))) | 1199 .invokeContinuation(fail); |
1232 .invokeContinuation(fail); | 1200 cps |
| 1201 .ifTruthy(argumentGuard.makeCheck(cps, node.argument(0))) |
| 1202 .invokeContinuation(fail); |
1233 | 1203 |
1234 cps.insideContinuation(fail) | 1204 cps.insideContinuation(fail) |
1235 ..invokeMethod(node.receiver, node.selector, node.mask, | 1205 ..invokeMethod( |
1236 [node.argument(0)], | 1206 node.receiver, node.selector, node.mask, [node.argument(0)], |
1237 callingConvention: CallingConvention.OneShotIntercepted) | 1207 callingConvention: CallingConvention.OneShotIntercepted) |
1238 ..put(new Unreachable()); | 1208 ..put(new Unreachable()); |
1239 | 1209 |
1240 return cps; | 1210 return cps; |
1241 } | 1211 } |
1242 | 1212 |
1243 /// Replaces the call with [operator], using the receiver and first argument | 1213 /// Replaces the call with [operator], using the receiver and first argument |
1244 /// as operands (in that order). | 1214 /// as operands (in that order). |
1245 /// | 1215 /// |
1246 /// If [guard] is given, the receiver and argument are both checked using | 1216 /// If [guard] is given, the receiver and argument are both checked using |
1247 /// that operator. | 1217 /// that operator. |
1248 CpsFragment makeBinary(BuiltinOperator operator, | 1218 CpsFragment makeBinary(BuiltinOperator operator, |
1249 {TypeCheckOperator guard: TypeCheckOperator.none}) { | 1219 {TypeCheckOperator guard: TypeCheckOperator.none}) { |
1250 CpsFragment cps = makeGuard(guard, guard); | 1220 CpsFragment cps = makeGuard(guard, guard); |
1251 Primitive left = guard.makeRefinement(cps, node.receiver, classWorld); | 1221 Primitive left = guard.makeRefinement(cps, node.receiver, classWorld); |
1252 Primitive right = | 1222 Primitive right = guard.makeRefinement(cps, node.argument(0), classWorld); |
1253 guard.makeRefinement(cps, node.argument(0), classWorld); | |
1254 Primitive result = cps.applyBuiltin(operator, [left, right]); | 1223 Primitive result = cps.applyBuiltin(operator, [left, right]); |
1255 result.hint = node.hint; | 1224 result.hint = node.hint; |
1256 node.replaceUsesWith(result); | 1225 node.replaceUsesWith(result); |
1257 return cps; | 1226 return cps; |
1258 } | 1227 } |
1259 | 1228 |
1260 /// Like [makeBinary] but for unary operators with the receiver as the | 1229 /// Like [makeBinary] but for unary operators with the receiver as the |
1261 /// argument. | 1230 /// argument. |
1262 CpsFragment makeUnary(BuiltinOperator operator, | 1231 CpsFragment makeUnary(BuiltinOperator operator, |
1263 {TypeCheckOperator guard: TypeCheckOperator.none}) { | 1232 {TypeCheckOperator guard: TypeCheckOperator.none}) { |
1264 CpsFragment cps = makeGuard(guard); | 1233 CpsFragment cps = makeGuard(guard); |
1265 Primitive argument = | 1234 Primitive argument = guard.makeRefinement(cps, node.receiver, classWorld); |
1266 guard.makeRefinement(cps, node.receiver, classWorld); | |
1267 Primitive result = cps.applyBuiltin(operator, [argument]); | 1235 Primitive result = cps.applyBuiltin(operator, [argument]); |
1268 result.hint = node.hint; | 1236 result.hint = node.hint; |
1269 node.replaceUsesWith(result); | 1237 node.replaceUsesWith(result); |
1270 return cps; | 1238 return cps; |
1271 } | 1239 } |
1272 | 1240 |
1273 Selector renameToOptimizedSelector(String name) { | 1241 Selector renameToOptimizedSelector(String name) { |
1274 return new Selector.call( | 1242 return new Selector.call( |
1275 new Name(name, backend.helpers.interceptorsLibrary), | 1243 new Name(name, backend.helpers.interceptorsLibrary), |
1276 node.selector.callStructure); | 1244 node.selector.callStructure); |
1277 } | 1245 } |
1278 | 1246 |
1279 /// Replaces the call with a call to [name] with the same inputs. | 1247 /// Replaces the call with a call to [name] with the same inputs. |
1280 InvokeMethod makeRenamedInvoke(String name) { | 1248 InvokeMethod makeRenamedInvoke(String name) { |
1281 return new InvokeMethod(node.receiver, | 1249 return new InvokeMethod(node.receiver, renameToOptimizedSelector(name), |
1282 renameToOptimizedSelector(name), | 1250 node.mask, node.arguments.toList(), |
1283 node.mask, | |
1284 node.arguments.toList(), | |
1285 sourceInformation: node.sourceInformation, | 1251 sourceInformation: node.sourceInformation, |
1286 callingConvention: node.callingConvention, | 1252 callingConvention: node.callingConvention, |
1287 interceptor: node.interceptor); | 1253 interceptor: node.interceptor); |
1288 } | 1254 } |
1289 | 1255 |
1290 TypeMask successType = | 1256 TypeMask successType = |
1291 typeSystem.receiverTypeFor(node.selector, node.receiver.type); | 1257 typeSystem.receiverTypeFor(node.selector, node.receiver.type); |
1292 | 1258 |
1293 if (node.selector.isOperator && node.argumentRefs.length == 1) { | 1259 if (node.selector.isOperator && node.argumentRefs.length == 1) { |
1294 Primitive leftArg = node.receiver; | 1260 Primitive leftArg = node.receiver; |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1341 return makeBinary(BuiltinOperator.NumShl, guard: checkIsNumber); | 1307 return makeBinary(BuiltinOperator.NumShl, guard: checkIsNumber); |
1342 } | 1308 } |
1343 // Try to insert a shift-right operator. JavaScript's right shift is | 1309 // Try to insert a shift-right operator. JavaScript's right shift is |
1344 // consistent with Dart's only for left operands in the unsigned | 1310 // consistent with Dart's only for left operands in the unsigned |
1345 // 32-bit range. | 1311 // 32-bit range. |
1346 if (opname == '>>') { | 1312 if (opname == '>>') { |
1347 if (lattice.isDefinitelyUint32(left, allowNull: true) && | 1313 if (lattice.isDefinitelyUint32(left, allowNull: true) && |
1348 lattice.isDefinitelyIntInRange(right, min: 0, max: 31)) { | 1314 lattice.isDefinitelyIntInRange(right, min: 0, max: 31)) { |
1349 return makeBinary(BuiltinOperator.NumShr, guard: checkIsNumber); | 1315 return makeBinary(BuiltinOperator.NumShr, guard: checkIsNumber); |
1350 } else if (lattice.isDefinitelyUint(left) && | 1316 } else if (lattice.isDefinitelyUint(left) && |
1351 lattice.isDefinitelyUint(right)) { | 1317 lattice.isDefinitelyUint(right)) { |
1352 return makeRenamedInvoke('_shrBothPositive'); | 1318 return makeRenamedInvoke('_shrBothPositive'); |
1353 } else if (lattice.isDefinitelyUint(left) && | 1319 } else if (lattice.isDefinitelyUint(left) && |
1354 lattice.isDefinitelyNum(right)) { | 1320 lattice.isDefinitelyNum(right)) { |
1355 return makeRenamedInvoke('_shrReceiverPositive'); | 1321 return makeRenamedInvoke('_shrReceiverPositive'); |
1356 } else if (lattice.isDefinitelyNum(left) && | 1322 } else if (lattice.isDefinitelyNum(left) && |
1357 lattice.isDefinitelyUint(right)) { | 1323 lattice.isDefinitelyUint(right)) { |
1358 return makeRenamedInvoke('_shrOtherPositive'); | 1324 return makeRenamedInvoke('_shrOtherPositive'); |
1359 } | 1325 } |
1360 } | 1326 } |
1361 // Try to use remainder for '%'. Both operands must be non-negative | 1327 // Try to use remainder for '%'. Both operands must be non-negative |
1362 // and the divisor must be non-zero. | 1328 // and the divisor must be non-zero. |
1363 if (opname == '%' && | 1329 if (opname == '%' && |
1364 lattice.isDefinitelyUint(left, allowNull: true) && | 1330 lattice.isDefinitelyUint(left, allowNull: true) && |
1365 lattice.isDefinitelyUint(right) && | 1331 lattice.isDefinitelyUint(right) && |
1366 lattice.isDefinitelyIntInRange(right, min: 1)) { | 1332 lattice.isDefinitelyIntInRange(right, min: 1)) { |
1367 return makeBinary(BuiltinOperator.NumRemainder, | 1333 return makeBinary(BuiltinOperator.NumRemainder, |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1409 guard: checkIsNumber); | 1375 guard: checkIsNumber); |
1410 } | 1376 } |
1411 } | 1377 } |
1412 } else if (name == 'codeUnitAt') { | 1378 } else if (name == 'codeUnitAt') { |
1413 if (node.argumentRefs.length == 1) { | 1379 if (node.argumentRefs.length == 1) { |
1414 Primitive index = node.argument(0); | 1380 Primitive index = node.argument(0); |
1415 if (lattice.isDefinitelyString(receiverValue) && | 1381 if (lattice.isDefinitelyString(receiverValue) && |
1416 lattice.isDefinitelyInt(getValue(index))) { | 1382 lattice.isDefinitelyInt(getValue(index))) { |
1417 CpsFragment cps = new CpsFragment(node.sourceInformation); | 1383 CpsFragment cps = new CpsFragment(node.sourceInformation); |
1418 receiver = makeBoundsCheck(cps, receiver, index); | 1384 receiver = makeBoundsCheck(cps, receiver, index); |
1419 ApplyBuiltinOperator get = | 1385 ApplyBuiltinOperator get = cps.applyBuiltin( |
1420 cps.applyBuiltin(BuiltinOperator.CharCodeAt, | 1386 BuiltinOperator.CharCodeAt, <Primitive>[receiver, index]); |
1421 <Primitive>[receiver, index]); | |
1422 node.replaceUsesWith(get); | 1387 node.replaceUsesWith(get); |
1423 get.hint = node.hint; | 1388 get.hint = node.hint; |
1424 return cps; | 1389 return cps; |
1425 } | 1390 } |
1426 } | 1391 } |
1427 } | 1392 } |
1428 } | 1393 } |
1429 return null; | 1394 return null; |
1430 } | 1395 } |
1431 | 1396 |
(...skipping 19 matching lines...) Expand all Loading... |
1451 // TODO(asgerf): Inlining native fields will make some tests pass for the | 1416 // TODO(asgerf): Inlining native fields will make some tests pass for the |
1452 // wrong reason, so for testing reasons avoid inlining them. | 1417 // wrong reason, so for testing reasons avoid inlining them. |
1453 if (backend.isNative(target) || backend.isJsInterop(target)) { | 1418 if (backend.isNative(target) || backend.isJsInterop(target)) { |
1454 return null; | 1419 return null; |
1455 } | 1420 } |
1456 if (node.selector.isGetter) { | 1421 if (node.selector.isGetter) { |
1457 return new GetField(node.receiver, target); | 1422 return new GetField(node.receiver, target); |
1458 } else if (node.selector.isSetter) { | 1423 } else if (node.selector.isSetter) { |
1459 if (target.isFinal) return null; | 1424 if (target.isFinal) return null; |
1460 assert(node.hasNoUses); | 1425 assert(node.hasNoUses); |
1461 return new SetField(node.receiver, | 1426 return new SetField(node.receiver, target, node.argument(0)); |
1462 target, | |
1463 node.argument(0)); | |
1464 } else if (node.selector.isCall) { | 1427 } else if (node.selector.isCall) { |
1465 CpsFragment cps = new CpsFragment(node.sourceInformation); | 1428 CpsFragment cps = new CpsFragment(node.sourceInformation); |
1466 Primitive fieldValue = cps.letPrim(new GetField(node.receiver, target)); | 1429 Primitive fieldValue = cps.letPrim(new GetField(node.receiver, target)); |
1467 Primitive result = cps.invokeMethod( | 1430 Primitive result = cps.invokeMethod( |
1468 fieldValue, | 1431 fieldValue, |
1469 new Selector.callClosureFrom(node.selector), | 1432 new Selector.callClosureFrom(node.selector), |
1470 typeSystem.getFieldType(target), | 1433 typeSystem.getFieldType(target), |
1471 node.arguments.toList()); | 1434 node.arguments.toList()); |
1472 node.replaceUsesWith(result); | 1435 node.replaceUsesWith(result); |
1473 return cps; | 1436 return cps; |
1474 } else { | 1437 } else { |
1475 return null; | 1438 return null; |
1476 } | 1439 } |
1477 } | 1440 } |
1478 | 1441 |
1479 /// Create a check that throws if [index] is not a valid index on [list]. | 1442 /// Create a check that throws if [index] is not a valid index on [list]. |
1480 /// | 1443 /// |
1481 /// This function assumes that [index] is an integer. | 1444 /// This function assumes that [index] is an integer. |
1482 /// | 1445 /// |
1483 /// Returns a CPS fragment whose context is the branch where no error | 1446 /// Returns a CPS fragment whose context is the branch where no error |
1484 /// was thrown. | 1447 /// was thrown. |
1485 Primitive makeBoundsCheck(CpsFragment cps, | 1448 Primitive makeBoundsCheck(CpsFragment cps, Primitive list, Primitive index, |
1486 Primitive list, | 1449 [int checkKind = BoundsCheck.BOTH_BOUNDS | BoundsCheck.INTEGER]) { |
1487 Primitive index, | |
1488 [int checkKind = BoundsCheck.BOTH_BOUNDS | BoundsCheck.INTEGER]) { | |
1489 if (compiler.options.trustPrimitives) { | 1450 if (compiler.options.trustPrimitives) { |
1490 return cps.letPrim(new BoundsCheck.noCheck(list, cps.sourceInformation)); | 1451 return cps.letPrim(new BoundsCheck.noCheck(list, cps.sourceInformation)); |
1491 } else { | 1452 } else { |
1492 GetLength length = cps.letPrim(new GetLength(list)); | 1453 GetLength length = cps.letPrim(new GetLength(list)); |
1493 list = cps.refine(list, typeSystem.nonNullType); | 1454 list = cps.refine(list, typeSystem.nonNullType); |
1494 BoundsCheck check = cps.letPrim(new BoundsCheck(list, index, length, | 1455 BoundsCheck check = cps.letPrim(new BoundsCheck( |
1495 checkKind, cps.sourceInformation)); | 1456 list, index, length, checkKind, cps.sourceInformation)); |
1496 if (check.hasIntegerCheck) { | 1457 if (check.hasIntegerCheck) { |
1497 if (typeSystem.isDefinitelyInt(index.type)) { | 1458 if (typeSystem.isDefinitelyInt(index.type)) { |
1498 check.checks &= ~BoundsCheck.INTEGER; | 1459 check.checks &= ~BoundsCheck.INTEGER; |
1499 } else { | 1460 } else { |
1500 cps.refine(index, typeSystem.uint32Type); | 1461 cps.refine(index, typeSystem.uint32Type); |
1501 } | 1462 } |
1502 } | 1463 } |
1503 return check; | 1464 return check; |
1504 } | 1465 } |
1505 } | 1466 } |
1506 | 1467 |
1507 /// Create a check that throws if the length of [list] is not equal to | 1468 /// Create a check that throws if the length of [list] is not equal to |
1508 /// [originalLength]. | 1469 /// [originalLength]. |
1509 /// | 1470 /// |
1510 /// Returns a CPS fragment whose context is the branch where no error | 1471 /// Returns a CPS fragment whose context is the branch where no error |
1511 /// was thrown. | 1472 /// was thrown. |
1512 CpsFragment makeConcurrentModificationCheck(Primitive list, | 1473 CpsFragment makeConcurrentModificationCheck( |
1513 Primitive originalLength, | 1474 Primitive list, Primitive originalLength, SourceInformation sourceInfo) { |
1514 SourceInformation sourceInfo) { | |
1515 CpsFragment cps = new CpsFragment(sourceInfo); | 1475 CpsFragment cps = new CpsFragment(sourceInfo); |
1516 Primitive lengthChanged = cps.applyBuiltin( | 1476 Primitive lengthChanged = cps.applyBuiltin(BuiltinOperator.StrictNeq, |
1517 BuiltinOperator.StrictNeq, | |
1518 <Primitive>[originalLength, cps.letPrim(new GetLength(list))]); | 1477 <Primitive>[originalLength, cps.letPrim(new GetLength(list))]); |
1519 cps.ifTruthy(lengthChanged).invokeStaticThrower( | 1478 cps.ifTruthy(lengthChanged).invokeStaticThrower( |
1520 helpers.throwConcurrentModificationError, | 1479 helpers.throwConcurrentModificationError, <Primitive>[list]); |
1521 <Primitive>[list]); | |
1522 return cps; | 1480 return cps; |
1523 } | 1481 } |
1524 | 1482 |
1525 /// Tries to replace [node] with a direct `length` or index access. | 1483 /// Tries to replace [node] with a direct `length` or index access. |
1526 /// | 1484 /// |
1527 /// Returns `true` if the node was replaced. | 1485 /// Returns `true` if the node was replaced. |
1528 specializeIndexableAccess(InvokeMethod node) { | 1486 specializeIndexableAccess(InvokeMethod node) { |
1529 Primitive receiver = node.receiver; | 1487 Primitive receiver = node.receiver; |
1530 AbstractConstantValue receiverValue = getValue(receiver); | 1488 AbstractConstantValue receiverValue = getValue(receiver); |
1531 if (!typeSystem.isDefinitelyIndexable(receiverValue.type, | 1489 if (!typeSystem.isDefinitelyIndexable(receiverValue.type, |
1532 allowNull: true)) { | 1490 allowNull: true)) { |
1533 return null; | 1491 return null; |
1534 } | 1492 } |
1535 switch (node.selector.name) { | 1493 switch (node.selector.name) { |
1536 case 'length': | 1494 case 'length': |
1537 if (node.selector.isGetter) { | 1495 if (node.selector.isGetter) { |
1538 return new GetLength(receiver); | 1496 return new GetLength(receiver); |
1539 } | 1497 } |
1540 if (node.selector.isSetter) { | 1498 if (node.selector.isSetter) { |
1541 if (!typeSystem.isDefinitelyExtendableArray(receiver.type, | 1499 if (!typeSystem.isDefinitelyExtendableArray(receiver.type, |
1542 allowNull: true)) { | 1500 allowNull: true)) { |
1543 return null; | 1501 return null; |
1544 } | 1502 } |
1545 CpsFragment cps = new CpsFragment(node.sourceInformation); | 1503 CpsFragment cps = new CpsFragment(node.sourceInformation); |
1546 Primitive newLength = node.argument(0); | 1504 Primitive newLength = node.argument(0); |
1547 if (!typeSystem.isDefinitelyUint(newLength.type)) { | 1505 if (!typeSystem.isDefinitelyUint(newLength.type)) { |
1548 // TODO(asgerf): We could let the SetLength instruction throw for | 1506 // TODO(asgerf): We could let the SetLength instruction throw for |
1549 // negative right-hand sides (see length setter in js_array.dart). | 1507 // negative right-hand sides (see length setter in js_array.dart). |
1550 if (compiler.options.trustPrimitives) { | 1508 if (compiler.options.trustPrimitives) { |
1551 newLength = cps.refine(newLength, typeSystem.uint32Type); | 1509 newLength = cps.refine(newLength, typeSystem.uint32Type); |
1552 newLength.type = typeSystem.uint32Type; | 1510 newLength.type = typeSystem.uint32Type; |
1553 } else { | 1511 } else { |
1554 return null; | 1512 return null; |
1555 } | 1513 } |
1556 } | 1514 } |
1557 cps.letPrim(new ApplyBuiltinMethod( | 1515 cps.letPrim(new ApplyBuiltinMethod(BuiltinMethod.SetLength, receiver, |
1558 BuiltinMethod.SetLength, | 1516 [newLength], node.sourceInformation)); |
1559 receiver, | |
1560 [newLength], | |
1561 node.sourceInformation)); | |
1562 if (!typeSystem.isDefinitelyUint32(newLength.type)) { | 1517 if (!typeSystem.isDefinitelyUint32(newLength.type)) { |
1563 // If the setter succeeded, the length must have been a uint32. | 1518 // If the setter succeeded, the length must have been a uint32. |
1564 cps.refine(newLength, typeSystem.uint32Type); | 1519 cps.refine(newLength, typeSystem.uint32Type); |
1565 } | 1520 } |
1566 return cps; | 1521 return cps; |
1567 } | 1522 } |
1568 return null; | 1523 return null; |
1569 | 1524 |
1570 case '[]': | 1525 case '[]': |
1571 Primitive index = node.argument(0); | 1526 Primitive index = node.argument(0); |
1572 CpsFragment cps = new CpsFragment(node.sourceInformation); | 1527 CpsFragment cps = new CpsFragment(node.sourceInformation); |
1573 receiver = makeBoundsCheck(cps, receiver, index); | 1528 receiver = makeBoundsCheck(cps, receiver, index); |
1574 GetIndex get = cps.letPrim(new GetIndex(receiver, index)); | 1529 GetIndex get = cps.letPrim(new GetIndex(receiver, index)); |
1575 node.replaceUsesWith(get); | 1530 node.replaceUsesWith(get); |
1576 // TODO(asgerf): Make replaceUsesWith set the hint? | 1531 // TODO(asgerf): Make replaceUsesWith set the hint? |
1577 get.hint = node.hint; | 1532 get.hint = node.hint; |
1578 return cps; | 1533 return cps; |
1579 | 1534 |
1580 case '[]=': | 1535 case '[]=': |
1581 if (!typeSystem.isDefinitelyMutableIndexable(receiverValue.type, | 1536 if (!typeSystem.isDefinitelyMutableIndexable(receiverValue.type, |
1582 allowNull: true)) { | 1537 allowNull: true)) { |
1583 return null; | 1538 return null; |
1584 } | 1539 } |
1585 Primitive index = node.argument(0); | 1540 Primitive index = node.argument(0); |
1586 Primitive value = node.argument(1); | 1541 Primitive value = node.argument(1); |
1587 CpsFragment cps = new CpsFragment(node.sourceInformation); | 1542 CpsFragment cps = new CpsFragment(node.sourceInformation); |
1588 receiver = makeBoundsCheck(cps, receiver, index); | 1543 receiver = makeBoundsCheck(cps, receiver, index); |
1589 cps.letPrim(new SetIndex(receiver, index, value)); | 1544 cps.letPrim(new SetIndex(receiver, index, value)); |
1590 assert(node.hasNoUses); | 1545 assert(node.hasNoUses); |
1591 return cps; | 1546 return cps; |
1592 | 1547 |
1593 case 'isEmpty': | 1548 case 'isEmpty': |
1594 if (!node.selector.isGetter) return null; | 1549 if (!node.selector.isGetter) return null; |
1595 CpsFragment cps = new CpsFragment(node.sourceInformation); | 1550 CpsFragment cps = new CpsFragment(node.sourceInformation); |
1596 Primitive length = cps.letPrim(new GetLength(receiver)); | 1551 Primitive length = cps.letPrim(new GetLength(receiver)); |
1597 Constant zero = cps.makeZero(); | 1552 Constant zero = cps.makeZero(); |
1598 ApplyBuiltinOperator op = cps.applyBuiltin(BuiltinOperator.StrictEq, | 1553 ApplyBuiltinOperator op = |
1599 [length, zero]); | 1554 cps.applyBuiltin(BuiltinOperator.StrictEq, [length, zero]); |
1600 node.replaceUsesWith(op); | 1555 node.replaceUsesWith(op); |
1601 op.hint = node.hint; | 1556 op.hint = node.hint; |
1602 return cps; | 1557 return cps; |
1603 | 1558 |
1604 case 'isNotEmpty': | 1559 case 'isNotEmpty': |
1605 if (!node.selector.isGetter) return null; | 1560 if (!node.selector.isGetter) return null; |
1606 CpsFragment cps = new CpsFragment(node.sourceInformation); | 1561 CpsFragment cps = new CpsFragment(node.sourceInformation); |
1607 Primitive length = cps.letPrim(new GetLength(receiver)); | 1562 Primitive length = cps.letPrim(new GetLength(receiver)); |
1608 Constant zero = cps.makeZero(); | 1563 Constant zero = cps.makeZero(); |
1609 ApplyBuiltinOperator op = cps.applyBuiltin(BuiltinOperator.StrictNeq, | 1564 ApplyBuiltinOperator op = |
1610 [length, zero]); | 1565 cps.applyBuiltin(BuiltinOperator.StrictNeq, [length, zero]); |
1611 node.replaceUsesWith(op); | 1566 node.replaceUsesWith(op); |
1612 op.hint = node.hint; | 1567 op.hint = node.hint; |
1613 return cps; | 1568 return cps; |
1614 | 1569 |
1615 default: | 1570 default: |
1616 return null; | 1571 return null; |
1617 } | 1572 } |
1618 } | 1573 } |
1619 | 1574 |
1620 /// Tries to replace [node] with one or more direct array access operations. | 1575 /// Tries to replace [node] with one or more direct array access operations. |
(...skipping 14 matching lines...) Expand all Loading... |
1635 switch (node.selector.name) { | 1590 switch (node.selector.name) { |
1636 case 'add': | 1591 case 'add': |
1637 if (!node.selector.isCall || | 1592 if (!node.selector.isCall || |
1638 node.selector.positionalArgumentCount != 1 || | 1593 node.selector.positionalArgumentCount != 1 || |
1639 node.selector.namedArgumentCount != 0) { | 1594 node.selector.namedArgumentCount != 0) { |
1640 return null; | 1595 return null; |
1641 } | 1596 } |
1642 if (!isExtendable) return null; | 1597 if (!isExtendable) return null; |
1643 Primitive addedItem = node.argument(0); | 1598 Primitive addedItem = node.argument(0); |
1644 CpsFragment cps = new CpsFragment(sourceInfo); | 1599 CpsFragment cps = new CpsFragment(sourceInfo); |
1645 cps.invokeBuiltin(BuiltinMethod.Push, | 1600 cps.invokeBuiltin(BuiltinMethod.Push, list, <Primitive>[addedItem]); |
1646 list, | |
1647 <Primitive>[addedItem]); | |
1648 if (node.hasAtLeastOneUse) { | 1601 if (node.hasAtLeastOneUse) { |
1649 node.replaceUsesWith(cps.makeNull()); | 1602 node.replaceUsesWith(cps.makeNull()); |
1650 } | 1603 } |
1651 return cps; | 1604 return cps; |
1652 | 1605 |
1653 case 'removeLast': | 1606 case 'removeLast': |
1654 if (!node.selector.isCall || | 1607 if (!node.selector.isCall || node.selector.argumentCount != 0) { |
1655 node.selector.argumentCount != 0) { | |
1656 return null; | 1608 return null; |
1657 } | 1609 } |
1658 if (!isExtendable) return null; | 1610 if (!isExtendable) return null; |
1659 CpsFragment cps = new CpsFragment(sourceInfo); | 1611 CpsFragment cps = new CpsFragment(sourceInfo); |
1660 list = makeBoundsCheck(cps, list, cps.makeMinusOne(), | 1612 list = makeBoundsCheck( |
1661 BoundsCheck.EMPTINESS); | 1613 cps, list, cps.makeMinusOne(), BoundsCheck.EMPTINESS); |
1662 Primitive removedItem = cps.invokeBuiltin(BuiltinMethod.Pop, | 1614 Primitive removedItem = |
1663 list, | 1615 cps.invokeBuiltin(BuiltinMethod.Pop, list, <Primitive>[]); |
1664 <Primitive>[]); | |
1665 removedItem.hint = node.hint; | 1616 removedItem.hint = node.hint; |
1666 node.replaceUsesWith(removedItem); | 1617 node.replaceUsesWith(removedItem); |
1667 return cps; | 1618 return cps; |
1668 | 1619 |
1669 case 'addAll': | 1620 case 'addAll': |
1670 if (!node.selector.isCall || | 1621 if (!node.selector.isCall || node.selector.argumentCount != 1) { |
1671 node.selector.argumentCount != 1) { | |
1672 return null; | 1622 return null; |
1673 } | 1623 } |
1674 if (!isExtendable) return null; | 1624 if (!isExtendable) return null; |
1675 Primitive addedList = node.argument(0); | 1625 Primitive addedList = node.argument(0); |
1676 // Rewrite addAll([x1, ..., xN]) to push(x1), ..., push(xN). | 1626 // Rewrite addAll([x1, ..., xN]) to push(x1), ..., push(xN). |
1677 // Ensure that the list is not mutated between creation and use. | 1627 // Ensure that the list is not mutated between creation and use. |
1678 // We aim for the common case where this is the only use of the list, | 1628 // We aim for the common case where this is the only use of the list, |
1679 // which also guarantees that this list is not mutated before use. | 1629 // which also guarantees that this list is not mutated before use. |
1680 if (addedList is! LiteralList || !addedList.hasExactlyOneUse) { | 1630 if (addedList is! LiteralList || !addedList.hasExactlyOneUse) { |
1681 return null; | 1631 return null; |
1682 } | 1632 } |
1683 LiteralList addedLiteral = addedList; | 1633 LiteralList addedLiteral = addedList; |
1684 CpsFragment cps = new CpsFragment(sourceInfo); | 1634 CpsFragment cps = new CpsFragment(sourceInfo); |
1685 for (Reference value in addedLiteral.valueRefs) { | 1635 for (Reference value in addedLiteral.valueRefs) { |
1686 cps.invokeBuiltin(BuiltinMethod.Push, | 1636 cps.invokeBuiltin( |
1687 list, | 1637 BuiltinMethod.Push, list, <Primitive>[value.definition]); |
1688 <Primitive>[value.definition]); | |
1689 } | 1638 } |
1690 if (node.hasAtLeastOneUse) { | 1639 if (node.hasAtLeastOneUse) { |
1691 node.replaceUsesWith(cps.makeNull()); | 1640 node.replaceUsesWith(cps.makeNull()); |
1692 } | 1641 } |
1693 return cps; | 1642 return cps; |
1694 | 1643 |
1695 case 'elementAt': | 1644 case 'elementAt': |
1696 if (!node.selector.isCall || | 1645 if (!node.selector.isCall || |
1697 node.selector.positionalArgumentCount != 1 || | 1646 node.selector.positionalArgumentCount != 1 || |
1698 node.selector.namedArgumentCount != 0) { | 1647 node.selector.namedArgumentCount != 0) { |
1699 return null; | 1648 return null; |
1700 } | 1649 } |
1701 if (listValue.isNullable) return null; | 1650 if (listValue.isNullable) return null; |
1702 Primitive index = node.argument(0); | 1651 Primitive index = node.argument(0); |
1703 if (!lattice.isDefinitelyInt(getValue(index))) return null; | 1652 if (!lattice.isDefinitelyInt(getValue(index))) return null; |
1704 CpsFragment cps = new CpsFragment(node.sourceInformation); | 1653 CpsFragment cps = new CpsFragment(node.sourceInformation); |
1705 list = makeBoundsCheck(cps, list, index); | 1654 list = makeBoundsCheck(cps, list, index); |
1706 GetIndex get = cps.letPrim(new GetIndex(list, index)); | 1655 GetIndex get = cps.letPrim(new GetIndex(list, index)); |
1707 get.hint = node.hint; | 1656 get.hint = node.hint; |
1708 node.replaceUsesWith(get); | 1657 node.replaceUsesWith(get); |
1709 return cps; | 1658 return cps; |
1710 | 1659 |
1711 case 'forEach': | 1660 case 'forEach': |
1712 Element element = | 1661 Element element = |
1713 compiler.world.locateSingleElement(node.selector, listValue.type); | 1662 compiler.world.locateSingleElement(node.selector, listValue.type); |
1714 if (element == null || | 1663 if (element == null || !element.isFunction || !node.selector.isCall) |
1715 !element.isFunction || | 1664 return null; |
1716 !node.selector.isCall) return null; | |
1717 assert(node.selector.positionalArgumentCount == 1); | 1665 assert(node.selector.positionalArgumentCount == 1); |
1718 assert(node.selector.namedArgumentCount == 0); | 1666 assert(node.selector.namedArgumentCount == 0); |
1719 FunctionDefinition target = functionCompiler.compileToCpsIr(element); | 1667 FunctionDefinition target = functionCompiler.compileToCpsIr(element); |
1720 | 1668 |
1721 CpsFragment cps = new CpsFragment(node.sourceInformation); | 1669 CpsFragment cps = new CpsFragment(node.sourceInformation); |
1722 Primitive result = cps.inlineFunction(target, | 1670 Primitive result = cps.inlineFunction( |
1723 node.receiver, | 1671 target, node.receiver, node.arguments.toList(), |
1724 node.arguments.toList(), | 1672 interceptor: node.interceptor, hint: node.hint); |
1725 interceptor: node.interceptor, | |
1726 hint: node.hint); | |
1727 node.replaceUsesWith(result); | 1673 node.replaceUsesWith(result); |
1728 return cps; | 1674 return cps; |
1729 | 1675 |
1730 case 'iterator': | 1676 case 'iterator': |
1731 // TODO(asgerf): This should be done differently. | 1677 // TODO(asgerf): This should be done differently. |
1732 // The types are recomputed in a very error-prone manner. | 1678 // The types are recomputed in a very error-prone manner. |
1733 if (!node.selector.isGetter) return null; | 1679 if (!node.selector.isGetter) return null; |
1734 Primitive iterator = node; | 1680 Primitive iterator = node; |
1735 LetPrim iteratorBinding = node.parent; | 1681 LetPrim iteratorBinding = node.parent; |
1736 | 1682 |
(...skipping 21 matching lines...) Expand all Loading... |
1758 InvokeMethod use = ref.parent; | 1704 InvokeMethod use = ref.parent; |
1759 if (use.selector == Selectors.current) { | 1705 if (use.selector == Selectors.current) { |
1760 // Rewrite iterator.current to a use of the 'current' variable. | 1706 // Rewrite iterator.current to a use of the 'current' variable. |
1761 if (use.hint != null) { | 1707 if (use.hint != null) { |
1762 // If 'current' was originally moved into a named variable, use | 1708 // If 'current' was originally moved into a named variable, use |
1763 // that variable name for the mutable variable. | 1709 // that variable name for the mutable variable. |
1764 current.hint = use.hint; | 1710 current.hint = use.hint; |
1765 } | 1711 } |
1766 use.replaceWith(new GetMutable(current)); | 1712 use.replaceWith(new GetMutable(current)); |
1767 } else { | 1713 } else { |
1768 assert (use.selector == Selectors.moveNext); | 1714 assert(use.selector == Selectors.moveNext); |
1769 // Rewrite iterator.moveNext() to: | 1715 // Rewrite iterator.moveNext() to: |
1770 // | 1716 // |
1771 // if (index < list.length) { | 1717 // if (index < list.length) { |
1772 // current = null; | 1718 // current = null; |
1773 // continuation(false); | 1719 // continuation(false); |
1774 // } else { | 1720 // } else { |
1775 // current = list[index]; | 1721 // current = list[index]; |
1776 // index = index + 1; | 1722 // index = index + 1; |
1777 // continuation(true); | 1723 // continuation(true); |
1778 // } | 1724 // } |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1811 // The check before the loop can often be eliminated because it | 1757 // The check before the loop can often be eliminated because it |
1812 // follows immediately after the 'iterator' call. | 1758 // follows immediately after the 'iterator' call. |
1813 InteriorNode parent = getEffectiveParent(use.parent); | 1759 InteriorNode parent = getEffectiveParent(use.parent); |
1814 if (!isFixedLength) { | 1760 if (!isFixedLength) { |
1815 if (parent is Continuation && parent.isRecursive) { | 1761 if (parent is Continuation && parent.isRecursive) { |
1816 // Check for concurrent modification before every invocation | 1762 // Check for concurrent modification before every invocation |
1817 // of the continuation. | 1763 // of the continuation. |
1818 // TODO(asgerf): Do this in a continuation so multiple | 1764 // TODO(asgerf): Do this in a continuation so multiple |
1819 // continues can share the same code. | 1765 // continues can share the same code. |
1820 for (Reference ref = parent.firstRef; | 1766 for (Reference ref = parent.firstRef; |
1821 ref != null; | 1767 ref != null; |
1822 ref = ref.next) { | 1768 ref = ref.next) { |
1823 Expression invocationCaller = ref.parent; | 1769 Expression invocationCaller = ref.parent; |
1824 if (getEffectiveParent(invocationCaller) == iteratorBinding) { | 1770 if (getEffectiveParent(invocationCaller) == iteratorBinding) { |
1825 // No need to check for concurrent modification immediately | 1771 // No need to check for concurrent modification immediately |
1826 // after the call to 'iterator'. | 1772 // after the call to 'iterator'. |
1827 continue; | 1773 continue; |
1828 } | 1774 } |
1829 CpsFragment check = makeConcurrentModificationCheck( | 1775 CpsFragment check = makeConcurrentModificationCheck( |
1830 list, originalLength, sourceInfo); | 1776 list, originalLength, sourceInfo); |
1831 insertBefore(invocationCaller, check); | 1777 insertBefore(invocationCaller, check); |
1832 } | 1778 } |
1833 } else { | 1779 } else { |
1834 cps.append(makeConcurrentModificationCheck( | 1780 cps.append(makeConcurrentModificationCheck( |
1835 list, originalLength, sourceInfo)); | 1781 list, originalLength, sourceInfo)); |
1836 } | 1782 } |
1837 } | 1783 } |
1838 | 1784 |
1839 // Check if there are more elements. | 1785 // Check if there are more elements. |
1840 Primitive hasMore = cps.applyBuiltin( | 1786 Primitive hasMore = cps.applyBuiltin(BuiltinOperator.NumLt, |
1841 BuiltinOperator.NumLt, | |
1842 [cps.getMutable(index), cps.letPrim(new GetLength(list))]); | 1787 [cps.getMutable(index), cps.letPrim(new GetLength(list))]); |
1843 | 1788 |
1844 // Return false if there are no more. | 1789 // Return false if there are no more. |
1845 CpsFragment falseBranch = cps.ifFalsy(hasMore); | 1790 CpsFragment falseBranch = cps.ifFalsy(hasMore); |
1846 falseBranch | 1791 falseBranch |
1847 ..setMutable(current, falseBranch.makeNull()) | 1792 ..setMutable(current, falseBranch.makeNull()) |
1848 ..invokeContinuation(moveNextCont, [falseBranch.makeFalse()]); | 1793 ..invokeContinuation(moveNextCont, [falseBranch.makeFalse()]); |
1849 | 1794 |
1850 // Return true if there are more element. | 1795 // Return true if there are more element. |
1851 current.type = typeSystem.elementTypeOfIndexable(listValue.type); | 1796 current.type = typeSystem.elementTypeOfIndexable(listValue.type); |
1852 cps.setMutable(current, | 1797 cps.setMutable(current, |
1853 cps.letPrim(new GetIndex(list, cps.getMutable(index)))); | 1798 cps.letPrim(new GetIndex(list, cps.getMutable(index)))); |
1854 cps.setMutable(index, cps.applyBuiltin( | 1799 cps.setMutable( |
1855 BuiltinOperator.NumAdd, | 1800 index, |
1856 [cps.getMutable(index), cps.makeOne()])); | 1801 cps.applyBuiltin(BuiltinOperator.NumAdd, |
| 1802 [cps.getMutable(index), cps.makeOne()])); |
1857 cps.invokeContinuation(moveNextCont, [cps.makeTrue()]); | 1803 cps.invokeContinuation(moveNextCont, [cps.makeTrue()]); |
1858 | 1804 |
1859 reanalyzeFragment(cps); | 1805 reanalyzeFragment(cps); |
1860 | 1806 |
1861 // Replace the moveNext() call. It will be visited later. | 1807 // Replace the moveNext() call. It will be visited later. |
1862 LetPrim let = use.parent; | 1808 LetPrim let = use.parent; |
1863 cps.context = moveNextCont; | 1809 cps.context = moveNextCont; |
1864 cps.insertBelow(let); | 1810 cps.insertBelow(let); |
1865 let.remove(); | 1811 let.remove(); |
1866 use..replaceUsesWith(result)..destroy(); | 1812 use |
| 1813 ..replaceUsesWith(result) |
| 1814 ..destroy(); |
1867 } | 1815 } |
1868 } | 1816 } |
1869 | 1817 |
1870 // All effective uses have been rewritten. | 1818 // All effective uses have been rewritten. |
1871 destroyRefinementsOfDeadPrimitive(iterator); | 1819 destroyRefinementsOfDeadPrimitive(iterator); |
1872 | 1820 |
1873 // Rewrite the iterator call to initializers for 'index' and 'current'. | 1821 // Rewrite the iterator call to initializers for 'index' and 'current'. |
1874 CpsFragment cps = new CpsFragment(); | 1822 CpsFragment cps = new CpsFragment(); |
1875 cps.letMutable(index, cps.makeZero()); | 1823 cps.letMutable(index, cps.makeZero()); |
1876 cps.letMutable(current, cps.makeNull()); | 1824 cps.letMutable(current, cps.makeNull()); |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1920 | 1868 |
1921 // If the selector does not apply, don't bother (will throw at runtime). | 1869 // If the selector does not apply, don't bother (will throw at runtime). |
1922 if (!call.signatureApplies(target)) return null; | 1870 if (!call.signatureApplies(target)) return null; |
1923 | 1871 |
1924 // If some optional arguments are missing, give up. | 1872 // If some optional arguments are missing, give up. |
1925 // TODO(asgerf): Improve optimization by inserting default arguments. | 1873 // TODO(asgerf): Improve optimization by inserting default arguments. |
1926 if (call.argumentCount != signature.parameterCount) return null; | 1874 if (call.argumentCount != signature.parameterCount) return null; |
1927 | 1875 |
1928 // Replace with InvokeStatic. | 1876 // Replace with InvokeStatic. |
1929 // The tear-off will be cleaned up by shrinking reductions. | 1877 // The tear-off will be cleaned up by shrinking reductions. |
1930 return new InvokeStatic(target, | 1878 return new InvokeStatic(target, new Selector.fromElement(target), |
1931 new Selector.fromElement(target), | 1879 node.arguments.toList(), node.sourceInformation); |
1932 node.arguments.toList(), | |
1933 node.sourceInformation); | |
1934 } | 1880 } |
1935 if (tearOff is InvokeMethod && tearOff.selector.isGetter) { | 1881 if (tearOff is InvokeMethod && tearOff.selector.isGetter) { |
1936 Selector getter = tearOff.selector; | 1882 Selector getter = tearOff.selector; |
1937 | 1883 |
1938 // TODO(asgerf): Support torn-off intercepted methods. | 1884 // TODO(asgerf): Support torn-off intercepted methods. |
1939 if (isInterceptedSelector(getter)) return null; | 1885 if (isInterceptedSelector(getter)) return null; |
1940 | 1886 |
1941 LetPrim tearOffBinding = tearOff.parent; | 1887 LetPrim tearOffBinding = tearOff.parent; |
1942 | 1888 |
1943 Primitive object = tearOff.receiver; | 1889 Primitive object = tearOff.receiver; |
(...skipping 21 matching lines...) Expand all Loading... |
1965 // therefore risk duplicating its side effects. | 1911 // therefore risk duplicating its side effects. |
1966 if (!isPure && tearOff.hasMultipleRefinedUses) return null; | 1912 if (!isPure && tearOff.hasMultipleRefinedUses) return null; |
1967 | 1913 |
1968 // If the getter call is impure, we risk reordering side effects, | 1914 // If the getter call is impure, we risk reordering side effects, |
1969 // unless it is immediately prior to the closure call. | 1915 // unless it is immediately prior to the closure call. |
1970 if (!isPure && getEffectiveParent(node.parent) != tearOffBinding) { | 1916 if (!isPure && getEffectiveParent(node.parent) != tearOffBinding) { |
1971 return null; | 1917 return null; |
1972 } | 1918 } |
1973 | 1919 |
1974 InvokeMethod invoke = new InvokeMethod( | 1920 InvokeMethod invoke = new InvokeMethod( |
1975 object, | 1921 object, |
1976 new Selector.call(getter.memberName, call.callStructure), | 1922 new Selector.call(getter.memberName, call.callStructure), |
1977 type, | 1923 type, |
1978 node.arguments.toList(), | 1924 node.arguments.toList(), |
1979 sourceInformation: node.sourceInformation); | 1925 sourceInformation: node.sourceInformation); |
1980 node.receiverRef.changeTo(new Parameter(null)); // Remove the tear off use
. | 1926 node.receiverRef |
| 1927 .changeTo(new Parameter(null)); // Remove the tear off use. |
1981 | 1928 |
1982 if (tearOff.hasNoRefinedUses) { | 1929 if (tearOff.hasNoRefinedUses) { |
1983 // Eliminate the getter call if it has no more uses. | 1930 // Eliminate the getter call if it has no more uses. |
1984 // This cannot be delegated to other optimizations because we need to | 1931 // This cannot be delegated to other optimizations because we need to |
1985 // avoid duplication of side effects. | 1932 // avoid duplication of side effects. |
1986 destroyRefinementsOfDeadPrimitive(tearOff); | 1933 destroyRefinementsOfDeadPrimitive(tearOff); |
1987 tearOff.destroy(); | 1934 tearOff.destroy(); |
1988 tearOffBinding.remove(); | 1935 tearOffBinding.remove(); |
1989 } else { | 1936 } else { |
1990 // There are more uses, so we cannot eliminate the getter call. This | 1937 // There are more uses, so we cannot eliminate the getter call. This |
(...skipping 10 matching lines...) Expand all Loading... |
2001 /// Inlines a single-use closure if it leaves the closure object with only | 1948 /// Inlines a single-use closure if it leaves the closure object with only |
2002 /// field accesses. This is optimized later by [ScalarReplacer]. | 1949 /// field accesses. This is optimized later by [ScalarReplacer]. |
2003 CpsFragment specializeSingleUseClosureCall(InvokeMethod node) { | 1950 CpsFragment specializeSingleUseClosureCall(InvokeMethod node) { |
2004 Selector call = node.selector; | 1951 Selector call = node.selector; |
2005 if (!call.isClosureCall) return null; | 1952 if (!call.isClosureCall) return null; |
2006 | 1953 |
2007 assert(!isInterceptedSelector(call)); | 1954 assert(!isInterceptedSelector(call)); |
2008 assert(call.argumentCount == node.argumentRefs.length); | 1955 assert(call.argumentCount == node.argumentRefs.length); |
2009 | 1956 |
2010 Primitive receiver = node.receiver; | 1957 Primitive receiver = node.receiver; |
2011 if (receiver is !CreateInstance) return null; | 1958 if (receiver is! CreateInstance) return null; |
2012 CreateInstance createInstance = receiver; | 1959 CreateInstance createInstance = receiver; |
2013 if (!createInstance.hasExactlyOneUse) return null; | 1960 if (!createInstance.hasExactlyOneUse) return null; |
2014 | 1961 |
2015 // Inline only closures. This avoids inlining the 'call' method of a class | 1962 // Inline only closures. This avoids inlining the 'call' method of a class |
2016 // that has many allocation sites. | 1963 // that has many allocation sites. |
2017 if (createInstance.classElement is !ClosureClassElement) return null; | 1964 if (createInstance.classElement is! ClosureClassElement) return null; |
2018 | 1965 |
2019 ClosureClassElement closureClassElement = createInstance.classElement; | 1966 ClosureClassElement closureClassElement = createInstance.classElement; |
2020 Element element = closureClassElement.localLookup(Identifiers.call); | 1967 Element element = closureClassElement.localLookup(Identifiers.call); |
2021 | 1968 |
2022 if (element == null || !element.isFunction) return null; | 1969 if (element == null || !element.isFunction) return null; |
2023 FunctionElement functionElement = element; | 1970 FunctionElement functionElement = element; |
2024 if (functionElement.asyncMarker != AsyncMarker.SYNC) return null; | 1971 if (functionElement.asyncMarker != AsyncMarker.SYNC) return null; |
2025 | 1972 |
2026 if (!call.signatureApplies(functionElement)) return null; | 1973 if (!call.signatureApplies(functionElement)) return null; |
2027 // Inline only for exact match. | 1974 // Inline only for exact match. |
2028 // TODO(sra): Handle call with defaulted arguments. | 1975 // TODO(sra): Handle call with defaulted arguments. |
2029 Selector targetSelector = new Selector.fromElement(functionElement); | 1976 Selector targetSelector = new Selector.fromElement(functionElement); |
2030 if (call.callStructure != targetSelector.callStructure) return null; | 1977 if (call.callStructure != targetSelector.callStructure) return null; |
2031 | 1978 |
2032 // Don't inline if [target] contains try-catch or try-finally. JavaScript | 1979 // Don't inline if [target] contains try-catch or try-finally. JavaScript |
2033 // engines typically do poor optimization of the entire function containing | 1980 // engines typically do poor optimization of the entire function containing |
2034 // the 'try'. | 1981 // the 'try'. |
2035 if (functionElement.resolvedAst.elements.containsTryStatement) return null; | 1982 if (functionElement.resolvedAst.elements.containsTryStatement) return null; |
2036 | 1983 |
2037 FunctionDefinition target = | 1984 FunctionDefinition target = |
2038 functionCompiler.compileToCpsIr(functionElement); | 1985 functionCompiler.compileToCpsIr(functionElement); |
2039 | 1986 |
2040 // Accesses to closed-over values are field access primitives. We we don't | 1987 // Accesses to closed-over values are field access primitives. We we don't |
2041 // inline if there are other uses of 'this' since that could be an escape or | 1988 // inline if there are other uses of 'this' since that could be an escape or |
2042 // a recursive call. | 1989 // a recursive call. |
2043 for (Reference ref = target.receiverParameter.firstRef; | 1990 for (Reference ref = target.receiverParameter.firstRef; |
2044 ref != null; | 1991 ref != null; |
2045 ref = ref.next) { | 1992 ref = ref.next) { |
2046 Node use = ref.parent; | 1993 Node use = ref.parent; |
2047 if (use is GetField) continue; | 1994 if (use is GetField) continue; |
2048 // Closures do not currently have writable fields, but closure conversion | 1995 // Closures do not currently have writable fields, but closure conversion |
2049 // could esily be changed to allocate some cells in a closure object. | 1996 // could esily be changed to allocate some cells in a closure object. |
2050 if (use is SetField && ref == use.objectRef) continue; | 1997 if (use is SetField && ref == use.objectRef) continue; |
2051 return null; | 1998 return null; |
2052 } | 1999 } |
2053 | 2000 |
2054 CpsFragment cps = new CpsFragment(node.sourceInformation); | 2001 CpsFragment cps = new CpsFragment(node.sourceInformation); |
2055 Primitive returnValue = cps.inlineFunction(target, | 2002 Primitive returnValue = cps.inlineFunction( |
2056 node.receiver, | 2003 target, node.receiver, node.arguments.toList(), |
2057 node.arguments.toList(), | |
2058 hint: node.hint); | 2004 hint: node.hint); |
2059 node.replaceUsesWith(returnValue); | 2005 node.replaceUsesWith(returnValue); |
2060 return cps; | 2006 return cps; |
2061 } | 2007 } |
2062 | 2008 |
2063 visitInterceptor(Interceptor node) { | 2009 visitInterceptor(Interceptor node) { |
2064 // Replace the interceptor with its input if the value is not intercepted. | 2010 // Replace the interceptor with its input if the value is not intercepted. |
2065 // If the input might be null, we cannot do this since the interceptor | 2011 // If the input might be null, we cannot do this since the interceptor |
2066 // might have to return JSNull. That case is handled by visitInvokeMethod | 2012 // might have to return JSNull. That case is handled by visitInvokeMethod |
2067 // and visitInvokeMethodDirectly which can sometimes tolerate that null | 2013 // and visitInvokeMethodDirectly which can sometimes tolerate that null |
2068 // is used instead of JSNull. | 2014 // is used instead of JSNull. |
2069 Primitive input = node.input; | 2015 Primitive input = node.input; |
2070 if (!input.type.isNullable && | 2016 if (!input.type.isNullable && |
2071 typeSystem.areDisjoint(input.type, typeSystem.interceptorType)) { | 2017 typeSystem.areDisjoint(input.type, typeSystem.interceptorType)) { |
2072 node.replaceUsesWith(input); | 2018 node.replaceUsesWith(input); |
2073 } | 2019 } |
2074 } | 2020 } |
2075 | 2021 |
2076 visitInvokeConstructor(InvokeConstructor node) { | 2022 visitInvokeConstructor(InvokeConstructor node) { |
2077 node.effects = | 2023 node.effects = |
2078 Effects.from(compiler.world.getSideEffectsOfElement(node.target)); | 2024 Effects.from(compiler.world.getSideEffectsOfElement(node.target)); |
2079 } | 2025 } |
2080 | 2026 |
2081 visitInvokeMethodDirectly(InvokeMethodDirectly node) { | 2027 visitInvokeMethodDirectly(InvokeMethodDirectly node) { |
2082 Element target = node.target; | 2028 Element target = node.target; |
2083 if (target is ConstructorBodyElement) { | 2029 if (target is ConstructorBodyElement) { |
2084 ConstructorBodyElement constructorBody = target; | 2030 ConstructorBodyElement constructorBody = target; |
2085 target = constructorBody.constructor; | 2031 target = constructorBody.constructor; |
2086 } | 2032 } |
2087 node.effects = | 2033 node.effects = Effects.from(compiler.world.getSideEffectsOfElement(target)); |
2088 Effects.from(compiler.world.getSideEffectsOfElement(target)); | |
2089 TypeMask receiverType = node.receiver.type; | 2034 TypeMask receiverType = node.receiver.type; |
2090 if (node.callingConvention == CallingConvention.Intercepted && | 2035 if (node.callingConvention == CallingConvention.Intercepted && |
2091 typeSystem.areDisjoint(receiverType, typeSystem.interceptorType)) { | 2036 typeSystem.areDisjoint(receiverType, typeSystem.interceptorType)) { |
2092 // Some direct calls take an interceptor because the target class is | 2037 // Some direct calls take an interceptor because the target class is |
2093 // mixed into a native class. If it is known at the call site that the | 2038 // mixed into a native class. If it is known at the call site that the |
2094 // receiver is non-intercepted, get rid of the interceptor. | 2039 // receiver is non-intercepted, get rid of the interceptor. |
2095 node.interceptorRef.changeTo(node.receiver); | 2040 node.interceptorRef.changeTo(node.receiver); |
2096 } | 2041 } |
2097 } | 2042 } |
2098 | 2043 |
2099 visitInvokeMethod(InvokeMethod node) { | 2044 visitInvokeMethod(InvokeMethod node) { |
2100 var specialized = | 2045 var specialized = specializeOperatorCall(node) ?? |
2101 specializeOperatorCall(node) ?? | |
2102 specializeFieldAccess(node) ?? | 2046 specializeFieldAccess(node) ?? |
2103 specializeIndexableAccess(node) ?? | 2047 specializeIndexableAccess(node) ?? |
2104 specializeArrayAccess(node) ?? | 2048 specializeArrayAccess(node) ?? |
2105 specializeSingleUseClosureCall(node) ?? | 2049 specializeSingleUseClosureCall(node) ?? |
2106 specializeClosureCall(node); | 2050 specializeClosureCall(node); |
2107 if (specialized != null) return specialized; | 2051 if (specialized != null) return specialized; |
2108 | 2052 |
2109 TypeMask receiverType = node.receiver.type; | 2053 TypeMask receiverType = node.receiver.type; |
2110 node.mask = typeSystem.intersection(node.mask, receiverType); | 2054 node.mask = typeSystem.intersection(node.mask, receiverType); |
2111 | 2055 |
2112 node.effects = Effects.from( | 2056 node.effects = Effects.from( |
2113 compiler.world.getSideEffectsOfSelector(node.selector, node.mask)); | 2057 compiler.world.getSideEffectsOfSelector(node.selector, node.mask)); |
2114 | 2058 |
2115 bool canBeNonThrowingCallOnNull = | 2059 bool canBeNonThrowingCallOnNull = |
2116 selectorsOnNull.contains(node.selector) && | 2060 selectorsOnNull.contains(node.selector) && receiverType.isNullable; |
2117 receiverType.isNullable; | |
2118 | 2061 |
2119 if (node.callingConvention == CallingConvention.Intercepted && | 2062 if (node.callingConvention == CallingConvention.Intercepted && |
2120 !canBeNonThrowingCallOnNull && | 2063 !canBeNonThrowingCallOnNull && |
2121 typeSystem.areDisjoint(receiverType, typeSystem.interceptorType)) { | 2064 typeSystem.areDisjoint(receiverType, typeSystem.interceptorType)) { |
2122 // Use the Dart receiver as the JS receiver. This changes the wording of | 2065 // Use the Dart receiver as the JS receiver. This changes the wording of |
2123 // the error message when the receiver is null, but we accept this. | 2066 // the error message when the receiver is null, but we accept this. |
2124 node.interceptorRef.changeTo(node.receiver); | 2067 node.interceptorRef.changeTo(node.receiver); |
2125 | 2068 |
2126 // Replace the extra receiver argument with a dummy value if the | 2069 // Replace the extra receiver argument with a dummy value if the |
2127 // target definitely does not use it. | 2070 // target definitely does not use it. |
2128 if (typeSystem.targetIgnoresReceiverArgument(receiverType, | 2071 if (typeSystem.targetIgnoresReceiverArgument( |
2129 node.selector)) { | 2072 receiverType, node.selector)) { |
2130 node.makeDummyIntercepted(); | 2073 node.makeDummyIntercepted(); |
2131 } | 2074 } |
2132 } | 2075 } |
2133 } | 2076 } |
2134 | 2077 |
2135 CpsFragment visitTypeCast(TypeCast node) { | 2078 CpsFragment visitTypeCast(TypeCast node) { |
2136 AbstractConstantValue value = getValue(node.value); | 2079 AbstractConstantValue value = getValue(node.value); |
2137 switch (lattice.isSubtypeOf(value, node.dartType, allowNull: true)) { | 2080 switch (lattice.isSubtypeOf(value, node.dartType, allowNull: true)) { |
2138 case AbstractBool.Maybe: | 2081 case AbstractBool.Maybe: |
2139 case AbstractBool.Nothing: | 2082 case AbstractBool.Nothing: |
(...skipping 13 matching lines...) Expand all Loading... |
2153 | 2096 |
2154 /// Specialize calls to internal static methods. | 2097 /// Specialize calls to internal static methods. |
2155 specializeInternalMethodCall(InvokeStatic node) { | 2098 specializeInternalMethodCall(InvokeStatic node) { |
2156 if (node.target == backend.helpers.stringInterpolationHelper) { | 2099 if (node.target == backend.helpers.stringInterpolationHelper) { |
2157 Primitive argument = node.argument(0); | 2100 Primitive argument = node.argument(0); |
2158 AbstractConstantValue value = getValue(argument); | 2101 AbstractConstantValue value = getValue(argument); |
2159 if (lattice.isDefinitelyString(value)) { | 2102 if (lattice.isDefinitelyString(value)) { |
2160 node.replaceUsesWith(argument); | 2103 node.replaceUsesWith(argument); |
2161 return new CpsFragment(); | 2104 return new CpsFragment(); |
2162 } else if (typeSystem.isDefinitelySelfInterceptor(value.type)) { | 2105 } else if (typeSystem.isDefinitelySelfInterceptor(value.type)) { |
2163 TypeMask toStringReturn = typeSystem.getInvokeReturnType( | 2106 TypeMask toStringReturn = |
2164 Selectors.toString_, value.type); | 2107 typeSystem.getInvokeReturnType(Selectors.toString_, value.type); |
2165 if (typeSystem.isDefinitelyString(toStringReturn)) { | 2108 if (typeSystem.isDefinitelyString(toStringReturn)) { |
2166 CpsFragment cps = new CpsFragment(node.sourceInformation); | 2109 CpsFragment cps = new CpsFragment(node.sourceInformation); |
2167 Primitive invoke = cps.invokeMethod( | 2110 Primitive invoke = cps.invokeMethod( |
2168 argument, | 2111 argument, Selectors.toString_, value.type, [], |
2169 Selectors.toString_, | |
2170 value.type, | |
2171 [], | |
2172 callingConvention: CallingConvention.DummyIntercepted); | 2112 callingConvention: CallingConvention.DummyIntercepted); |
2173 node.replaceUsesWith(invoke); | 2113 node.replaceUsesWith(invoke); |
2174 return cps; | 2114 return cps; |
2175 } | 2115 } |
2176 } | 2116 } |
2177 } else if (node.target == compiler.identicalFunction) { | 2117 } else if (node.target == compiler.identicalFunction) { |
2178 if (node.argumentRefs.length == 2) { | 2118 if (node.argumentRefs.length == 2) { |
2179 return new ApplyBuiltinOperator(BuiltinOperator.Identical, | 2119 return new ApplyBuiltinOperator(BuiltinOperator.Identical, |
2180 [node.argument(0), node.argument(1)], | 2120 [node.argument(0), node.argument(1)], node.sourceInformation); |
2181 node.sourceInformation); | |
2182 } | 2121 } |
2183 } | 2122 } |
2184 return null; | 2123 return null; |
2185 } | 2124 } |
2186 | 2125 |
2187 visitInvokeStatic(InvokeStatic node) { | 2126 visitInvokeStatic(InvokeStatic node) { |
2188 node.effects = Effects.from( | 2127 node.effects = |
2189 compiler.world.getSideEffectsOfElement(node.target)); | 2128 Effects.from(compiler.world.getSideEffectsOfElement(node.target)); |
2190 return specializeInternalMethodCall(node); | 2129 return specializeInternalMethodCall(node); |
2191 } | 2130 } |
2192 | 2131 |
2193 AbstractConstantValue getValue(Variable node) { | 2132 AbstractConstantValue getValue(Variable node) { |
2194 assert(node.type != null); | 2133 assert(node.type != null); |
2195 ConstantValue constant = values[node]; | 2134 ConstantValue constant = values[node]; |
2196 if (constant != null) { | 2135 if (constant != null) { |
2197 return new AbstractConstantValue.constantValue(constant, node.type); | 2136 return new AbstractConstantValue.constantValue(constant, node.type); |
2198 } | 2137 } |
2199 return new AbstractConstantValue.nonConstant(node.type); | 2138 return new AbstractConstantValue.nonConstant(node.type); |
2200 } | 2139 } |
2201 | 2140 |
2202 | |
2203 /*************************** PRIMITIVES **************************/ | 2141 /*************************** PRIMITIVES **************************/ |
2204 // | 2142 // |
2205 // The visit method for a primitive may return one of the following: | 2143 // The visit method for a primitive may return one of the following: |
2206 // - Primitive: | 2144 // - Primitive: |
2207 // The visited primitive will be replaced by the returned primitive. | 2145 // The visited primitive will be replaced by the returned primitive. |
2208 // The type of the primitive will be recomputed. | 2146 // The type of the primitive will be recomputed. |
2209 // - CpsFragment: | 2147 // - CpsFragment: |
2210 // The primitive binding will be destroyed and replaced by the given | 2148 // The primitive binding will be destroyed and replaced by the given |
2211 // code fragment. All types in the fragment will be recomputed. | 2149 // code fragment. All types in the fragment will be recomputed. |
2212 // - Null: | 2150 // - Null: |
(...skipping 10 matching lines...) Expand all Loading... |
2223 // Concatenate consecutive constants. | 2161 // Concatenate consecutive constants. |
2224 bool argumentsWereRemoved = false; | 2162 bool argumentsWereRemoved = false; |
2225 int i = 0; | 2163 int i = 0; |
2226 while (i < node.argumentRefs.length - 1) { | 2164 while (i < node.argumentRefs.length - 1) { |
2227 int startOfSequence = i; | 2165 int startOfSequence = i; |
2228 AbstractConstantValue firstValue = getValue(node.argument(i++)); | 2166 AbstractConstantValue firstValue = getValue(node.argument(i++)); |
2229 if (!firstValue.isConstant) continue; | 2167 if (!firstValue.isConstant) continue; |
2230 AbstractConstantValue secondValue = getValue(node.argument(i++)); | 2168 AbstractConstantValue secondValue = getValue(node.argument(i++)); |
2231 if (!secondValue.isConstant) continue; | 2169 if (!secondValue.isConstant) continue; |
2232 | 2170 |
2233 ast.DartString string = | 2171 ast.DartString string = new ast.ConsDartString( |
2234 new ast.ConsDartString(getString(firstValue), | 2172 getString(firstValue), getString(secondValue)); |
2235 getString(secondValue)); | |
2236 | 2173 |
2237 // We found a sequence of at least two constants. | 2174 // We found a sequence of at least two constants. |
2238 // Look for the end of the sequence. | 2175 // Look for the end of the sequence. |
2239 while (i < node.argumentRefs.length) { | 2176 while (i < node.argumentRefs.length) { |
2240 AbstractConstantValue value = getValue(node.argument(i)); | 2177 AbstractConstantValue value = getValue(node.argument(i)); |
2241 if (!value.isConstant) break; | 2178 if (!value.isConstant) break; |
2242 string = new ast.ConsDartString(string, getString(value)); | 2179 string = new ast.ConsDartString(string, getString(value)); |
2243 ++i; | 2180 ++i; |
2244 } | 2181 } |
2245 Constant prim = | 2182 Constant prim = |
(...skipping 27 matching lines...) Expand all Loading... |
2273 BuiltinOperator newOperator; | 2210 BuiltinOperator newOperator; |
2274 if (left.isNullConstant || right.isNullConstant) { | 2211 if (left.isNullConstant || right.isNullConstant) { |
2275 // Use `==` for comparing against null, so JS undefined and JS null | 2212 // Use `==` for comparing against null, so JS undefined and JS null |
2276 // are considered equal. | 2213 // are considered equal. |
2277 newOperator = BuiltinOperator.LooseEq; | 2214 newOperator = BuiltinOperator.LooseEq; |
2278 } else if (!left.isNullable || !right.isNullable) { | 2215 } else if (!left.isNullable || !right.isNullable) { |
2279 // If at most one operand can be Dart null, we can use `===`. | 2216 // If at most one operand can be Dart null, we can use `===`. |
2280 // This is not safe when we might compare JS null and JS undefined. | 2217 // This is not safe when we might compare JS null and JS undefined. |
2281 newOperator = BuiltinOperator.StrictEq; | 2218 newOperator = BuiltinOperator.StrictEq; |
2282 } else if (lattice.isDefinitelyNum(left, allowNull: true) && | 2219 } else if (lattice.isDefinitelyNum(left, allowNull: true) && |
2283 lattice.isDefinitelyNum(right, allowNull: true)) { | 2220 lattice.isDefinitelyNum(right, allowNull: true)) { |
2284 // If both operands can be null, but otherwise are of the same type, | 2221 // If both operands can be null, but otherwise are of the same type, |
2285 // we can use `==` for comparison. | 2222 // we can use `==` for comparison. |
2286 // This is not safe e.g. for comparing strings against numbers. | 2223 // This is not safe e.g. for comparing strings against numbers. |
2287 newOperator = BuiltinOperator.LooseEq; | 2224 newOperator = BuiltinOperator.LooseEq; |
2288 } else if (lattice.isDefinitelyString(left, allowNull: true) && | 2225 } else if (lattice.isDefinitelyString(left, allowNull: true) && |
2289 lattice.isDefinitelyString(right, allowNull: true)) { | 2226 lattice.isDefinitelyString(right, allowNull: true)) { |
2290 newOperator = BuiltinOperator.LooseEq; | 2227 newOperator = BuiltinOperator.LooseEq; |
2291 } else if (lattice.isDefinitelyBool(left, allowNull: true) && | 2228 } else if (lattice.isDefinitelyBool(left, allowNull: true) && |
2292 lattice.isDefinitelyBool(right, allowNull: true)) { | 2229 lattice.isDefinitelyBool(right, allowNull: true)) { |
2293 newOperator = BuiltinOperator.LooseEq; | 2230 newOperator = BuiltinOperator.LooseEq; |
2294 } | 2231 } |
2295 if (newOperator != null) { | 2232 if (newOperator != null) { |
2296 return new ApplyBuiltinOperator(newOperator, | 2233 return new ApplyBuiltinOperator( |
2297 node.arguments.toList(), | 2234 newOperator, node.arguments.toList(), node.sourceInformation); |
2298 node.sourceInformation); | |
2299 } | 2235 } |
2300 break; | 2236 break; |
2301 | 2237 |
2302 case BuiltinOperator.StrictEq: | 2238 case BuiltinOperator.StrictEq: |
2303 case BuiltinOperator.LooseEq: | 2239 case BuiltinOperator.LooseEq: |
2304 case BuiltinOperator.StrictNeq: | 2240 case BuiltinOperator.StrictNeq: |
2305 case BuiltinOperator.LooseNeq: | 2241 case BuiltinOperator.LooseNeq: |
2306 bool negated = | 2242 bool negated = node.operator == BuiltinOperator.StrictNeq || |
2307 node.operator == BuiltinOperator.StrictNeq || | |
2308 node.operator == BuiltinOperator.LooseNeq; | 2243 node.operator == BuiltinOperator.LooseNeq; |
2309 for (int firstIndex in [0, 1]) { | 2244 for (int firstIndex in [0, 1]) { |
2310 int secondIndex = 1 - firstIndex; | 2245 int secondIndex = 1 - firstIndex; |
2311 Primitive firstArg = node.argument(firstIndex); | 2246 Primitive firstArg = node.argument(firstIndex); |
2312 Primitive secondArg = node.argument(secondIndex); | 2247 Primitive secondArg = node.argument(secondIndex); |
2313 AbstractConstantValue first = getValue(firstArg); | 2248 AbstractConstantValue first = getValue(firstArg); |
2314 if (!lattice.isDefinitelyBool(first)) continue; | 2249 if (!lattice.isDefinitelyBool(first)) continue; |
2315 AbstractConstantValue second = getValue(secondArg); | 2250 AbstractConstantValue second = getValue(secondArg); |
2316 if (!second.isConstant || !second.constant.isBool) continue; | 2251 if (!second.isConstant || !second.constant.isBool) continue; |
2317 bool isTrueConstant = second.constant.isTrue; | 2252 bool isTrueConstant = second.constant.isTrue; |
2318 if (isTrueConstant == !negated) { | 2253 if (isTrueConstant == !negated) { |
2319 // (x === true) ==> x | 2254 // (x === true) ==> x |
2320 // (x !== false) ==> x | 2255 // (x !== false) ==> x |
2321 node.replaceUsesWith(firstArg); | 2256 node.replaceUsesWith(firstArg); |
2322 return null; | 2257 return null; |
2323 } else { | 2258 } else { |
2324 // (x === false) ==> !x | 2259 // (x === false) ==> !x |
2325 // (x !== true) ==> !x | 2260 // (x !== true) ==> !x |
2326 return new ApplyBuiltinOperator( | 2261 return new ApplyBuiltinOperator( |
2327 BuiltinOperator.IsFalsy, | 2262 BuiltinOperator.IsFalsy, [firstArg], node.sourceInformation); |
2328 [firstArg], | |
2329 node.sourceInformation); | |
2330 } | 2263 } |
2331 } | 2264 } |
2332 break; | 2265 break; |
2333 | 2266 |
2334 default: | 2267 default: |
2335 } | 2268 } |
2336 return null; | 2269 return null; |
2337 } | 2270 } |
2338 | 2271 |
2339 void visitApplyBuiltinMethod(ApplyBuiltinMethod node) { | 2272 void visitApplyBuiltinMethod(ApplyBuiltinMethod node) {} |
2340 } | |
2341 | 2273 |
2342 visitTypeTest(TypeTest node) { | 2274 visitTypeTest(TypeTest node) { |
2343 Primitive prim = node.value; | 2275 Primitive prim = node.value; |
2344 | 2276 |
2345 Primitive unaryBuiltinOperator(BuiltinOperator operator) => | 2277 Primitive unaryBuiltinOperator(BuiltinOperator operator) => |
2346 new ApplyBuiltinOperator( | 2278 new ApplyBuiltinOperator( |
2347 operator, <Primitive>[prim], node.sourceInformation); | 2279 operator, <Primitive>[prim], node.sourceInformation); |
2348 | 2280 |
2349 AbstractConstantValue value = getValue(prim); | 2281 AbstractConstantValue value = getValue(prim); |
2350 types.DartType dartType = node.dartType; | 2282 types.DartType dartType = node.dartType; |
2351 | 2283 |
2352 if (!(dartType.isInterfaceType && dartType.isRaw)) { | 2284 if (!(dartType.isInterfaceType && dartType.isRaw)) { |
2353 // TODO(23685): Efficient function arity check. | 2285 // TODO(23685): Efficient function arity check. |
2354 // TODO(sra): Pass interceptor to runtime subtype functions. | 2286 // TODO(sra): Pass interceptor to runtime subtype functions. |
2355 return null; | 2287 return null; |
2356 } | 2288 } |
2357 | 2289 |
2358 if (dartType == dartTypes.coreTypes.intType) { | 2290 if (dartType == dartTypes.coreTypes.intType) { |
2359 // Compile as typeof x === 'number' && Math.floor(x) === x | 2291 // Compile as typeof x === 'number' && Math.floor(x) === x |
2360 if (lattice.isDefinitelyNum(value, allowNull: true)) { | 2292 if (lattice.isDefinitelyNum(value, allowNull: true)) { |
2361 // If value is null or a number, we can skip the typeof test. | 2293 // If value is null or a number, we can skip the typeof test. |
2362 return new ApplyBuiltinOperator( | 2294 return new ApplyBuiltinOperator(BuiltinOperator.IsFloor, |
2363 BuiltinOperator.IsFloor, | 2295 <Primitive>[prim, prim], node.sourceInformation); |
2364 <Primitive>[prim, prim], | |
2365 node.sourceInformation); | |
2366 } | 2296 } |
2367 if (lattice.isDefinitelyNotNonIntegerDouble(value)) { | 2297 if (lattice.isDefinitelyNotNonIntegerDouble(value)) { |
2368 // If the value cannot be a non-integer double, but might not be a | 2298 // If the value cannot be a non-integer double, but might not be a |
2369 // number at all, we can skip the Math.floor test. | 2299 // number at all, we can skip the Math.floor test. |
2370 return unaryBuiltinOperator(BuiltinOperator.IsNumber); | 2300 return unaryBuiltinOperator(BuiltinOperator.IsNumber); |
2371 } | 2301 } |
2372 return new ApplyBuiltinOperator( | 2302 return new ApplyBuiltinOperator(BuiltinOperator.IsInteger, |
2373 BuiltinOperator.IsInteger, | 2303 <Primitive>[prim, prim, prim], node.sourceInformation); |
2374 <Primitive>[prim, prim, prim], | |
2375 node.sourceInformation); | |
2376 } | 2304 } |
2377 if (node.dartType == dartTypes.coreTypes.numType || | 2305 if (node.dartType == dartTypes.coreTypes.numType || |
2378 node.dartType == dartTypes.coreTypes.doubleType) { | 2306 node.dartType == dartTypes.coreTypes.doubleType) { |
2379 return new ApplyBuiltinOperator( | 2307 return new ApplyBuiltinOperator( |
2380 BuiltinOperator.IsNumber, | 2308 BuiltinOperator.IsNumber, <Primitive>[prim], node.sourceInformation); |
2381 <Primitive>[prim], | |
2382 node.sourceInformation); | |
2383 } | 2309 } |
2384 | 2310 |
2385 AbstractBool isNullableSubtype = | 2311 AbstractBool isNullableSubtype = |
2386 lattice.isSubtypeOf(value, node.dartType, allowNull: true); | 2312 lattice.isSubtypeOf(value, node.dartType, allowNull: true); |
2387 AbstractBool isNullPassingTest = | 2313 AbstractBool isNullPassingTest = |
2388 lattice.isSubtypeOf(lattice.nullValue, node.dartType, allowNull: false); | 2314 lattice.isSubtypeOf(lattice.nullValue, node.dartType, allowNull: false); |
2389 if (isNullableSubtype == AbstractBool.True && | 2315 if (isNullableSubtype == AbstractBool.True && |
2390 isNullPassingTest == AbstractBool.False) { | 2316 isNullPassingTest == AbstractBool.False) { |
2391 // Null is the only value not satisfying the type test. | 2317 // Null is the only value not satisfying the type test. |
2392 // Replace the type test with a null-check. | 2318 // Replace the type test with a null-check. |
2393 // This has lower priority than the 'typeof'-based tests because | 2319 // This has lower priority than the 'typeof'-based tests because |
2394 // 'typeof' expressions might give the VM some more useful information. | 2320 // 'typeof' expressions might give the VM some more useful information. |
2395 Primitive nullConst = makeConstantPrimitive(new NullConstantValue()); | 2321 Primitive nullConst = makeConstantPrimitive(new NullConstantValue()); |
2396 new LetPrim(nullConst).insertAbove(node.parent); | 2322 new LetPrim(nullConst).insertAbove(node.parent); |
2397 return new ApplyBuiltinOperator( | 2323 return new ApplyBuiltinOperator(BuiltinOperator.LooseNeq, |
2398 BuiltinOperator.LooseNeq, | 2324 <Primitive>[prim, nullConst], node.sourceInformation); |
2399 <Primitive>[prim, nullConst], | |
2400 node.sourceInformation); | |
2401 } | 2325 } |
2402 | 2326 |
2403 if (dartType.element == functionCompiler.glue.jsFixedArrayClass) { | 2327 if (dartType.element == functionCompiler.glue.jsFixedArrayClass) { |
2404 // TODO(sra): Check input is restricted to JSArray. | 2328 // TODO(sra): Check input is restricted to JSArray. |
2405 return unaryBuiltinOperator(BuiltinOperator.IsFixedLengthJSArray); | 2329 return unaryBuiltinOperator(BuiltinOperator.IsFixedLengthJSArray); |
2406 } | 2330 } |
2407 | 2331 |
2408 if (dartType.element == functionCompiler.glue.jsExtendableArrayClass) { | 2332 if (dartType.element == functionCompiler.glue.jsExtendableArrayClass) { |
2409 // TODO(sra): Check input is restricted to JSArray. | 2333 // TODO(sra): Check input is restricted to JSArray. |
2410 return unaryBuiltinOperator(BuiltinOperator.IsExtendableJSArray); | 2334 return unaryBuiltinOperator(BuiltinOperator.IsExtendableJSArray); |
(...skipping 30 matching lines...) Expand all Loading... |
2441 return null; | 2365 return null; |
2442 } | 2366 } |
2443 | 2367 |
2444 visitBoundsCheck(BoundsCheck node) { | 2368 visitBoundsCheck(BoundsCheck node) { |
2445 // Eliminate bounds checks using constant folding. | 2369 // Eliminate bounds checks using constant folding. |
2446 // The [BoundsChecker] pass does not try to eliminate checks that could be | 2370 // The [BoundsChecker] pass does not try to eliminate checks that could be |
2447 // eliminated by constant folding. | 2371 // eliminated by constant folding. |
2448 if (node.hasNoChecks) return; | 2372 if (node.hasNoChecks) return; |
2449 Primitive indexPrim = node.index; | 2373 Primitive indexPrim = node.index; |
2450 int index = lattice.intValue(getValue(indexPrim)); | 2374 int index = lattice.intValue(getValue(indexPrim)); |
2451 int length = node.lengthRef == null | 2375 int length = |
2452 ? null | 2376 node.lengthRef == null ? null : lattice.intValue(getValue(node.length)); |
2453 : lattice.intValue(getValue(node.length)); | |
2454 if (index != null && length != null && index < length) { | 2377 if (index != null && length != null && index < length) { |
2455 node.checks &= ~BoundsCheck.UPPER_BOUND; | 2378 node.checks &= ~BoundsCheck.UPPER_BOUND; |
2456 } | 2379 } |
2457 if (index != null && index >= 0) { | 2380 if (index != null && index >= 0) { |
2458 node.checks &= ~BoundsCheck.LOWER_BOUND; | 2381 node.checks &= ~BoundsCheck.LOWER_BOUND; |
2459 } | 2382 } |
2460 if (length != null && length > 0) { | 2383 if (length != null && length > 0) { |
2461 node.checks &= ~BoundsCheck.EMPTINESS; | 2384 node.checks &= ~BoundsCheck.EMPTINESS; |
2462 } | 2385 } |
2463 if (typeSystem.isDefinitelyInt(indexPrim.type)) { | 2386 if (typeSystem.isDefinitelyInt(indexPrim.type)) { |
2464 node.checks &= ~BoundsCheck.INTEGER; | 2387 node.checks &= ~BoundsCheck.INTEGER; |
2465 } | 2388 } |
2466 if (!node.lengthUsedInCheck && node.lengthRef != null) { | 2389 if (!node.lengthUsedInCheck && node.lengthRef != null) { |
2467 node..lengthRef.unlink()..lengthRef = null; | 2390 node |
| 2391 ..lengthRef.unlink() |
| 2392 ..lengthRef = null; |
2468 } | 2393 } |
2469 if (node.checks == BoundsCheck.NONE) { | 2394 if (node.checks == BoundsCheck.NONE) { |
2470 // We can't remove the bounds check node because it may still be used to | 2395 // We can't remove the bounds check node because it may still be used to |
2471 // restrict code motion. But the index is no longer needed. | 2396 // restrict code motion. But the index is no longer needed. |
2472 // TODO(asgerf): Since this was eliminated by constant folding, it should | 2397 // TODO(asgerf): Since this was eliminated by constant folding, it should |
2473 // be safe to remove because any path-sensitive information we relied | 2398 // be safe to remove because any path-sensitive information we relied |
2474 // upon to do this are expressed by other refinement nodes that also | 2399 // upon to do this are expressed by other refinement nodes that also |
2475 // restrict code motion. However, if we want to run this pass after | 2400 // restrict code motion. However, if we want to run this pass after |
2476 // [BoundsChecker] that would not be safe any more, so for now we | 2401 // [BoundsChecker] that would not be safe any more, so for now we |
2477 // keep the node for forward compatibilty. | 2402 // keep the node for forward compatibilty. |
2478 node..indexRef.unlink()..indexRef = null; | 2403 node |
| 2404 ..indexRef.unlink() |
| 2405 ..indexRef = null; |
2479 } | 2406 } |
2480 } | 2407 } |
2481 | 2408 |
2482 visitReceiverCheck(ReceiverCheck node) { | 2409 visitReceiverCheck(ReceiverCheck node) { |
2483 Primitive input = node.value; | 2410 Primitive input = node.value; |
2484 if (!input.type.isNullable && | 2411 if (!input.type.isNullable && |
2485 (node.isNullCheck || | 2412 (node.isNullCheck || |
2486 !input.type.needsNoSuchMethodHandling(node.selector, classWorld))) { | 2413 !input.type.needsNoSuchMethodHandling(node.selector, classWorld))) { |
2487 node.replaceUsesWith(input); | 2414 node.replaceUsesWith(input); |
2488 return new CpsFragment(); | 2415 return new CpsFragment(); |
2489 } | 2416 } |
2490 return null; | 2417 return null; |
2491 } | 2418 } |
2492 | 2419 |
2493 visitGetLength(GetLength node) { | 2420 visitGetLength(GetLength node) { |
2494 node.isFinal = typeSystem.isDefinitelyFixedLengthIndexable( | 2421 node.isFinal = typeSystem.isDefinitelyFixedLengthIndexable(node.object.type, |
2495 node.object.type, allowNull: true); | 2422 allowNull: true); |
2496 } | 2423 } |
2497 | 2424 |
2498 visitReadTypeVariable(ReadTypeVariable node) { | 2425 visitReadTypeVariable(ReadTypeVariable node) { |
2499 // Pattern match on | 2426 // Pattern match on |
2500 // | 2427 // |
2501 // ReadTypeVariable(var, CreateInstance(..., TypeExpression(arguments))) | 2428 // ReadTypeVariable(var, CreateInstance(..., TypeExpression(arguments))) |
2502 // | 2429 // |
2503 // and extract the argument that corresponds to the type variable. This is a | 2430 // and extract the argument that corresponds to the type variable. This is a |
2504 // shrinking reduction. | 2431 // shrinking reduction. |
2505 // | 2432 // |
(...skipping 26 matching lines...) Expand all Loading... |
2532 } | 2459 } |
2533 return null; | 2460 return null; |
2534 } | 2461 } |
2535 | 2462 |
2536 bool isNullConstant(Primitive prim) => prim is Constant && prim.value.isNull; | 2463 bool isNullConstant(Primitive prim) => prim is Constant && prim.value.isNull; |
2537 | 2464 |
2538 visitCreateInstance(CreateInstance node) { | 2465 visitCreateInstance(CreateInstance node) { |
2539 Primitive typeInformation = node.typeInformation; | 2466 Primitive typeInformation = node.typeInformation; |
2540 if (typeInformation is TypeExpression && | 2467 if (typeInformation is TypeExpression && |
2541 typeInformation.arguments.every(isNullConstant)) { | 2468 typeInformation.arguments.every(isNullConstant)) { |
2542 node..typeInformationRef.unlink()..typeInformationRef = null; | 2469 node |
| 2470 ..typeInformationRef.unlink() |
| 2471 ..typeInformationRef = null; |
2543 } | 2472 } |
2544 } | 2473 } |
2545 } | 2474 } |
2546 | 2475 |
2547 /** | 2476 /** |
2548 * Runs an analysis pass on the given function definition in order to detect | 2477 * Runs an analysis pass on the given function definition in order to detect |
2549 * const-ness as well as reachability, both of which are used in the subsequent | 2478 * const-ness as well as reachability, both of which are used in the subsequent |
2550 * transformation pass. | 2479 * transformation pass. |
2551 */ | 2480 */ |
2552 class TypePropagationVisitor implements Visitor { | 2481 class TypePropagationVisitor implements Visitor { |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2584 } | 2513 } |
2585 | 2514 |
2586 AbstractConstantValue constantValue(ConstantValue constant, [TypeMask type]) { | 2515 AbstractConstantValue constantValue(ConstantValue constant, [TypeMask type]) { |
2587 return lattice.constant(constant, type); | 2516 return lattice.constant(constant, type); |
2588 } | 2517 } |
2589 | 2518 |
2590 // Stores the current lattice value for primitives and mutable variables. | 2519 // Stores the current lattice value for primitives and mutable variables. |
2591 // Access through [getValue] and [setValue]. | 2520 // Access through [getValue] and [setValue]. |
2592 final Map<Variable, ConstantValue> values; | 2521 final Map<Variable, ConstantValue> values; |
2593 | 2522 |
2594 TypePropagationVisitor(this.lattice, | 2523 TypePropagationVisitor(this.lattice, this.values, this.internalError); |
2595 this.values, | |
2596 this.internalError); | |
2597 | 2524 |
2598 void analyze(FunctionDefinition root, bool recomputeAll) { | 2525 void analyze(FunctionDefinition root, bool recomputeAll) { |
2599 reachableContinuations.clear(); | 2526 reachableContinuations.clear(); |
2600 if (recomputeAll) { | 2527 if (recomputeAll) { |
2601 new ResetAnalysisInfo(reachableContinuations, values).visit(root); | 2528 new ResetAnalysisInfo(reachableContinuations, values).visit(root); |
2602 } | 2529 } |
2603 | 2530 |
2604 // Initially, only the root node is reachable. | 2531 // Initially, only the root node is reachable. |
2605 push(root); | 2532 push(root); |
2606 | 2533 |
(...skipping 16 matching lines...) Expand all Loading... |
2623 // Process all usages of a changed definition. | 2550 // Process all usages of a changed definition. |
2624 Definition def = defWorklist.removeLast(); | 2551 Definition def = defWorklist.removeLast(); |
2625 | 2552 |
2626 // Visit all uses of this definition. This might add new entries to | 2553 // Visit all uses of this definition. This might add new entries to |
2627 // [nodeWorklist], for example by visiting a newly-constant usage within | 2554 // [nodeWorklist], for example by visiting a newly-constant usage within |
2628 // a branch node. | 2555 // a branch node. |
2629 for (Reference ref = def.firstRef; ref != null; ref = ref.next) { | 2556 for (Reference ref = def.firstRef; ref != null; ref = ref.next) { |
2630 visit(ref.parent); | 2557 visit(ref.parent); |
2631 } | 2558 } |
2632 } else { | 2559 } else { |
2633 break; // Both worklists empty. | 2560 break; // Both worklists empty. |
2634 } | 2561 } |
2635 } | 2562 } |
2636 } | 2563 } |
2637 | 2564 |
2638 /// Adds [node] to the worklist. | 2565 /// Adds [node] to the worklist. |
2639 void push(Node node) { | 2566 void push(Node node) { |
2640 nodeWorklist.add(node); | 2567 nodeWorklist.add(node); |
2641 } | 2568 } |
2642 | 2569 |
2643 /// If the passed node is not yet reachable, mark it reachable and add it | 2570 /// If the passed node is not yet reachable, mark it reachable and add it |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2677 assert(newValue.kind >= oldValue.kind); | 2604 assert(newValue.kind >= oldValue.kind); |
2678 | 2605 |
2679 values[node] = newValue.isConstant ? newValue.constant : null; | 2606 values[node] = newValue.isConstant ? newValue.constant : null; |
2680 defWorklist.add(node); | 2607 defWorklist.add(node); |
2681 } | 2608 } |
2682 | 2609 |
2683 /// Sets the type of the given primitive. | 2610 /// Sets the type of the given primitive. |
2684 /// | 2611 /// |
2685 /// If [updateValue] is a constant and [canReplace] is true, the primitive | 2612 /// If [updateValue] is a constant and [canReplace] is true, the primitive |
2686 /// is also marked as safe for elimination, so it can be constant-folded. | 2613 /// is also marked as safe for elimination, so it can be constant-folded. |
2687 void setResult(UnsafePrimitive prim, | 2614 void setResult(UnsafePrimitive prim, AbstractConstantValue updateValue, |
2688 AbstractConstantValue updateValue, | 2615 {bool canReplace: false}) { |
2689 {bool canReplace: false}) { | |
2690 // TODO(asgerf): Separate constant folding from side effect analysis. | 2616 // TODO(asgerf): Separate constant folding from side effect analysis. |
2691 setValue(prim, updateValue); | 2617 setValue(prim, updateValue); |
2692 prim.isSafeForElimination = canReplace && updateValue.isConstant; | 2618 prim.isSafeForElimination = canReplace && updateValue.isConstant; |
2693 } | 2619 } |
2694 | 2620 |
2695 bool isInterceptedSelector(Selector selector) { | 2621 bool isInterceptedSelector(Selector selector) { |
2696 return backend.isInterceptedSelector(selector); | 2622 return backend.isInterceptedSelector(selector); |
2697 } | 2623 } |
2698 | 2624 |
2699 // -------------------------- Visitor overrides ------------------------------ | 2625 // -------------------------- Visitor overrides ------------------------------ |
2700 void visit(Node node) { node.accept(this); } | 2626 void visit(Node node) { |
| 2627 node.accept(this); |
| 2628 } |
2701 | 2629 |
2702 void visitFunctionDefinition(FunctionDefinition node) { | 2630 void visitFunctionDefinition(FunctionDefinition node) { |
2703 if (node.interceptorParameter != null) { | 2631 if (node.interceptorParameter != null) { |
2704 setValue(node.interceptorParameter, nonConstant(typeSystem.nonNullType)); | 2632 setValue(node.interceptorParameter, nonConstant(typeSystem.nonNullType)); |
2705 } | 2633 } |
2706 // If the abstract value of the function parameters is Nothing, use the | 2634 // If the abstract value of the function parameters is Nothing, use the |
2707 // inferred parameter type. Otherwise (e.g., when inlining) do not | 2635 // inferred parameter type. Otherwise (e.g., when inlining) do not |
2708 // change the abstract value. | 2636 // change the abstract value. |
2709 if (node.receiverParameter != null && | 2637 if (node.receiverParameter != null && |
2710 getValue(node.receiverParameter).isNothing) { | 2638 getValue(node.receiverParameter).isNothing) { |
2711 setValue(node.receiverParameter, | 2639 setValue(node.receiverParameter, |
2712 nonConstant(typeSystem.getReceiverType(node.element))); | 2640 nonConstant(typeSystem.getReceiverType(node.element))); |
2713 } | 2641 } |
2714 bool hasParameterWithoutValue = false; | 2642 bool hasParameterWithoutValue = false; |
2715 for (Parameter param in node.parameters) { | 2643 for (Parameter param in node.parameters) { |
2716 if (getValue(param).isNothing) { | 2644 if (getValue(param).isNothing) { |
2717 TypeMask type = param.hint is ParameterElement | 2645 TypeMask type = param.hint is ParameterElement |
2718 ? typeSystem.getParameterType(param.hint) | 2646 ? typeSystem.getParameterType(param.hint) |
2719 : typeSystem.dynamicType; | 2647 : typeSystem.dynamicType; |
2720 setValue(param, lattice.fromMask(type)); | 2648 setValue(param, lattice.fromMask(type)); |
2721 if (type.isEmpty) hasParameterWithoutValue = true; | 2649 if (type.isEmpty) hasParameterWithoutValue = true; |
2722 } | 2650 } |
2723 } | 2651 } |
2724 if (!hasParameterWithoutValue) { // Don't analyze unreachable code. | 2652 if (!hasParameterWithoutValue) { |
| 2653 // Don't analyze unreachable code. |
2725 push(node.body); | 2654 push(node.body); |
2726 } | 2655 } |
2727 } | 2656 } |
2728 | 2657 |
2729 void visitLetPrim(LetPrim node) { | 2658 void visitLetPrim(LetPrim node) { |
2730 visit(node.primitive); // No reason to delay visits to primitives. | 2659 visit(node.primitive); // No reason to delay visits to primitives. |
2731 push(node.body); | 2660 push(node.body); |
2732 } | 2661 } |
2733 | 2662 |
2734 void visitLetCont(LetCont node) { | 2663 void visitLetCont(LetCont node) { |
(...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2841 AbstractConstantValue right = getValue(node.argument(0)); | 2770 AbstractConstantValue right = getValue(node.argument(0)); |
2842 BinaryOperator operator = BinaryOperator.parse(opname); | 2771 BinaryOperator operator = BinaryOperator.parse(opname); |
2843 AbstractConstantValue result = | 2772 AbstractConstantValue result = |
2844 lattice.binaryOp(operator, receiver, right); | 2773 lattice.binaryOp(operator, receiver, right); |
2845 return finish(result, canReplace: !receiver.isNullable); | 2774 return finish(result, canReplace: !receiver.isNullable); |
2846 } | 2775 } |
2847 return finish(null); | 2776 return finish(null); |
2848 } | 2777 } |
2849 | 2778 |
2850 void visitApplyBuiltinOperator(ApplyBuiltinOperator node) { | 2779 void visitApplyBuiltinOperator(ApplyBuiltinOperator node) { |
2851 | |
2852 void unaryOp( | 2780 void unaryOp( |
2853 AbstractConstantValue operation(AbstractConstantValue argument), | 2781 AbstractConstantValue operation(AbstractConstantValue argument), |
2854 TypeMask defaultType) { | 2782 TypeMask defaultType) { |
2855 AbstractConstantValue value = getValue(node.argument(0)); | 2783 AbstractConstantValue value = getValue(node.argument(0)); |
2856 setValue(node, operation(value) ?? nonConstant(defaultType)); | 2784 setValue(node, operation(value) ?? nonConstant(defaultType)); |
2857 } | 2785 } |
2858 | 2786 |
2859 void binaryOp( | 2787 void binaryOp( |
2860 AbstractConstantValue operation(AbstractConstantValue left, | 2788 AbstractConstantValue operation( |
2861 AbstractConstantValue right), | 2789 AbstractConstantValue left, AbstractConstantValue right), |
2862 TypeMask defaultType) { | 2790 TypeMask defaultType) { |
2863 AbstractConstantValue left = getValue(node.argument(0)); | 2791 AbstractConstantValue left = getValue(node.argument(0)); |
2864 AbstractConstantValue right = getValue(node.argument(1)); | 2792 AbstractConstantValue right = getValue(node.argument(1)); |
2865 setValue(node, operation(left, right) ?? nonConstant(defaultType)); | 2793 setValue(node, operation(left, right) ?? nonConstant(defaultType)); |
2866 } | 2794 } |
2867 | 2795 |
2868 void binaryNumOp( | 2796 void binaryNumOp( |
2869 AbstractConstantValue operation(AbstractConstantValue left, | 2797 AbstractConstantValue operation( |
2870 AbstractConstantValue right)) { | 2798 AbstractConstantValue left, AbstractConstantValue right)) { |
2871 binaryOp(operation, typeSystem.numType); | 2799 binaryOp(operation, typeSystem.numType); |
2872 } | 2800 } |
2873 | 2801 |
2874 void binaryUint32Op( | 2802 void binaryUint32Op( |
2875 AbstractConstantValue operation(AbstractConstantValue left, | 2803 AbstractConstantValue operation( |
2876 AbstractConstantValue right)) { | 2804 AbstractConstantValue left, AbstractConstantValue right)) { |
2877 binaryOp(operation, typeSystem.uint32Type); | 2805 binaryOp(operation, typeSystem.uint32Type); |
2878 } | 2806 } |
2879 | 2807 |
2880 void binaryBoolOp( | 2808 void binaryBoolOp( |
2881 AbstractConstantValue operation(AbstractConstantValue left, | 2809 AbstractConstantValue operation( |
2882 AbstractConstantValue right)) { | 2810 AbstractConstantValue left, AbstractConstantValue right)) { |
2883 binaryOp(operation, typeSystem.boolType); | 2811 binaryOp(operation, typeSystem.boolType); |
2884 } | 2812 } |
2885 | 2813 |
2886 switch (node.operator) { | 2814 switch (node.operator) { |
2887 case BuiltinOperator.StringConcatenate: | 2815 case BuiltinOperator.StringConcatenate: |
2888 ast.DartString stringValue = const ast.LiteralDartString(''); | 2816 ast.DartString stringValue = const ast.LiteralDartString(''); |
2889 for (Reference<Primitive> arg in node.argumentRefs) { | 2817 for (Reference<Primitive> arg in node.argumentRefs) { |
2890 AbstractConstantValue value = getValue(arg.definition); | 2818 AbstractConstantValue value = getValue(arg.definition); |
2891 if (value.isNothing) { | 2819 if (value.isNothing) { |
2892 setValue(node, lattice.nothing); | 2820 setValue(node, lattice.nothing); |
2893 return; // And come back later | 2821 return; // And come back later |
2894 } else if (value.isConstant && | 2822 } else if (value.isConstant && |
2895 value.constant.isString && | 2823 value.constant.isString && |
2896 stringValue != null) { | 2824 stringValue != null) { |
2897 StringConstantValue constant = value.constant; | 2825 StringConstantValue constant = value.constant; |
2898 stringValue = | 2826 stringValue = |
2899 new ast.ConsDartString(stringValue, constant.primitiveValue); | 2827 new ast.ConsDartString(stringValue, constant.primitiveValue); |
2900 } else { | 2828 } else { |
2901 stringValue = null; | 2829 stringValue = null; |
2902 break; | 2830 break; |
2903 } | 2831 } |
2904 } | 2832 } |
2905 if (stringValue == null) { | 2833 if (stringValue == null) { |
2906 setValue(node, nonConstant(typeSystem.stringType)); | 2834 setValue(node, nonConstant(typeSystem.stringType)); |
2907 } else { | 2835 } else { |
2908 setValue(node, constantValue(new StringConstantValue(stringValue))); | 2836 setValue(node, constantValue(new StringConstantValue(stringValue))); |
2909 } | 2837 } |
2910 break; | 2838 break; |
2911 | 2839 |
2912 case BuiltinOperator.CharCodeAt: | 2840 case BuiltinOperator.CharCodeAt: |
2913 binaryOp(lattice.codeUnitAtSpecial, typeSystem.uint31Type); | 2841 binaryOp(lattice.codeUnitAtSpecial, typeSystem.uint31Type); |
2914 break; | 2842 break; |
2915 | 2843 |
2916 case BuiltinOperator.Identical: | 2844 case BuiltinOperator.Identical: |
2917 case BuiltinOperator.StrictEq: | 2845 case BuiltinOperator.StrictEq: |
2918 case BuiltinOperator.StrictNeq: | 2846 case BuiltinOperator.StrictNeq: |
2919 case BuiltinOperator.LooseEq: | 2847 case BuiltinOperator.LooseEq: |
2920 case BuiltinOperator.LooseNeq: | 2848 case BuiltinOperator.LooseNeq: |
2921 bool negated = | 2849 bool negated = node.operator == BuiltinOperator.StrictNeq || |
2922 node.operator == BuiltinOperator.StrictNeq || | |
2923 node.operator == BuiltinOperator.LooseNeq; | 2850 node.operator == BuiltinOperator.LooseNeq; |
2924 AbstractConstantValue left = getValue(node.argument(0)); | 2851 AbstractConstantValue left = getValue(node.argument(0)); |
2925 AbstractConstantValue right = getValue(node.argument(1)); | 2852 AbstractConstantValue right = getValue(node.argument(1)); |
2926 if (left.isNothing || right.isNothing) { | 2853 if (left.isNothing || right.isNothing) { |
2927 setValue(node, lattice.nothing); | 2854 setValue(node, lattice.nothing); |
2928 return; | 2855 return; |
2929 } | 2856 } |
2930 if (left.isConstant && right.isConstant) { | 2857 if (left.isConstant && right.isConstant) { |
2931 ConstantValue equal = lattice.constantSystem.identity.fold( | 2858 ConstantValue equal = lattice.constantSystem.identity |
2932 left.constant, right.constant); | 2859 .fold(left.constant, right.constant); |
2933 if (equal != null && equal.isBool) { | 2860 if (equal != null && equal.isBool) { |
2934 ConstantValue result = | 2861 ConstantValue result = |
2935 new BoolConstantValue(equal.isTrue == !negated); | 2862 new BoolConstantValue(equal.isTrue == !negated); |
2936 setValue(node, constantValue(result, typeSystem.boolType)); | 2863 setValue(node, constantValue(result, typeSystem.boolType)); |
2937 return; | 2864 return; |
2938 } | 2865 } |
2939 } | 2866 } |
2940 if (typeSystem.areDisjoint(left.type, right.type)) { | 2867 if (typeSystem.areDisjoint(left.type, right.type)) { |
2941 ConstantValue result = new BoolConstantValue(negated); | 2868 ConstantValue result = new BoolConstantValue(negated); |
2942 setValue(node, constantValue(result, typeSystem.boolType)); | 2869 setValue(node, constantValue(result, typeSystem.boolType)); |
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3031 case BuiltinOperator.IsUnmodifiableJSArray: | 2958 case BuiltinOperator.IsUnmodifiableJSArray: |
3032 case BuiltinOperator.IsModifiableJSArray: | 2959 case BuiltinOperator.IsModifiableJSArray: |
3033 setValue(node, nonConstant(typeSystem.boolType)); | 2960 setValue(node, nonConstant(typeSystem.boolType)); |
3034 break; | 2961 break; |
3035 } | 2962 } |
3036 } | 2963 } |
3037 | 2964 |
3038 void visitApplyBuiltinMethod(ApplyBuiltinMethod node) { | 2965 void visitApplyBuiltinMethod(ApplyBuiltinMethod node) { |
3039 AbstractConstantValue receiver = getValue(node.receiver); | 2966 AbstractConstantValue receiver = getValue(node.receiver); |
3040 if (node.method == BuiltinMethod.Pop) { | 2967 if (node.method == BuiltinMethod.Pop) { |
3041 setValue(node, nonConstant( | 2968 setValue( |
3042 typeSystem.elementTypeOfIndexable(receiver.type))); | 2969 node, nonConstant(typeSystem.elementTypeOfIndexable(receiver.type))); |
3043 } else { | 2970 } else { |
3044 setValue(node, nonConstant()); | 2971 setValue(node, nonConstant()); |
3045 } | 2972 } |
3046 } | 2973 } |
3047 | 2974 |
3048 void visitInvokeMethodDirectly(InvokeMethodDirectly node) { | 2975 void visitInvokeMethodDirectly(InvokeMethodDirectly node) { |
3049 if (node.isConstructorBodyCall) { | 2976 if (node.isConstructorBodyCall) { |
3050 setResult(node, lattice.nullValue); | 2977 setResult(node, lattice.nullValue); |
3051 } else if (node.isTearOff) { | 2978 } else if (node.isTearOff) { |
3052 setResult(node, nonConstant(typeSystem.functionType)); | 2979 setResult(node, nonConstant(typeSystem.functionType)); |
3053 } else { | 2980 } else { |
3054 setResult(node, nonConstant(typeSystem.getReturnType(node.target))); | 2981 setResult(node, nonConstant(typeSystem.getReturnType(node.target))); |
3055 } | 2982 } |
3056 } | 2983 } |
3057 | 2984 |
3058 void visitInvokeConstructor(InvokeConstructor node) { | 2985 void visitInvokeConstructor(InvokeConstructor node) { |
3059 if (node.allocationSiteType != null) { | 2986 if (node.allocationSiteType != null) { |
3060 setResult(node, nonConstant(node.allocationSiteType)); | 2987 setResult(node, nonConstant(node.allocationSiteType)); |
3061 } else { | 2988 } else { |
3062 setResult(node, nonConstant(typeSystem.getReturnType(node.target))); | 2989 setResult(node, nonConstant(typeSystem.getReturnType(node.target))); |
3063 } | 2990 } |
3064 } | 2991 } |
3065 | 2992 |
3066 void visitThrow(Throw node) { | 2993 void visitThrow(Throw node) {} |
3067 } | |
3068 | 2994 |
3069 void visitRethrow(Rethrow node) { | 2995 void visitRethrow(Rethrow node) {} |
3070 } | |
3071 | 2996 |
3072 void visitUnreachable(Unreachable node) { | 2997 void visitUnreachable(Unreachable node) {} |
3073 } | |
3074 | 2998 |
3075 void visitBranch(Branch node) { | 2999 void visitBranch(Branch node) { |
3076 AbstractConstantValue conditionCell = getValue(node.condition); | 3000 AbstractConstantValue conditionCell = getValue(node.condition); |
3077 AbstractBool boolifiedValue = node.isStrictCheck | 3001 AbstractBool boolifiedValue = node.isStrictCheck |
3078 ? lattice.strictBoolify(conditionCell) | 3002 ? lattice.strictBoolify(conditionCell) |
3079 : lattice.boolify(conditionCell); | 3003 : lattice.boolify(conditionCell); |
3080 switch (boolifiedValue) { | 3004 switch (boolifiedValue) { |
3081 case AbstractBool.Nothing: | 3005 case AbstractBool.Nothing: |
3082 break; | 3006 break; |
3083 case AbstractBool.True: | 3007 case AbstractBool.True: |
(...skipping 17 matching lines...) Expand all Loading... |
3101 // TODO(sra): We could see if we can find the value in the interceptor | 3025 // TODO(sra): We could see if we can find the value in the interceptor |
3102 // expression. It would probably have no benefit - we only see | 3026 // expression. It would probably have no benefit - we only see |
3103 // TypeTestViaFlag after rewriting TypeTest and the rewrite of TypeTest | 3027 // TypeTestViaFlag after rewriting TypeTest and the rewrite of TypeTest |
3104 // would already have done the interesting optimizations. | 3028 // would already have done the interesting optimizations. |
3105 setValue(node, nonConstant(typeSystem.boolType)); | 3029 setValue(node, nonConstant(typeSystem.boolType)); |
3106 } | 3030 } |
3107 | 3031 |
3108 void handleTypeTest( | 3032 void handleTypeTest( |
3109 Primitive node, AbstractConstantValue input, types.DartType dartType) { | 3033 Primitive node, AbstractConstantValue input, types.DartType dartType) { |
3110 TypeMask boolType = typeSystem.boolType; | 3034 TypeMask boolType = typeSystem.boolType; |
3111 switch(lattice.isSubtypeOf(input, dartType, allowNull: false)) { | 3035 switch (lattice.isSubtypeOf(input, dartType, allowNull: false)) { |
3112 case AbstractBool.Nothing: | 3036 case AbstractBool.Nothing: |
3113 break; // And come back later. | 3037 break; // And come back later. |
3114 | 3038 |
3115 case AbstractBool.True: | 3039 case AbstractBool.True: |
3116 setValue(node, constantValue(new TrueConstantValue(), boolType)); | 3040 setValue(node, constantValue(new TrueConstantValue(), boolType)); |
3117 break; | 3041 break; |
3118 | 3042 |
3119 case AbstractBool.False: | 3043 case AbstractBool.False: |
3120 setValue(node, constantValue(new FalseConstantValue(), boolType)); | 3044 setValue(node, constantValue(new FalseConstantValue(), boolType)); |
3121 break; | 3045 break; |
(...skipping 15 matching lines...) Expand all Loading... |
3137 setValue(node, input); | 3061 setValue(node, input); |
3138 break; | 3062 break; |
3139 | 3063 |
3140 case AbstractBool.False: | 3064 case AbstractBool.False: |
3141 setValue(node, lattice.nothing); // Cast fails. | 3065 setValue(node, lattice.nothing); // Cast fails. |
3142 break; | 3066 break; |
3143 | 3067 |
3144 case AbstractBool.Maybe: | 3068 case AbstractBool.Maybe: |
3145 // Narrow type of output to those that survive the cast. | 3069 // Narrow type of output to those that survive the cast. |
3146 TypeMask type = input.type.intersection( | 3070 TypeMask type = input.type.intersection( |
3147 typeSystem.subtypesOf(node.dartType).nullable(), | 3071 typeSystem.subtypesOf(node.dartType).nullable(), classWorld); |
3148 classWorld); | |
3149 setValue(node, nonConstant(type)); | 3072 setValue(node, nonConstant(type)); |
3150 break; | 3073 break; |
3151 } | 3074 } |
3152 } | 3075 } |
3153 | 3076 |
3154 void visitSetMutable(SetMutable node) { | 3077 void visitSetMutable(SetMutable node) { |
3155 setValue(node.variable, getValue(node.value)); | 3078 setValue(node.variable, getValue(node.value)); |
3156 } | 3079 } |
3157 | 3080 |
3158 void visitLiteralList(LiteralList node) { | 3081 void visitLiteralList(LiteralList node) { |
(...skipping 11 matching lines...) Expand all Loading... |
3170 setValue(node, nonConstant(typeSystem.getTypeOf(value))); | 3093 setValue(node, nonConstant(typeSystem.getTypeOf(value))); |
3171 } else { | 3094 } else { |
3172 setValue(node, constantValue(value, typeSystem.getTypeOf(value))); | 3095 setValue(node, constantValue(value, typeSystem.getTypeOf(value))); |
3173 } | 3096 } |
3174 } | 3097 } |
3175 | 3098 |
3176 void visitGetMutable(GetMutable node) { | 3099 void visitGetMutable(GetMutable node) { |
3177 setValue(node, getValue(node.variable)); | 3100 setValue(node, getValue(node.variable)); |
3178 } | 3101 } |
3179 | 3102 |
3180 void visitMutableVariable(MutableVariable node) { | 3103 void visitMutableVariable(MutableVariable node) {} |
3181 } | |
3182 | 3104 |
3183 void visitParameter(Parameter node) { | 3105 void visitParameter(Parameter node) {} |
3184 } | |
3185 | 3106 |
3186 void visitContinuation(Continuation node) { | 3107 void visitContinuation(Continuation node) { |
3187 node.parameters.forEach(visit); | 3108 node.parameters.forEach(visit); |
3188 | 3109 |
3189 if (node.body != null) { | 3110 if (node.body != null) { |
3190 push(node.body); | 3111 push(node.body); |
3191 } | 3112 } |
3192 } | 3113 } |
3193 | 3114 |
3194 void visitGetStatic(GetStatic node) { | 3115 void visitGetStatic(GetStatic node) { |
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3270 | 3191 |
3271 void visitCreateInvocationMirror(CreateInvocationMirror node) { | 3192 void visitCreateInvocationMirror(CreateInvocationMirror node) { |
3272 // TODO(asgerf): Expose [Invocation] type. | 3193 // TODO(asgerf): Expose [Invocation] type. |
3273 setValue(node, nonConstant(typeSystem.nonNullType)); | 3194 setValue(node, nonConstant(typeSystem.nonNullType)); |
3274 } | 3195 } |
3275 | 3196 |
3276 @override | 3197 @override |
3277 void visitForeignCode(ForeignCode node) { | 3198 void visitForeignCode(ForeignCode node) { |
3278 bool firstArgumentIsNullable = false; | 3199 bool firstArgumentIsNullable = false; |
3279 if (node.argumentRefs.length > 0) { | 3200 if (node.argumentRefs.length > 0) { |
3280 AbstractConstantValue first = getValue(node.argumentRefs.first.definition)
; | 3201 AbstractConstantValue first = |
| 3202 getValue(node.argumentRefs.first.definition); |
3281 if (first.isNothing) { | 3203 if (first.isNothing) { |
3282 setValue(node, nothing); | 3204 setValue(node, nothing); |
3283 return; | 3205 return; |
3284 } | 3206 } |
3285 firstArgumentIsNullable = first.isNullable; | 3207 firstArgumentIsNullable = first.isNullable; |
3286 } | 3208 } |
3287 setValue(node, nonConstant(node.storedType)); | 3209 setValue(node, nonConstant(node.storedType)); |
3288 node.isSafeForElimination = | 3210 node.isSafeForElimination = |
3289 !node.nativeBehavior.sideEffects.hasSideEffects() && | 3211 !node.nativeBehavior.sideEffects.hasSideEffects() && |
3290 (!node.nativeBehavior.throwBehavior.canThrow || | 3212 (!node.nativeBehavior.throwBehavior.canThrow || |
3291 (!firstArgumentIsNullable && | 3213 (!firstArgumentIsNullable && |
3292 node.nativeBehavior.throwBehavior.isOnlyNullNSMGuard)); | 3214 node.nativeBehavior.throwBehavior.isOnlyNullNSMGuard)); |
3293 } | 3215 } |
3294 | 3216 |
3295 @override | 3217 @override |
3296 void visitGetLength(GetLength node) { | 3218 void visitGetLength(GetLength node) { |
3297 AbstractConstantValue input = getValue(node.object); | 3219 AbstractConstantValue input = getValue(node.object); |
3298 node.objectIsNotNull = input.isDefinitelyNotNull; | 3220 node.objectIsNotNull = input.isDefinitelyNotNull; |
3299 AbstractConstantValue length = lattice.lengthSpecial(input); | 3221 AbstractConstantValue length = lattice.lengthSpecial(input); |
3300 if (length != null) { | 3222 if (length != null) { |
3301 // TODO(asgerf): Constant-folding the length might degrade the VM's | 3223 // TODO(asgerf): Constant-folding the length might degrade the VM's |
3302 // own bounds-check elimination? | 3224 // own bounds-check elimination? |
3303 setValue(node, length); | 3225 setValue(node, length); |
3304 } else { | 3226 } else { |
3305 setValue(node, nonConstant(typeSystem.uint32Type)); | 3227 setValue(node, nonConstant(typeSystem.uint32Type)); |
3306 } | 3228 } |
3307 } | 3229 } |
3308 | 3230 |
3309 @override | 3231 @override |
3310 void visitGetIndex(GetIndex node) { | 3232 void visitGetIndex(GetIndex node) { |
3311 AbstractConstantValue object = getValue(node.object); | 3233 AbstractConstantValue object = getValue(node.object); |
3312 if (object.isNothing || object.isNullConstant) { | 3234 if (object.isNothing || object.isNullConstant) { |
3313 setValue(node, nothing); | 3235 setValue(node, nothing); |
3314 } else { | 3236 } else { |
3315 node.objectIsNotNull = object.isDefinitelyNotNull; | 3237 node.objectIsNotNull = object.isDefinitelyNotNull; |
3316 setValue(node, nonConstant(typeSystem.elementTypeOfIndexable(object.type))
); | 3238 setValue( |
| 3239 node, nonConstant(typeSystem.elementTypeOfIndexable(object.type))); |
3317 } | 3240 } |
3318 } | 3241 } |
3319 | 3242 |
3320 @override | 3243 @override |
3321 void visitSetIndex(SetIndex node) {} | 3244 void visitSetIndex(SetIndex node) {} |
3322 | 3245 |
3323 @override | 3246 @override |
3324 void visitAwait(Await node) { | 3247 void visitAwait(Await node) { |
3325 setResult(node, nonConstant()); | 3248 setResult(node, nonConstant()); |
3326 } | 3249 } |
3327 | 3250 |
3328 @override | 3251 @override |
3329 visitYield(Yield node) { | 3252 visitYield(Yield node) { |
3330 setValue(node, nonConstant()); | 3253 setValue(node, nonConstant()); |
3331 } | 3254 } |
3332 | 3255 |
3333 @override | 3256 @override |
3334 void visitRefinement(Refinement node) { | 3257 void visitRefinement(Refinement node) { |
3335 setValue(node, lattice.intersectWithType( | 3258 setValue( |
3336 getValue(node.value.definition), | 3259 node, |
3337 node.refineType)); | 3260 lattice.intersectWithType( |
| 3261 getValue(node.value.definition), node.refineType)); |
3338 } | 3262 } |
3339 | 3263 |
3340 @override | 3264 @override |
3341 void visitBoundsCheck(BoundsCheck node) { | 3265 void visitBoundsCheck(BoundsCheck node) { |
3342 setValue(node, getValue(node.object)); | 3266 setValue(node, getValue(node.object)); |
3343 } | 3267 } |
3344 | 3268 |
3345 @override | 3269 @override |
3346 void visitReceiverCheck(ReceiverCheck node) { | 3270 void visitReceiverCheck(ReceiverCheck node) { |
3347 AbstractConstantValue value = getValue(node.value); | 3271 AbstractConstantValue value = getValue(node.value); |
(...skipping 14 matching lines...) Expand all Loading... |
3362 /// Represents the abstract value of a primitive value at some point in the | 3286 /// Represents the abstract value of a primitive value at some point in the |
3363 /// program. Abstract values of all kinds have a type [T]. | 3287 /// program. Abstract values of all kinds have a type [T]. |
3364 /// | 3288 /// |
3365 /// The different kinds of abstract values represents the knowledge about the | 3289 /// The different kinds of abstract values represents the knowledge about the |
3366 /// constness of the value: | 3290 /// constness of the value: |
3367 /// NOTHING: cannot have any value | 3291 /// NOTHING: cannot have any value |
3368 /// CONSTANT: is a constant. The value is stored in the [constant] field, | 3292 /// CONSTANT: is a constant. The value is stored in the [constant] field, |
3369 /// and the type of the constant is in the [type] field. | 3293 /// and the type of the constant is in the [type] field. |
3370 /// NONCONST: not a constant, but [type] may hold some information. | 3294 /// NONCONST: not a constant, but [type] may hold some information. |
3371 class AbstractConstantValue { | 3295 class AbstractConstantValue { |
3372 static const int NOTHING = 0; | 3296 static const int NOTHING = 0; |
3373 static const int CONSTANT = 1; | 3297 static const int CONSTANT = 1; |
3374 static const int NONCONST = 2; | 3298 static const int NONCONST = 2; |
3375 | 3299 |
3376 final int kind; | 3300 final int kind; |
3377 final ConstantValue constant; | 3301 final ConstantValue constant; |
3378 final TypeMask type; | 3302 final TypeMask type; |
3379 | 3303 |
3380 AbstractConstantValue._internal(this.kind, this.constant, this.type) { | 3304 AbstractConstantValue._internal(this.kind, this.constant, this.type) { |
3381 assert(kind != CONSTANT || constant != null); | 3305 assert(kind != CONSTANT || constant != null); |
3382 assert(constant is! SyntheticConstantValue); | 3306 assert(constant is! SyntheticConstantValue); |
3383 } | 3307 } |
3384 | 3308 |
3385 AbstractConstantValue.nothing() | 3309 AbstractConstantValue.nothing() |
3386 : this._internal(NOTHING, null, new TypeMask.nonNullEmpty()); | 3310 : this._internal(NOTHING, null, new TypeMask.nonNullEmpty()); |
3387 | 3311 |
3388 AbstractConstantValue.constantValue(ConstantValue constant, TypeMask type) | 3312 AbstractConstantValue.constantValue(ConstantValue constant, TypeMask type) |
3389 : this._internal(CONSTANT, constant, type); | 3313 : this._internal(CONSTANT, constant, type); |
3390 | 3314 |
3391 factory AbstractConstantValue.nonConstant(TypeMask type) { | 3315 factory AbstractConstantValue.nonConstant(TypeMask type) { |
3392 if (type.isEmpty) { | 3316 if (type.isEmpty) { |
3393 return new AbstractConstantValue.nothing(); | 3317 return new AbstractConstantValue.nothing(); |
3394 } else if (type.isNull) { | 3318 } else if (type.isNull) { |
3395 return new AbstractConstantValue.constantValue( | 3319 return new AbstractConstantValue.constantValue( |
3396 new NullConstantValue(), type); | 3320 new NullConstantValue(), type); |
3397 } else { | 3321 } else { |
3398 return new AbstractConstantValue._internal(NONCONST, null, type); | 3322 return new AbstractConstantValue._internal(NONCONST, null, type); |
3399 } | 3323 } |
3400 } | 3324 } |
3401 | 3325 |
3402 bool get isNothing => (kind == NOTHING); | 3326 bool get isNothing => (kind == NOTHING); |
3403 bool get isConstant => (kind == CONSTANT); | 3327 bool get isConstant => (kind == CONSTANT); |
3404 bool get isNonConst => (kind == NONCONST); | 3328 bool get isNonConst => (kind == NONCONST); |
3405 bool get isNullConstant => kind == CONSTANT && constant.isNull; | 3329 bool get isNullConstant => kind == CONSTANT && constant.isNull; |
3406 bool get isTrueConstant => kind == CONSTANT && constant.isTrue; | 3330 bool get isTrueConstant => kind == CONSTANT && constant.isTrue; |
3407 bool get isFalseConstant => kind == CONSTANT && constant.isFalse; | 3331 bool get isFalseConstant => kind == CONSTANT && constant.isFalse; |
3408 bool get isZeroConstant => kind == CONSTANT && constant.isZero; | 3332 bool get isZeroConstant => kind == CONSTANT && constant.isZero; |
3409 bool get isZeroOrNegativeConstant { | 3333 bool get isZeroOrNegativeConstant { |
3410 if (kind != CONSTANT || !constant.isNum) return false; | 3334 if (kind != CONSTANT || !constant.isNum) return false; |
3411 PrimitiveConstantValue value = constant; | 3335 PrimitiveConstantValue value = constant; |
3412 return value.primitiveValue <= 0; | 3336 return value.primitiveValue <= 0; |
3413 } | 3337 } |
| 3338 |
3414 bool get isNegativeConstant { | 3339 bool get isNegativeConstant { |
3415 if (kind != CONSTANT || !constant.isNum) return false; | 3340 if (kind != CONSTANT || !constant.isNum) return false; |
3416 PrimitiveConstantValue value = constant; | 3341 PrimitiveConstantValue value = constant; |
3417 return value.primitiveValue < 0; | 3342 return value.primitiveValue < 0; |
3418 } | 3343 } |
3419 | 3344 |
3420 bool get isNullable => kind != NOTHING && type.isNullable; | 3345 bool get isNullable => kind != NOTHING && type.isNullable; |
3421 bool get isDefinitelyNotNull => kind == NOTHING || !type.isNullable; | 3346 bool get isDefinitelyNotNull => kind == NOTHING || !type.isNullable; |
3422 | 3347 |
3423 int get hashCode { | 3348 int get hashCode { |
3424 int hash = kind * 31 + constant.hashCode * 59 + type.hashCode * 67; | 3349 int hash = kind * 31 + constant.hashCode * 59 + type.hashCode * 67; |
3425 return hash & 0x3fffffff; | 3350 return hash & 0x3fffffff; |
3426 } | 3351 } |
3427 | 3352 |
3428 bool operator ==(AbstractConstantValue that) { | 3353 bool operator ==(AbstractConstantValue that) { |
3429 return that.kind == this.kind && | 3354 return that.kind == this.kind && |
3430 that.constant == this.constant && | 3355 that.constant == this.constant && |
3431 that.type == this.type; | 3356 that.type == this.type; |
3432 } | 3357 } |
3433 | 3358 |
3434 String toString() { | 3359 String toString() { |
3435 switch (kind) { | 3360 switch (kind) { |
3436 case NOTHING: return "Nothing"; | 3361 case NOTHING: |
3437 case CONSTANT: return "Constant: ${constant.unparse()}: $type"; | 3362 return "Nothing"; |
3438 case NONCONST: return "Non-constant: $type"; | 3363 case CONSTANT: |
3439 default: assert(false); | 3364 return "Constant: ${constant.unparse()}: $type"; |
| 3365 case NONCONST: |
| 3366 return "Non-constant: $type"; |
| 3367 default: |
| 3368 assert(false); |
3440 } | 3369 } |
3441 return null; | 3370 return null; |
3442 } | 3371 } |
3443 } | 3372 } |
3444 | 3373 |
3445 /// Suggested name for a synthesized loop index. | 3374 /// Suggested name for a synthesized loop index. |
3446 class LoopIndexEntity extends Entity { | 3375 class LoopIndexEntity extends Entity { |
3447 String get name => 'i'; | 3376 String get name => 'i'; |
3448 } | 3377 } |
3449 | 3378 |
(...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3566 } | 3495 } |
3567 | 3496 |
3568 Primitive makeCheck(CpsFragment cps, Primitive value) { | 3497 Primitive makeCheck(CpsFragment cps, Primitive value) { |
3569 return cps.applyBuiltin(negatedOperator, [value]); | 3498 return cps.applyBuiltin(negatedOperator, [value]); |
3570 } | 3499 } |
3571 | 3500 |
3572 Primitive makeRefinement(CpsFragment cps, Primitive value, World world) { | 3501 Primitive makeRefinement(CpsFragment cps, Primitive value, World world) { |
3573 return cps.refine(value, new TypeMask.nonNullSubclass(classElement, world)); | 3502 return cps.refine(value, new TypeMask.nonNullSubclass(classElement, world)); |
3574 } | 3503 } |
3575 } | 3504 } |
OLD | NEW |