| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 part of ssa; | 5 part of ssa; |
| 6 | 6 |
| 7 class SsaCodeGeneratorTask extends CompilerTask { | 7 class SsaCodeGeneratorTask extends CompilerTask { |
| 8 | 8 |
| 9 final JavaScriptBackend backend; | 9 final JavaScriptBackend backend; |
| 10 | 10 |
| (...skipping 237 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 248 | 248 |
| 249 js.Node attachLocationRange(js.Node jsNode, | 249 js.Node attachLocationRange(js.Node jsNode, |
| 250 SourceFileLocation sourcePosition, | 250 SourceFileLocation sourcePosition, |
| 251 SourceFileLocation endSourcePosition) { | 251 SourceFileLocation endSourcePosition) { |
| 252 jsNode.sourcePosition = sourcePosition; | 252 jsNode.sourcePosition = sourcePosition; |
| 253 jsNode.endSourcePosition = endSourcePosition; | 253 jsNode.endSourcePosition = endSourcePosition; |
| 254 return jsNode; | 254 return jsNode; |
| 255 } | 255 } |
| 256 | 256 |
| 257 void preGenerateMethod(HGraph graph) { | 257 void preGenerateMethod(HGraph graph) { |
| 258 new SsaInstructionSelection(compiler).visitGraph(graph); |
| 258 new SsaTypeKnownRemover().visitGraph(graph); | 259 new SsaTypeKnownRemover().visitGraph(graph); |
| 259 new SsaInstructionMerger(generateAtUseSite, compiler).visitGraph(graph); | 260 new SsaInstructionMerger(generateAtUseSite, compiler).visitGraph(graph); |
| 260 new SsaConditionMerger( | 261 new SsaConditionMerger( |
| 261 generateAtUseSite, controlFlowOperators).visitGraph(graph); | 262 generateAtUseSite, controlFlowOperators).visitGraph(graph); |
| 262 SsaLiveIntervalBuilder intervalBuilder = new SsaLiveIntervalBuilder( | 263 SsaLiveIntervalBuilder intervalBuilder = new SsaLiveIntervalBuilder( |
| 263 compiler, generateAtUseSite, controlFlowOperators); | 264 compiler, generateAtUseSite, controlFlowOperators); |
| 264 intervalBuilder.visitGraph(graph); | 265 intervalBuilder.visitGraph(graph); |
| 265 SsaVariableAllocator allocator = new SsaVariableAllocator( | 266 SsaVariableAllocator allocator = new SsaVariableAllocator( |
| 266 compiler, | 267 compiler, |
| 267 intervalBuilder.liveInstructions, | 268 intervalBuilder.liveInstructions, |
| (...skipping 924 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1192 | 1193 |
| 1193 // We want the outcome of bit-operations to be positive. We use the unsigned | 1194 // We want the outcome of bit-operations to be positive. We use the unsigned |
| 1194 // shift operator to achieve this. | 1195 // shift operator to achieve this. |
| 1195 visitBitInvokeUnary(HInvokeUnary node, String op) { | 1196 visitBitInvokeUnary(HInvokeUnary node, String op) { |
| 1196 visitInvokeUnary(node, op); | 1197 visitInvokeUnary(node, op); |
| 1197 if (requiresUintConversion(node)) { | 1198 if (requiresUintConversion(node)) { |
| 1198 push(new js.Binary(">>>", pop(), new js.LiteralNumber("0")), node); | 1199 push(new js.Binary(">>>", pop(), new js.LiteralNumber("0")), node); |
| 1199 } | 1200 } |
| 1200 } | 1201 } |
| 1201 | 1202 |
| 1202 void emitIdentityComparison(HInstruction left, | 1203 void emitIdentityComparison(HIdentity instruction, bool inverse) { |
| 1203 HInstruction right, | 1204 String op = instruction.singleComparisonOp; |
| 1204 bool inverse) { | 1205 HInstruction left = instruction.left; |
| 1205 String op = singleIdentityComparison(left, right, compiler); | 1206 HInstruction right = instruction.right; |
| 1206 if (op != null) { | 1207 if (op != null) { |
| 1207 use(left); | 1208 use(left); |
| 1208 js.Expression jsLeft = pop(); | 1209 js.Expression jsLeft = pop(); |
| 1209 use(right); | 1210 use(right); |
| 1210 push(new js.Binary(mapRelationalOperator(op, inverse), jsLeft, pop())); | 1211 push(new js.Binary(mapRelationalOperator(op, inverse), jsLeft, pop())); |
| 1211 } else { | 1212 } else { |
| 1212 assert(NullConstant.JsNull == 'null'); | 1213 assert(NullConstant.JsNull == 'null'); |
| 1213 use(left); | 1214 use(left); |
| 1214 js.Binary leftEqualsNull = | 1215 js.Binary leftEqualsNull = |
| 1215 new js.Binary("==", pop(), new js.LiteralNull()); | 1216 new js.Binary("==", pop(), new js.LiteralNull()); |
| 1216 use(right); | 1217 use(right); |
| 1217 js.Binary rightEqualsNull = | 1218 js.Binary rightEqualsNull = |
| 1218 new js.Binary(mapRelationalOperator("==", inverse), | 1219 new js.Binary(mapRelationalOperator("==", inverse), |
| 1219 pop(), new js.LiteralNull()); | 1220 pop(), new js.LiteralNull()); |
| 1220 use(right); | 1221 use(right); |
| 1221 use(left); | 1222 use(left); |
| 1222 js.Binary tripleEq = new js.Binary(mapRelationalOperator("===", inverse), | 1223 js.Binary tripleEq = new js.Binary(mapRelationalOperator("===", inverse), |
| 1223 pop(), pop()); | 1224 pop(), pop()); |
| 1224 | 1225 |
| 1225 push(new js.Conditional(leftEqualsNull, rightEqualsNull, tripleEq)); | 1226 push(new js.Conditional(leftEqualsNull, rightEqualsNull, tripleEq)); |
| 1226 } | 1227 } |
| 1227 } | 1228 } |
| 1228 | 1229 |
| 1229 visitIdentity(HIdentity node) { | 1230 visitIdentity(HIdentity node) { |
| 1230 emitIdentityComparison(node.left, node.right, false); | 1231 emitIdentityComparison(node, false); |
| 1231 } | 1232 } |
| 1232 | 1233 |
| 1233 visitAdd(HAdd node) => visitInvokeBinary(node, '+'); | 1234 visitAdd(HAdd node) => visitInvokeBinary(node, '+'); |
| 1234 visitDivide(HDivide node) => visitInvokeBinary(node, '/'); | 1235 visitDivide(HDivide node) => visitInvokeBinary(node, '/'); |
| 1235 visitMultiply(HMultiply node) => visitInvokeBinary(node, '*'); | 1236 visitMultiply(HMultiply node) => visitInvokeBinary(node, '*'); |
| 1236 visitSubtract(HSubtract node) => visitInvokeBinary(node, '-'); | 1237 visitSubtract(HSubtract node) => visitInvokeBinary(node, '-'); |
| 1237 visitBitAnd(HBitAnd node) => visitBitInvokeBinary(node, '&'); | 1238 visitBitAnd(HBitAnd node) => visitBitInvokeBinary(node, '&'); |
| 1238 visitBitNot(HBitNot node) => visitBitInvokeUnary(node, '~'); | 1239 visitBitNot(HBitNot node) => visitBitInvokeUnary(node, '~'); |
| 1239 visitBitOr(HBitOr node) => visitBitInvokeBinary(node, '|'); | 1240 visitBitOr(HBitOr node) => visitBitInvokeBinary(node, '|'); |
| 1240 visitBitXor(HBitXor node) => visitBitInvokeBinary(node, '^'); | 1241 visitBitXor(HBitXor node) => visitBitInvokeBinary(node, '^'); |
| (...skipping 564 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1805 // This optimization doesn't work for NaN, so we only do it if the | 1806 // This optimization doesn't work for NaN, so we only do it if the |
| 1806 // type is known to be an integer. | 1807 // type is known to be an integer. |
| 1807 return left.isInteger(compiler) && right.isInteger(compiler); | 1808 return left.isInteger(compiler) && right.isInteger(compiler); |
| 1808 } | 1809 } |
| 1809 | 1810 |
| 1810 bool handledBySpecialCase = false; | 1811 bool handledBySpecialCase = false; |
| 1811 if (isGenerateAtUseSite(input)) { | 1812 if (isGenerateAtUseSite(input)) { |
| 1812 handledBySpecialCase = true; | 1813 handledBySpecialCase = true; |
| 1813 if (input is HIs) { | 1814 if (input is HIs) { |
| 1814 emitIs(input, '!=='); | 1815 emitIs(input, '!=='); |
| 1816 } else if (input is HIsViaInterceptor) { |
| 1817 emitIsViaInterceptor(input, true); |
| 1815 } else if (input is HNot) { | 1818 } else if (input is HNot) { |
| 1816 use(input.inputs[0]); | 1819 use(input.inputs[0]); |
| 1817 } else if (input is HIdentity) { | 1820 } else if (input is HIdentity) { |
| 1818 HIdentity identity = input; | 1821 emitIdentityComparison(input, true); |
| 1819 emitIdentityComparison(identity.left, identity.right, true); | |
| 1820 } else if (input is HBoolify) { | 1822 } else if (input is HBoolify) { |
| 1821 use(input.inputs[0]); | 1823 use(input.inputs[0]); |
| 1822 push(new js.Binary("!==", pop(), newLiteralBool(true)), input); | 1824 push(new js.Binary("!==", pop(), newLiteralBool(true)), input); |
| 1823 } else if (canGenerateOptimizedComparison(input)) { | 1825 } else if (canGenerateOptimizedComparison(input)) { |
| 1824 HRelational relational = input; | 1826 HRelational relational = input; |
| 1825 BinaryOperation operation = | 1827 BinaryOperation operation = |
| 1826 relational.operation(backend.constantSystem); | 1828 relational.operation(backend.constantSystem); |
| 1827 String op = mapRelationalOperator(operation.name, true); | 1829 String op = mapRelationalOperator(operation.name, true); |
| 1828 visitRelational(input, op); | 1830 visitRelational(input, op); |
| 1829 } else { | 1831 } else { |
| (...skipping 403 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2233 return; | 2235 return; |
| 2234 } else if (element == backend.jsFixedArrayClass) { | 2236 } else if (element == backend.jsFixedArrayClass) { |
| 2235 if (negative) { | 2237 if (negative) { |
| 2236 checkExtendableArray(input); | 2238 checkExtendableArray(input); |
| 2237 } else { | 2239 } else { |
| 2238 checkFixedArray(input); | 2240 checkFixedArray(input); |
| 2239 } | 2241 } |
| 2240 return; | 2242 return; |
| 2241 } | 2243 } |
| 2242 if (interceptor != null) { | 2244 if (interceptor != null) { |
| 2243 use(interceptor); | 2245 checkTypeViaProperty(interceptor, type, negative); |
| 2244 } else { | 2246 } else { |
| 2245 use(input); | 2247 checkTypeViaProperty(input, type, negative); |
| 2246 } | 2248 } |
| 2249 } |
| 2247 | 2250 |
| 2251 void checkTypeViaProperty(HInstruction input, DartType type, bool negative) { |
| 2248 world.registerIsCheck(type, work.resolutionTree); | 2252 world.registerIsCheck(type, work.resolutionTree); |
| 2249 | 2253 |
| 2254 use(input); |
| 2255 |
| 2250 js.PropertyAccess field = | 2256 js.PropertyAccess field = |
| 2251 new js.PropertyAccess.field(pop(), backend.namer.operatorIsType(type)); | 2257 new js.PropertyAccess.field(pop(), backend.namer.operatorIsType(type)); |
| 2252 // We always negate at least once so that the result is boolified. | 2258 // We always negate at least once so that the result is boolified. |
| 2253 push(new js.Prefix('!', field)); | 2259 push(new js.Prefix('!', field)); |
| 2254 // If the result is not negated, put another '!' in front. | 2260 // If the result is not negated, put another '!' in front. |
| 2255 if (!negative) push(new js.Prefix('!', pop())); | 2261 if (!negative) push(new js.Prefix('!', pop())); |
| 2256 } | 2262 } |
| 2257 | 2263 |
| 2258 void handleNumberOrStringSupertypeCheck(HInstruction input, | 2264 void handleNumberOrStringSupertypeCheck(HInstruction input, |
| 2259 HInstruction interceptor, | 2265 HInstruction interceptor, |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2312 String combiner = negative ? '&&' : '||'; | 2318 String combiner = negative ? '&&' : '||'; |
| 2313 push(new js.Binary(negative ? '||' : '&&', | 2319 push(new js.Binary(negative ? '||' : '&&', |
| 2314 objectTest, | 2320 objectTest, |
| 2315 new js.Binary(combiner, arrayTest, pop()))); | 2321 new js.Binary(combiner, arrayTest, pop()))); |
| 2316 } | 2322 } |
| 2317 | 2323 |
| 2318 void visitIs(HIs node) { | 2324 void visitIs(HIs node) { |
| 2319 emitIs(node, "==="); | 2325 emitIs(node, "==="); |
| 2320 } | 2326 } |
| 2321 | 2327 |
| 2328 void visitIsViaInterceptor(HIsViaInterceptor node) { |
| 2329 emitIsViaInterceptor(node, false); |
| 2330 } |
| 2331 |
| 2322 void emitIs(HIs node, String relation) { | 2332 void emitIs(HIs node, String relation) { |
| 2323 DartType type = node.typeExpression; | 2333 DartType type = node.typeExpression; |
| 2324 world.registerIsCheck(type, work.resolutionTree); | 2334 world.registerIsCheck(type, work.resolutionTree); |
| 2325 HInstruction input = node.expression; | 2335 HInstruction input = node.expression; |
| 2326 | 2336 |
| 2327 // If this is changed to single == there are several places below that must | 2337 // If this is changed to single == there are several places below that must |
| 2328 // be changed to match. | 2338 // be changed to match. |
| 2329 assert(relation == '===' || relation == '!=='); | 2339 assert(relation == '===' || relation == '!=='); |
| 2330 bool negative = relation == '!=='; | 2340 bool negative = relation == '!=='; |
| 2331 | 2341 |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2391 js.Expression objectTest = pop(); | 2401 js.Expression objectTest = pop(); |
| 2392 checkType(input, interceptor, type, negative: negative); | 2402 checkType(input, interceptor, type, negative: negative); |
| 2393 push(new js.Binary(negative ? '||' : '&&', objectTest, pop()), node); | 2403 push(new js.Binary(negative ? '||' : '&&', objectTest, pop()), node); |
| 2394 } else { | 2404 } else { |
| 2395 checkType(input, interceptor, type, negative: negative); | 2405 checkType(input, interceptor, type, negative: negative); |
| 2396 attachLocationToLast(node); | 2406 attachLocationToLast(node); |
| 2397 } | 2407 } |
| 2398 } | 2408 } |
| 2399 } | 2409 } |
| 2400 | 2410 |
| 2411 void emitIsViaInterceptor(HIsViaInterceptor node, bool negative) { |
| 2412 checkTypeViaProperty(node.interceptor, node.typeExpression, negative); |
| 2413 attachLocationToLast(node); |
| 2414 } |
| 2415 |
| 2401 js.Expression generateTest(HInstruction input, TypeMask checkedType) { | 2416 js.Expression generateTest(HInstruction input, TypeMask checkedType) { |
| 2402 TypeMask receiver = input.instructionType; | 2417 TypeMask receiver = input.instructionType; |
| 2403 // Figure out if it is beneficial to turn this into a null check. | 2418 // Figure out if it is beneficial to turn this into a null check. |
| 2404 // V8 generally prefers 'typeof' checks, but for integers and | 2419 // V8 generally prefers 'typeof' checks, but for integers and |
| 2405 // indexable primitives we cannot compile this test into a single | 2420 // indexable primitives we cannot compile this test into a single |
| 2406 // typeof check so the null check is cheaper. | 2421 // typeof check so the null check is cheaper. |
| 2407 bool turnIntoNumCheck = input.isIntegerOrNull(compiler) | 2422 bool turnIntoNumCheck = input.isIntegerOrNull(compiler) |
| 2408 && checkedType.containsOnlyInt(compiler); | 2423 && checkedType.containsOnlyInt(compiler); |
| 2409 bool turnIntoNullCheck = !turnIntoNumCheck | 2424 bool turnIntoNullCheck = !turnIntoNumCheck |
| 2410 && (checkedType.nullable() == receiver) | 2425 && (checkedType.nullable() == receiver) |
| (...skipping 233 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2644 js.PropertyAccess accessHelper(String name) { | 2659 js.PropertyAccess accessHelper(String name) { |
| 2645 Element helper = compiler.findHelper(name); | 2660 Element helper = compiler.findHelper(name); |
| 2646 if (helper == null) { | 2661 if (helper == null) { |
| 2647 // For mocked-up tests. | 2662 // For mocked-up tests. |
| 2648 return js.js('(void 0).$name'); | 2663 return js.js('(void 0).$name'); |
| 2649 } | 2664 } |
| 2650 world.registerStaticUse(helper); | 2665 world.registerStaticUse(helper); |
| 2651 return backend.namer.elementAccess(helper); | 2666 return backend.namer.elementAccess(helper); |
| 2652 } | 2667 } |
| 2653 } | 2668 } |
| 2654 | |
| 2655 String singleIdentityComparison(HInstruction left, | |
| 2656 HInstruction right, | |
| 2657 Compiler compiler) { | |
| 2658 // Returns the single identity comparison (== or ===) or null if a more | |
| 2659 // complex expression is required. | |
| 2660 if (left.canBeNull() && right.canBeNull()) { | |
| 2661 if (left.isConstantNull() || right.isConstantNull() || | |
| 2662 (left.isPrimitive(compiler) && | |
| 2663 left.instructionType == right.instructionType)) { | |
| 2664 return '=='; | |
| 2665 } | |
| 2666 return null; | |
| 2667 } else { | |
| 2668 return '==='; | |
| 2669 } | |
| 2670 } | |
| OLD | NEW |