Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(449)

Side by Side Diff: pkg/compiler/lib/src/cps_ir/type_propagation.dart

Issue 1507313006: dart2js cps: Add instruction for null checks. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Remove spurious change Created 5 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
9 ClosureClassElement; 9 ClosureClassElement;
10 import '../common.dart'; 10 import '../common.dart';
(...skipping 665 matching lines...) Expand 10 before | Expand all | Expand 10 after
676 /// reachable method matching the selector may be targeted. 676 /// reachable method matching the selector may be targeted.
677 AbstractConstantValue getInvokeReturnType(Selector selector, TypeMask mask) { 677 AbstractConstantValue getInvokeReturnType(Selector selector, TypeMask mask) {
678 return fromMask(typeSystem.getInvokeReturnType(selector, mask)); 678 return fromMask(typeSystem.getInvokeReturnType(selector, mask));
679 } 679 }
680 680
681 AbstractConstantValue fromMask(TypeMask mask) { 681 AbstractConstantValue fromMask(TypeMask mask) {
682 ConstantValue constantValue = typeSystem.getConstantOf(mask); 682 ConstantValue constantValue = typeSystem.getConstantOf(mask);
683 if (constantValue != null) return constant(constantValue, mask); 683 if (constantValue != null) return constant(constantValue, mask);
684 return nonConstant(mask); 684 return nonConstant(mask);
685 } 685 }
686
687 AbstractConstantValue nonNullable(AbstractConstantValue value) {
688 if (value.isNullConstant) return nothing;
689 if (!value.isNullable) return value;
690 return nonConstant(value.type.nonNullable());
691 }
686 } 692 }
687 693
688 /** 694 /**
689 * Propagates types (including value types for constants) throughout the IR, and 695 * Propagates types (including value types for constants) throughout the IR, and
690 * replaces branches with fixed jumps as well as side-effect free expressions 696 * replaces branches with fixed jumps as well as side-effect free expressions
691 * with known constant results. 697 * with known constant results.
692 * 698 *
693 * Should be followed by the [ShrinkingReducer] pass. 699 * Should be followed by the [ShrinkingReducer] pass.
694 * 700 *
695 * Implemented according to 'Constant Propagation with Conditional Branches' 701 * Implemented according to 'Constant Propagation with Conditional Branches'
(...skipping 385 matching lines...) Expand 10 before | Expand all | Expand 10 after
1081 /// if (typeof x !== 'number') return x.$lt(); 1087 /// if (typeof x !== 'number') return x.$lt();
1082 /// 1088 ///
1083 /// The [guard] must accept all possible non-null values for the receiver, 1089 /// The [guard] must accept all possible non-null values for the receiver,
1084 /// but should be as specific as possible so the VM gets more information 1090 /// but should be as specific as possible so the VM gets more information
1085 /// from the check. 1091 /// from the check.
1086 Primitive guardReceiver(CpsFragment cps, BuiltinOperator guard) { 1092 Primitive guardReceiver(CpsFragment cps, BuiltinOperator guard) {
1087 if (guard == null || getValue(node.dartReceiver).isDefinitelyNotNull) { 1093 if (guard == null || getValue(node.dartReceiver).isDefinitelyNotNull) {
1088 return node.dartReceiver; 1094 return node.dartReceiver;
1089 } 1095 }
1090 if (!trustPrimitives) { 1096 if (!trustPrimitives) {
1097 // TODO(asgerf): Perhaps a separate optimization should decide that
1098 // the guarded check is better based on the type?
sra1 2015/12/11 03:34:06 We should also consider generating something like
asgerf 2015/12/11 10:15:40 I see. NullCheck would certainly not be an approp
1091 Primitive check = cps.applyBuiltin(guard, [node.dartReceiver]); 1099 Primitive check = cps.applyBuiltin(guard, [node.dartReceiver]);
1092 cps.ifFalsy(check) 1100 return cps.letPrim(new NullCheck.guarded(check, node.dartReceiver,
1093 ..invokeMethod(node.dartReceiver, node.selector, 1101 node.selector, node.sourceInformation));
1094 typeSystem.nullType, []) 1102 } else {
1095 ..put(new Unreachable()); 1103 // Refine the receiver to be non-null for use in the operator.
1104 // This restricts code motion and improves the type computed for the
1105 // built-in operator that depends on it.
1106 // This must be done even if trusting primitives.
1107 return cps.letPrim(
1108 new Refinement(node.dartReceiver, typeSystem.nonNullType));
1096 } 1109 }
1097 // Refine the receiver to be non-null for use in the operator.
1098 // This restricts code motion and improves the type computed for the
1099 // built-in operator that depends on it.
1100 // This must be done even if trusting primitives.
1101 Primitive refined = cps.letPrim(
1102 new Refinement(node.dartReceiver, typeSystem.nonNullType));
1103 return refined;
1104 } 1110 }
1105 1111
1106 /// Replaces the call with [operator], using the receiver and first argument 1112 /// Replaces the call with [operator], using the receiver and first argument
1107 /// as operands (in that order). 1113 /// as operands (in that order).
1108 /// 1114 ///
1109 /// If [guard] is given, the receiver is checked using [guardReceiver], 1115 /// If [guard] is given, the receiver is checked using [guardReceiver],
1110 /// unless it is known kot to be null. 1116 /// unless it is known kot to be null.
1111 CpsFragment makeBinary(BuiltinOperator operator, {BuiltinOperator guard}) { 1117 CpsFragment makeBinary(BuiltinOperator operator, {BuiltinOperator guard}) {
1112 CpsFragment cps = new CpsFragment(node.sourceInformation); 1118 CpsFragment cps = new CpsFragment(node.sourceInformation);
1113 Primitive left = guardReceiver(cps, guard); 1119 Primitive left = guardReceiver(cps, guard);
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
1157 } 1163 }
1158 if (behavesLikeIdentical) { 1164 if (behavesLikeIdentical) {
1159 return makeBinary(BuiltinOperator.Identical); 1165 return makeBinary(BuiltinOperator.Identical);
1160 } 1166 }
1161 } else { 1167 } else {
1162 if (lattice.isDefinitelyNum(left, allowNull: true) && 1168 if (lattice.isDefinitelyNum(left, allowNull: true) &&
1163 lattice.isDefinitelyNum(right, allowNull: trustPrimitives)) { 1169 lattice.isDefinitelyNum(right, allowNull: trustPrimitives)) {
1164 // Try to insert a numeric operator. 1170 // Try to insert a numeric operator.
1165 BuiltinOperator operator = NumBinaryBuiltins[opname]; 1171 BuiltinOperator operator = NumBinaryBuiltins[opname];
1166 if (operator != null) { 1172 if (operator != null) {
1167 return makeBinary(operator, guard: BuiltinOperator.IsNumber); 1173 return makeBinary(operator, guard: BuiltinOperator.IsNotNumber);
1168 } 1174 }
1169 // Shift operators are not in [NumBinaryBuiltins] because Dart shifts 1175 // Shift operators are not in [NumBinaryBuiltins] because Dart shifts
1170 // behave different to JS shifts, especially in the handling of the 1176 // behave different to JS shifts, especially in the handling of the
1171 // shift count. 1177 // shift count.
1172 // Try to insert a shift-left operator. 1178 // Try to insert a shift-left operator.
1173 if (opname == '<<' && 1179 if (opname == '<<' &&
1174 lattice.isDefinitelyInt(left, allowNull: true) && 1180 lattice.isDefinitelyInt(left, allowNull: true) &&
1175 lattice.isDefinitelyIntInRange(right, min: 0, max: 31)) { 1181 lattice.isDefinitelyIntInRange(right, min: 0, max: 31)) {
1176 return makeBinary(BuiltinOperator.NumShl, 1182 return makeBinary(BuiltinOperator.NumShl,
1177 guard: BuiltinOperator.IsNumber); 1183 guard: BuiltinOperator.IsNotNumber);
1178 } 1184 }
1179 // Try to insert a shift-right operator. JavaScript's right shift is 1185 // Try to insert a shift-right operator. JavaScript's right shift is
1180 // consistent with Dart's only for left operands in the unsigned 1186 // consistent with Dart's only for left operands in the unsigned
1181 // 32-bit range. 1187 // 32-bit range.
1182 if (opname == '>>' && 1188 if (opname == '>>' &&
1183 lattice.isDefinitelyUint32(left, allowNull: true) && 1189 lattice.isDefinitelyUint32(left, allowNull: true) &&
1184 lattice.isDefinitelyIntInRange(right, min: 0, max: 31)) { 1190 lattice.isDefinitelyIntInRange(right, min: 0, max: 31)) {
1185 return makeBinary(BuiltinOperator.NumShr, 1191 return makeBinary(BuiltinOperator.NumShr,
1186 guard: BuiltinOperator.IsNumber); 1192 guard: BuiltinOperator.IsNotNumber);
1187 } 1193 }
1188 // Try to use remainder for '%'. Both operands must be non-negative 1194 // Try to use remainder for '%'. Both operands must be non-negative
1189 // and the divisor must be non-zero. 1195 // and the divisor must be non-zero.
1190 if (opname == '%' && 1196 if (opname == '%' &&
1191 lattice.isDefinitelyUint(left, allowNull: true) && 1197 lattice.isDefinitelyUint(left, allowNull: true) &&
1192 lattice.isDefinitelyUint(right) && 1198 lattice.isDefinitelyUint(right) &&
1193 lattice.isDefinitelyIntInRange(right, min: 1)) { 1199 lattice.isDefinitelyIntInRange(right, min: 1)) {
1194 return makeBinary(BuiltinOperator.NumRemainder, 1200 return makeBinary(BuiltinOperator.NumRemainder,
1195 guard: BuiltinOperator.IsNumber); 1201 guard: BuiltinOperator.IsNotNumber);
1196 } 1202 }
1197 1203
1198 if (opname == '~/' && 1204 if (opname == '~/' &&
1199 lattice.isDefinitelyUint32(left, allowNull: true) && 1205 lattice.isDefinitelyUint32(left, allowNull: true) &&
1200 lattice.isDefinitelyIntInRange(right, min: 2)) { 1206 lattice.isDefinitelyIntInRange(right, min: 2)) {
1201 return makeBinary(BuiltinOperator.NumTruncatingDivideToSigned32, 1207 return makeBinary(BuiltinOperator.NumTruncatingDivideToSigned32,
1202 guard: BuiltinOperator.IsNumber); 1208 guard: BuiltinOperator.IsNotNumber);
1203 } 1209 }
1204 } 1210 }
1205 if (lattice.isDefinitelyString(left, allowNull: trustPrimitives) && 1211 if (lattice.isDefinitelyString(left, allowNull: trustPrimitives) &&
1206 lattice.isDefinitelyString(right, allowNull: trustPrimitives) && 1212 lattice.isDefinitelyString(right, allowNull: trustPrimitives) &&
1207 opname == '+') { 1213 opname == '+') {
1208 // TODO(asgerf): Add IsString builtin so we can use a guard here. 1214 // TODO(asgerf): Add IsString builtin so we can use a guard here.
1209 return makeBinary(BuiltinOperator.StringConcatenate); 1215 return makeBinary(BuiltinOperator.StringConcatenate);
1210 } 1216 }
1211 } 1217 }
1212 } 1218 }
1213 if (node.selector.isOperator && node.arguments.length == 1) { 1219 if (node.selector.isOperator && node.arguments.length == 1) {
1214 Primitive argument = node.dartReceiver; 1220 Primitive argument = node.dartReceiver;
1215 AbstractConstantValue value = getValue(argument); 1221 AbstractConstantValue value = getValue(argument);
1216 1222
1217 if (lattice.isDefinitelyNum(value, allowNull: true)) { 1223 if (lattice.isDefinitelyNum(value, allowNull: true)) {
1218 String opname = node.selector.name; 1224 String opname = node.selector.name;
1219 if (opname == '~') { 1225 if (opname == '~') {
1220 return makeUnary(BuiltinOperator.NumBitNot, 1226 return makeUnary(BuiltinOperator.NumBitNot,
1221 guard: BuiltinOperator.IsNumber); 1227 guard: BuiltinOperator.IsNotNumber);
1222 } 1228 }
1223 if (opname == 'unary-') { 1229 if (opname == 'unary-') {
1224 return makeUnary(BuiltinOperator.NumNegate, 1230 return makeUnary(BuiltinOperator.NumNegate,
1225 guard: BuiltinOperator.IsNumber); 1231 guard: BuiltinOperator.IsNotNumber);
1226 } 1232 }
1227 } 1233 }
1228 } 1234 }
1229 if (node.selector.isCall) { 1235 if (node.selector.isCall) {
1230 String name = node.selector.name; 1236 String name = node.selector.name;
1231 Primitive receiver = node.dartReceiver; 1237 Primitive receiver = node.dartReceiver;
1232 AbstractConstantValue receiverValue = getValue(receiver); 1238 AbstractConstantValue receiverValue = getValue(receiver);
1233 if (name == 'remainder') { 1239 if (name == 'remainder') {
1234 if (node.arguments.length == 2) { 1240 if (node.arguments.length == 2) {
1235 Primitive arg = node.dartArgument(0); 1241 Primitive arg = node.dartArgument(0);
1236 AbstractConstantValue argValue = getValue(arg); 1242 AbstractConstantValue argValue = getValue(arg);
1237 if (lattice.isDefinitelyInt(receiverValue, allowNull: true) && 1243 if (lattice.isDefinitelyInt(receiverValue, allowNull: true) &&
1238 lattice.isDefinitelyInt(argValue) && 1244 lattice.isDefinitelyInt(argValue) &&
1239 isIntNotZero(argValue)) { 1245 isIntNotZero(argValue)) {
1240 return makeBinary(BuiltinOperator.NumRemainder, 1246 return makeBinary(BuiltinOperator.NumRemainder,
1241 guard: BuiltinOperator.IsNumber); 1247 guard: BuiltinOperator.IsNotNumber);
1242 } 1248 }
1243 } 1249 }
1244 } else if (name == 'codeUnitAt') { 1250 } else if (name == 'codeUnitAt') {
1245 if (node.arguments.length == 2) { 1251 if (node.arguments.length == 2) {
1246 Primitive index = node.dartArgument(0); 1252 Primitive index = node.dartArgument(0);
1247 if (lattice.isDefinitelyString(receiverValue) && 1253 if (lattice.isDefinitelyString(receiverValue) &&
1248 lattice.isDefinitelyInt(getValue(index))) { 1254 lattice.isDefinitelyInt(getValue(index))) {
1249 SourceInformation sourceInfo = node.sourceInformation; 1255 SourceInformation sourceInfo = node.sourceInformation;
1250 CpsFragment cps = makeBoundsCheck(receiver, index, sourceInfo); 1256 CpsFragment cps = makeBoundsCheck(receiver, index, sourceInfo);
1251 ApplyBuiltinOperator get = 1257 ApplyBuiltinOperator get =
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after
1315 Primitive isTooSmall = cps.applyBuiltin( 1321 Primitive isTooSmall = cps.applyBuiltin(
1316 BuiltinOperator.NumLt, 1322 BuiltinOperator.NumLt,
1317 <Primitive>[index, cps.makeZero()]); 1323 <Primitive>[index, cps.makeZero()]);
1318 cps.ifTruthy(isTooSmall).invokeContinuation(fail); 1324 cps.ifTruthy(isTooSmall).invokeContinuation(fail);
1319 } 1325 }
1320 Primitive isTooLarge = cps.applyBuiltin( 1326 Primitive isTooLarge = cps.applyBuiltin(
1321 BuiltinOperator.NumGe, 1327 BuiltinOperator.NumGe,
1322 <Primitive>[index, cps.letPrim(new GetLength(list))]); 1328 <Primitive>[index, cps.letPrim(new GetLength(list))]);
1323 cps.ifTruthy(isTooLarge).invokeContinuation(fail); 1329 cps.ifTruthy(isTooLarge).invokeContinuation(fail);
1324 cps.insideContinuation(fail).invokeStaticThrower( 1330 cps.insideContinuation(fail).invokeStaticThrower(
1325 helpers.throwIndexOutOfBoundsError, 1331 helpers.throwIndexOutOfRangeException,
1326 <Primitive>[list, index]); 1332 <Primitive>[list, index]);
1327 return cps; 1333 return cps;
1328 } 1334 }
1329 1335
1330 /// Create a check that throws if the length of [list] is not equal to 1336 /// Create a check that throws if the length of [list] is not equal to
1331 /// [originalLength]. 1337 /// [originalLength].
1332 /// 1338 ///
1333 /// Returns a CPS fragment whose context is the branch where no error 1339 /// Returns a CPS fragment whose context is the branch where no error
1334 /// was thrown. 1340 /// was thrown.
1335 CpsFragment makeConcurrentModificationCheck(Primitive list, 1341 CpsFragment makeConcurrentModificationCheck(Primitive list,
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after
1430 return null; 1436 return null;
1431 } 1437 }
1432 if (!isExtendable) return null; 1438 if (!isExtendable) return null;
1433 CpsFragment cps = new CpsFragment(sourceInfo); 1439 CpsFragment cps = new CpsFragment(sourceInfo);
1434 Primitive length = cps.letPrim(new GetLength(list)); 1440 Primitive length = cps.letPrim(new GetLength(list));
1435 Primitive isEmpty = cps.applyBuiltin( 1441 Primitive isEmpty = cps.applyBuiltin(
1436 BuiltinOperator.StrictEq, 1442 BuiltinOperator.StrictEq,
1437 [length, cps.makeZero()]); 1443 [length, cps.makeZero()]);
1438 CpsFragment fail = cps.ifTruthy(isEmpty); 1444 CpsFragment fail = cps.ifTruthy(isEmpty);
1439 fail.invokeStaticThrower( 1445 fail.invokeStaticThrower(
1440 helpers.throwIndexOutOfBoundsError, 1446 helpers.throwIndexOutOfRangeException,
1441 [list, fail.makeConstant(new IntConstantValue(-1))]); 1447 [list, fail.makeConstant(new IntConstantValue(-1))]);
1442 Primitive removedItem = cps.invokeBuiltin(BuiltinMethod.Pop, 1448 Primitive removedItem = cps.invokeBuiltin(BuiltinMethod.Pop,
1443 list, 1449 list,
1444 <Primitive>[]); 1450 <Primitive>[]);
1445 removedItem.hint = node.hint; 1451 removedItem.hint = node.hint;
1446 node.replaceUsesWith(removedItem); 1452 node.replaceUsesWith(removedItem);
1447 return cps; 1453 return cps;
1448 1454
1449 case 'addAll': 1455 case 'addAll':
1450 if (!node.selector.isCall || 1456 if (!node.selector.isCall ||
(...skipping 939 matching lines...) Expand 10 before | Expand all | Expand 10 after
2390 functionCompiler.glue.getInterceptedClassesOn(invoke.selector); 2396 functionCompiler.glue.getInterceptedClassesOn(invoke.selector);
2391 if (interceptedClasses.contains(helpers.jsDoubleClass)) return false; 2397 if (interceptedClasses.contains(helpers.jsDoubleClass)) return false;
2392 if (interceptedClasses.contains(helpers.jsIntClass)) return false; 2398 if (interceptedClasses.contains(helpers.jsIntClass)) return false;
2393 continue; 2399 continue;
2394 } 2400 }
2395 // Other uses need full distinction. 2401 // Other uses need full distinction.
2396 return false; 2402 return false;
2397 } 2403 }
2398 return true; 2404 return true;
2399 } 2405 }
2406
2407 visitNullCheck(NullCheck node) {
2408 if (!getValue(node.value.definition).isNullable) {
2409 node.replaceUsesWith(node.value.definition);
2410 return new CpsFragment();
2411 }
2412 return null;
2413 }
2400 } 2414 }
2401 2415
2402 /** 2416 /**
2403 * Runs an analysis pass on the given function definition in order to detect 2417 * Runs an analysis pass on the given function definition in order to detect
2404 * const-ness as well as reachability, both of which are used in the subsequent 2418 * const-ness as well as reachability, both of which are used in the subsequent
2405 * transformation pass. 2419 * transformation pass.
2406 */ 2420 */
2407 class TypePropagationVisitor implements Visitor { 2421 class TypePropagationVisitor implements Visitor {
2408 // The node worklist stores nodes that are both reachable and need to be 2422 // The node worklist stores nodes that are both reachable and need to be
2409 // processed, but have not been processed yet. Using a worklist avoids deep 2423 // processed, but have not been processed yet. Using a worklist avoids deep
(...skipping 767 matching lines...) Expand 10 before | Expand all | Expand 10 after
3177 if (value.isNothing || 3191 if (value.isNothing ||
3178 typeSystem.areDisjoint(value.type, node.refineType)) { 3192 typeSystem.areDisjoint(value.type, node.refineType)) {
3179 setValue(node, nothing); 3193 setValue(node, nothing);
3180 } else if (value.isConstant) { 3194 } else if (value.isConstant) {
3181 setValue(node, value); 3195 setValue(node, value);
3182 } else { 3196 } else {
3183 setValue(node, 3197 setValue(node,
3184 nonConstant(value.type.intersection(node.refineType, classWorld))); 3198 nonConstant(value.type.intersection(node.refineType, classWorld)));
3185 } 3199 }
3186 } 3200 }
3201
3202 @override
3203 void visitNullCheck(NullCheck node) {
3204 setValue(node, lattice.nonNullable(getValue(node.value.definition)));
3205 }
3187 } 3206 }
3188 3207
3189 /// Represents the abstract value of a primitive value at some point in the 3208 /// Represents the abstract value of a primitive value at some point in the
3190 /// program. Abstract values of all kinds have a type [T]. 3209 /// program. Abstract values of all kinds have a type [T].
3191 /// 3210 ///
3192 /// The different kinds of abstract values represents the knowledge about the 3211 /// The different kinds of abstract values represents the knowledge about the
3193 /// constness of the value: 3212 /// constness of the value:
3194 /// NOTHING: cannot have any value 3213 /// NOTHING: cannot have any value
3195 /// CONSTANT: is a constant. The value is stored in the [constant] field, 3214 /// CONSTANT: is a constant. The value is stored in the [constant] field,
3196 /// and the type of the constant is in the [type] field. 3215 /// and the type of the constant is in the [type] field.
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after
3289 processLetPrim(LetPrim node) { 3308 processLetPrim(LetPrim node) {
3290 node.primitive.type = null; 3309 node.primitive.type = null;
3291 values[node.primitive] = null; 3310 values[node.primitive] = null;
3292 } 3311 }
3293 3312
3294 processLetMutable(LetMutable node) { 3313 processLetMutable(LetMutable node) {
3295 node.variable.type = null; 3314 node.variable.type = null;
3296 values[node.variable] = null; 3315 values[node.variable] = null;
3297 } 3316 }
3298 } 3317 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698