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

Side by Side Diff: sdk/lib/_internal/compiler/implementation/ssa/optimize.dart

Issue 12087101: Turn getters and setters that we know are not intercepted into regular getter and setter calls. (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 7 years, 10 months 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 | Annotate | Revision Log
« no previous file with comments | « no previous file | tests/compiler/dart2js/interceptor_test.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 abstract class OptimizationPhase { 7 abstract class OptimizationPhase {
8 String get name; 8 String get name;
9 void visitGraph(HGraph graph); 9 void visitGraph(HGraph graph);
10 } 10 }
(...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after
138 void visitGraph(HGraph visitee) { 138 void visitGraph(HGraph visitee) {
139 graph = visitee; 139 graph = visitee;
140 visitDominatorTree(visitee); 140 visitDominatorTree(visitee);
141 } 141 }
142 142
143 visitBasicBlock(HBasicBlock block) { 143 visitBasicBlock(HBasicBlock block) {
144 HInstruction instruction = block.first; 144 HInstruction instruction = block.first;
145 while (instruction != null) { 145 while (instruction != null) {
146 HInstruction next = instruction.next; 146 HInstruction next = instruction.next;
147 HInstruction replacement = instruction.accept(this); 147 HInstruction replacement = instruction.accept(this);
148 if (!identical(replacement, instruction)) { 148 if (replacement != instruction) {
149 if (!replacement.isInBasicBlock()) {
150 // The constant folding can return an instruction that is already
151 // part of the graph (like an input), so we only add the replacement
152 // if necessary.
153 block.addAfter(instruction, replacement);
154 }
155 block.rewrite(instruction, replacement); 149 block.rewrite(instruction, replacement);
156 block.remove(instruction);
157 150
158 // If we can replace [instruction] with [replacement], then 151 // If we can replace [instruction] with [replacement], then
159 // [replacement]'s type can be narrowed. 152 // [replacement]'s type can be narrowed.
160 types[replacement] = 153 types[replacement] =
161 types[replacement].intersection(types[instruction], compiler); 154 types[replacement].intersection(types[instruction], compiler);
162 155
163 // If the replacement instruction does not know its 156 // If the replacement instruction does not know its
164 // source element, use the source element of the 157 // source element, use the source element of the
165 // instruction. 158 // instruction.
166 if (replacement.sourceElement == null) { 159 if (replacement.sourceElement == null) {
167 replacement.sourceElement = instruction.sourceElement; 160 replacement.sourceElement = instruction.sourceElement;
168 } 161 }
169 if (replacement.sourcePosition == null) { 162 if (replacement.sourcePosition == null) {
170 replacement.sourcePosition = instruction.sourcePosition; 163 replacement.sourcePosition = instruction.sourcePosition;
171 } 164 }
165 if (!replacement.isInBasicBlock()) {
166 // The constant folding can return an instruction that is already
167 // part of the graph (like an input), so we only add the replacement
168 // if necessary.
169 block.addAfter(instruction, replacement);
170 // Visit the replacement as the next instruction in case it
171 // can also be constant folded away.
172 next = replacement;
173 }
174 block.remove(instruction);
172 } 175 }
173 instruction = next; 176 instruction = next;
174 } 177 }
175 } 178 }
176 179
177 HInstruction visitInstruction(HInstruction node) { 180 HInstruction visitInstruction(HInstruction node) {
178 return node; 181 return node;
179 } 182 }
180 183
181 HInstruction visitBoolify(HBoolify node) { 184 HInstruction visitBoolify(HBoolify node) {
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
213 216
214 HInstruction foldUnary(UnaryOperation operation, HInstruction operand) { 217 HInstruction foldUnary(UnaryOperation operation, HInstruction operand) {
215 if (operand is HConstant) { 218 if (operand is HConstant) {
216 HConstant receiver = operand; 219 HConstant receiver = operand;
217 Constant folded = operation.fold(receiver.constant); 220 Constant folded = operation.fold(receiver.constant);
218 if (folded != null) return graph.addConstant(folded); 221 if (folded != null) return graph.addConstant(folded);
219 } 222 }
220 return null; 223 return null;
221 } 224 }
222 225
226 HInstruction optimizeLengthInterceptedGetter(HInvokeDynamic node) {
227 HInstruction actualReceiver = node.inputs[1];
228 if (actualReceiver.isIndexablePrimitive(types)) {
229 if (actualReceiver.isConstantString()) {
230 HConstant constantInput = actualReceiver;
231 StringConstant constant = constantInput.constant;
232 return graph.addConstantInt(constant.length, constantSystem);
233 } else if (actualReceiver.isConstantList()) {
234 HConstant constantInput = actualReceiver;
235 ListConstant constant = constantInput.constant;
236 return graph.addConstantInt(constant.length, constantSystem);
237 }
238 Element element;
239 bool isAssignable;
240 if (actualReceiver.isString(types)) {
241 element = backend.jsStringLength;
242 isAssignable = false;
243 } else {
244 element = backend.jsArrayLength;
245 isAssignable = !actualReceiver.isFixedArray(types);
246 }
247 HFieldGet result = new HFieldGet(
248 element, actualReceiver, isAssignable: isAssignable);
249 result.guaranteedType = HType.INTEGER;
250 types[result] = HType.INTEGER;
251 return result;
252 } else if (actualReceiver.isConstantMap()) {
253 HConstant constantInput = actualReceiver;
254 MapConstant constant = constantInput.constant;
255 return graph.addConstantInt(constant.length, constantSystem);
256 }
257 return node;
258 }
259
223 HInstruction handleInterceptorCall(HInvokeDynamic node) { 260 HInstruction handleInterceptorCall(HInvokeDynamic node) {
224 // We only optimize for intercepted method calls in this method. 261 // We only optimize for intercepted method calls in this method.
225 if (node.selector.isGetter() || node.selector.isSetter()) return node; 262 Selector selector = node.selector;
226
227 HInstruction input = node.inputs[1];
228 if (input.isString(types)
229 && node.selector.name == const SourceString('toString')) {
230 return node.inputs[1];
231 }
232 263
233 // Try constant folding the instruction. 264 // Try constant folding the instruction.
234 Operation operation = node.specializer.operation(constantSystem); 265 Operation operation = node.specializer.operation(constantSystem);
235 if (operation != null) { 266 if (operation != null) {
236 HInstruction instruction = node.inputs.length == 2 267 HInstruction instruction = node.inputs.length == 2
237 ? foldUnary(operation, node.inputs[1]) 268 ? foldUnary(operation, node.inputs[1])
238 : foldBinary(operation, node.inputs[1], node.inputs[2]); 269 : foldBinary(operation, node.inputs[1], node.inputs[2]);
239 if (instruction != null) return instruction; 270 if (instruction != null) return instruction;
240 } 271 }
241 272
242 // Try converting the instruction to a builtin instruction. 273 // Try converting the instruction to a builtin instruction.
243 HInstruction instruction = 274 HInstruction instruction =
244 node.specializer.tryConvertToBuiltin(node, types); 275 node.specializer.tryConvertToBuiltin(node, types);
245 if (instruction != null) return instruction; 276 if (instruction != null) return instruction;
246 277
247 // Check if this call does not need to be intercepted. 278 // Check if this call does not need to be intercepted.
279 HInstruction input = node.inputs[1];
248 HType type = types[input]; 280 HType type = types[input];
249 var interceptor = node.inputs[0]; 281 var interceptor = node.inputs[0];
250 if (interceptor is !HThis && !type.canBePrimitive()) { 282 if (interceptor is !HThis && !type.canBePrimitive()) {
251 // If the type can be null, and the intercepted method can be in 283 // If the type can be null, and the intercepted method can be in
252 // the object class, keep the interceptor. 284 // the object class, keep the interceptor.
253 if (type.canBeNull()) { 285 if (type.canBeNull()) {
254 Set<ClassElement> interceptedClasses; 286 Set<ClassElement> interceptedClasses;
255 if (interceptor is HInterceptor) { 287 if (interceptor is HInterceptor) {
256 interceptedClasses = interceptor.interceptedClasses; 288 interceptedClasses = interceptor.interceptedClasses;
257 } else if (node is HOneShotInterceptor) { 289 } else if (node is HOneShotInterceptor) {
258 var oneShotInterceptor = node; 290 var oneShotInterceptor = node;
259 interceptedClasses = oneShotInterceptor.interceptedClasses; 291 interceptedClasses = oneShotInterceptor.interceptedClasses;
260 } 292 }
261 if (interceptedClasses.contains(compiler.objectClass)) return node; 293 if (interceptedClasses.contains(compiler.objectClass)) return node;
262 } 294 }
263 // Change the call to a regular invoke dynamic call. 295 if (selector.isGetter()) {
264 return new HInvokeDynamicMethod( 296 // Change the call to a regular invoke dynamic call.
265 node.selector, node.inputs.getRange(1, node.inputs.length - 1)); 297 return new HInvokeDynamicGetter(selector, null, input, false);
298 } else if (selector.isSetter()) {
299 return new HInvokeDynamicSetter(
300 selector, null, input, node.inputs[2], false);
301 } else {
302 // Change the call to a regular invoke dynamic call.
303 return new HInvokeDynamicMethod(
304 selector, node.inputs.getRange(1, node.inputs.length - 1));
305 }
266 } 306 }
267 307
268 Selector selector = node.selector;
269 SourceString selectorName = selector.name; 308 SourceString selectorName = selector.name;
270 Element target; 309 if (selector.isCall()) {
271 if (input.isExtendableArray(types)) { 310 Element target;
272 if (selectorName == backend.jsArrayRemoveLast.name 311 if (input.isExtendableArray(types)) {
273 && selector.argumentCount == 0) { 312 if (selectorName == backend.jsArrayRemoveLast.name
274 target = backend.jsArrayRemoveLast; 313 && selector.argumentCount == 0) {
275 } else if (selectorName == backend.jsArrayAdd.name 314 target = backend.jsArrayRemoveLast;
276 && selector.argumentCount == 1 315 } else if (selectorName == backend.jsArrayAdd.name
kasperl 2013/02/01 16:29:51 Maybe we could store some object in the backend th
ngeoffray 2013/02/04 13:31:30 Actually, we're kind of doing a Selector.applies c
277 && selector.namedArgumentCount == 0 316 && selector.argumentCount == 1
278 && !compiler.enableTypeAssertions) { 317 && selector.namedArgumentCount == 0
279 target = backend.jsArrayAdd; 318 && !compiler.enableTypeAssertions) {
319 target = backend.jsArrayAdd;
320 }
321 } else if (input.isString(types)) {
322 if (selectorName == backend.jsStringSplit.name
323 && selector.argumentCount == 1
324 && selector.namedArgumentCount == 0
325 && node.inputs[2].isString(types)) {
326 target = backend.jsStringSplit;
327 } else if (selectorName == backend.jsStringConcat.name
328 && selector.argumentCount == 1
329 && selector.namedArgumentCount == 0
330 && node.inputs[2].isString(types)) {
331 target = backend.jsStringConcat;
332 } else if (selectorName == const SourceString('toString')
333 && selector.argumentCount == 0) {
334 return node.inputs[1];
335 }
280 } 336 }
281 } else if (input.isString(types)) { 337 if (target != null) {
282 if (selectorName == backend.jsStringSplit.name 338 HInvokeDynamicMethod result = new HInvokeDynamicMethod(
283 && selector.argumentCount == 1 339 node.selector, node.inputs.getRange(1, node.inputs.length - 1));
284 && selector.namedArgumentCount == 0 340 result.element = target;
285 && node.inputs[2].isString(types)) { 341 return result;
286 target = backend.jsStringSplit;
287 } else if (selectorName == backend.jsStringConcat.name
288 && selector.argumentCount == 1
289 && selector.namedArgumentCount == 0
290 && node.inputs[2].isString(types)) {
291 target = backend.jsStringConcat;
292 } 342 }
293 } 343 } else if (selector.isGetter()
294 if (target != null) { 344 && selectorName == const SourceString("length")) {
295 HInvokeDynamicMethod result = new HInvokeDynamicMethod( 345 return optimizeLengthInterceptedGetter(node);
296 node.selector, node.inputs.getRange(1, node.inputs.length - 1));
297 result.element = target;
298 return result;
299 } 346 }
300 return node; 347 return node;
301 } 348 }
302 349
303 bool isFixedSizeListConstructor(HInvokeStatic node) { 350 bool isFixedSizeListConstructor(HInvokeStatic node) {
304 Element element = node.target.element; 351 Element element = node.target.element;
305 if (backend.fixedLengthListConstructor == null) { 352 if (backend.fixedLengthListConstructor == null) {
306 backend.fixedLengthListConstructor = 353 backend.fixedLengthListConstructor =
307 compiler.listClass.lookupConstructor( 354 compiler.listClass.lookupConstructor(
308 new Selector.callConstructor(const SourceString("fixedLength"), 355 new Selector.callConstructor(const SourceString("fixedLength"),
(...skipping 29 matching lines...) Expand all
338 node.element = element; 385 node.element = element;
339 } 386 }
340 // TODO(ngeoffray): If the method has optional parameters, 387 // TODO(ngeoffray): If the method has optional parameters,
341 // we should pass the default values here. 388 // we should pass the default values here.
342 } 389 }
343 } 390 }
344 } 391 }
345 return node; 392 return node;
346 } 393 }
347 394
348 /**
349 * Turns a primitive instruction (e.g. [HIndex], [HAdd], ...) into a
350 * [HInvokeDynamic] because we know the receiver is not a JS
351 * primitive object.
352 */
353 HInstruction fromPrimitiveInstructionToDynamicInvocation(HInstruction node,
354 Selector selector) {
355 HBoundedType type = types[node.inputs[1]];
356 HInvokeDynamicMethod result = new HInvokeDynamicMethod(
357 selector,
358 node.inputs.getRange(1, node.inputs.length - 1));
359 if (type.isExact()) {
360 HBoundedType concrete = type;
361 // TODO(johnniwinther): Add lookup by selector to HBoundedType.
362 Element element = concrete.lookupMember(selector.name);
363 if (selector.applies(element, compiler)) {
364 // The target is only valid if the selector applies.
365 result.element = element;
366 }
367 }
368 return result;
369 }
370
371 HInstruction visitIntegerCheck(HIntegerCheck node) { 395 HInstruction visitIntegerCheck(HIntegerCheck node) {
372 HInstruction value = node.value; 396 HInstruction value = node.value;
373 if (value.isInteger(types)) return value; 397 if (value.isInteger(types)) return value;
374 if (value.isConstant()) { 398 if (value.isConstant()) {
375 HConstant constantInstruction = value; 399 HConstant constantInstruction = value;
376 assert(!constantInstruction.constant.isInt()); 400 assert(!constantInstruction.constant.isInt());
377 if (!constantSystem.isInt(constantInstruction.constant)) { 401 if (!constantSystem.isInt(constantInstruction.constant)) {
378 // -0.0 is a double but will pass the runtime integer check. 402 // -0.0 is a double but will pass the runtime integer check.
379 node.alwaysFalse = true; 403 node.alwaysFalse = true;
380 } 404 }
(...skipping 196 matching lines...) Expand 10 before | Expand all | Expand 10 after
577 // [:new List.fixedLength(int):]. 601 // [:new List.fixedLength(int):].
578 HInvokeStatic call = node.receiver; 602 HInvokeStatic call = node.receiver;
579 if (isFixedSizeListConstructor(call)) { 603 if (isFixedSizeListConstructor(call)) {
580 return call.inputs[1]; 604 return call.inputs[1];
581 } 605 }
582 } 606 }
583 } 607 }
584 return node; 608 return node;
585 } 609 }
586 610
587 HInstruction optimizeLengthInterceptedCall(HInvokeDynamicGetter node) {
588 HInstruction actualReceiver = node.inputs[1];
589 if (actualReceiver.isIndexablePrimitive(types)) {
590 if (actualReceiver.isConstantString()) {
591 HConstant constantInput = actualReceiver;
592 StringConstant constant = constantInput.constant;
593 return graph.addConstantInt(constant.length, constantSystem);
594 } else if (actualReceiver.isConstantList()) {
595 HConstant constantInput = actualReceiver;
596 ListConstant constant = constantInput.constant;
597 return graph.addConstantInt(constant.length, constantSystem);
598 }
599 Element element;
600 bool isAssignable;
601 if (actualReceiver.isString(types)) {
602 element = backend.jsStringLength;
603 isAssignable = false;
604 } else {
605 element = backend.jsArrayLength;
606 isAssignable = !actualReceiver.isFixedArray(types);
607 }
608 HFieldGet result = new HFieldGet(
609 element, actualReceiver, isAssignable: isAssignable);
610 result.guaranteedType = HType.INTEGER;
611 types[result] = HType.INTEGER;
612 return result;
613 } else if (actualReceiver.isConstantMap()) {
614 HConstant constantInput = actualReceiver;
615 MapConstant constant = constantInput.constant;
616 return graph.addConstantInt(constant.length, constantSystem);
617 }
618 return node;
619 }
620
621 HInstruction visitInvokeDynamicGetter(HInvokeDynamicGetter node) { 611 HInstruction visitInvokeDynamicGetter(HInvokeDynamicGetter node) {
622 if (node.selector.name == const SourceString('length') 612 if (node.isInterceptorCall) return handleInterceptorCall(node);
623 && node.isInterceptorCall) {
624 return optimizeLengthInterceptedCall(node);
625 }
626 613
627 Element field = 614 Element field =
628 findConcreteFieldForDynamicAccess(node.receiver, node.selector); 615 findConcreteFieldForDynamicAccess(node.receiver, node.selector);
629 if (field == null) return node; 616 if (field == null) return node;
630 617
631 Modifiers modifiers = field.modifiers; 618 Modifiers modifiers = field.modifiers;
632 bool isFinalOrConst = modifiers.isFinal() || modifiers.isConst(); 619 bool isFinalOrConst = modifiers.isFinal() || modifiers.isConst();
633 if (!compiler.resolverWorld.hasInvokedSetter(field, compiler)) { 620 if (!compiler.resolverWorld.hasInvokedSetter(field, compiler)) {
634 // If no setter is ever used for this field it is only initialized in the 621 // If no setter is ever used for this field it is only initialized in the
635 // initializer list. 622 // initializer list.
636 isFinalOrConst = true; 623 isFinalOrConst = true;
637 } 624 }
638 HFieldGet result = new HFieldGet( 625 HFieldGet result = new HFieldGet(
639 field, node.inputs[0], isAssignable: !isFinalOrConst); 626 field, node.inputs[0], isAssignable: !isFinalOrConst);
640 HType type = backend.optimisticFieldType(field); 627 HType type = backend.optimisticFieldType(field);
641 if (type != null) { 628 if (type != null) {
642 result.guaranteedType = type; 629 result.guaranteedType = type;
643 backend.registerFieldTypesOptimization( 630 backend.registerFieldTypesOptimization(
644 work.element, field, result.guaranteedType); 631 work.element, field, result.guaranteedType);
645 } 632 }
646 return result; 633 return result;
647 } 634 }
648 635
649 HInstruction visitInvokeDynamicSetter(HInvokeDynamicSetter node) { 636 HInstruction visitInvokeDynamicSetter(HInvokeDynamicSetter node) {
637 if (node.isInterceptorCall) return handleInterceptorCall(node);
638
650 Element field = 639 Element field =
651 findConcreteFieldForDynamicAccess(node.receiver, node.selector); 640 findConcreteFieldForDynamicAccess(node.receiver, node.selector);
652 if (field == null || !field.isAssignable()) return node; 641 if (field == null || !field.isAssignable()) return node;
653 HInstruction value = node.inputs[1]; 642 HInstruction value = node.inputs[1];
654 if (compiler.enableTypeAssertions) { 643 if (compiler.enableTypeAssertions) {
655 HInstruction other = value.convertType( 644 HInstruction other = value.convertType(
656 compiler, 645 compiler,
657 field.computeType(compiler), 646 field.computeType(compiler),
658 HTypeConversion.CHECKED_MODE_CHECK); 647 HTypeConversion.CHECKED_MODE_CHECK);
659 if (other != value) { 648 if (other != value) {
(...skipping 874 matching lines...) Expand 10 before | Expand all | Expand 10 after
1534 HBasicBlock block = user.block; 1523 HBasicBlock block = user.block;
1535 block.addAfter(user, interceptor); 1524 block.addAfter(user, interceptor);
1536 block.rewrite(user, interceptor); 1525 block.rewrite(user, interceptor);
1537 block.remove(user); 1526 block.remove(user);
1538 1527
1539 // The interceptor will be removed in the dead code elimination 1528 // The interceptor will be removed in the dead code elimination
1540 // phase. Note that removing it here would not work because of how 1529 // phase. Note that removing it here would not work because of how
1541 // the [visitBasicBlock] is implemented. 1530 // the [visitBasicBlock] is implemented.
1542 } 1531 }
1543 } 1532 }
OLDNEW
« no previous file with comments | « no previous file | tests/compiler/dart2js/interceptor_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698