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

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
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; 308 if (selector.isCall()) {
269 SourceString selectorName = selector.name; 309 Element target;
270 Element target; 310 if (input.isExtendableArray(types)) {
271 if (input.isExtendableArray(types)) { 311 if (selector.applies(backend.jsArrayRemoveLast, compiler)) {
272 if (selectorName == backend.jsArrayRemoveLast.name 312 target = backend.jsArrayRemoveLast;
273 && selector.argumentCount == 0) { 313 } else if (selector.applies(backend.jsArrayAdd, compiler)) {
274 target = backend.jsArrayRemoveLast; 314 // The codegen special cases array calls, but does not
275 } else if (selectorName == backend.jsArrayAdd.name 315 // inlined argument type checks.
kasperl 2013/02/04 13:39:03 but does not inlined -> but do not inline ?
ngeoffray 2013/02/04 14:51:07 Done.
276 && selector.argumentCount == 1 316 if (!compiler.enableTypeAssertions) {
277 && selector.namedArgumentCount == 0 317 target = backend.jsArrayAdd;
278 && !compiler.enableTypeAssertions) { 318 }
279 target = backend.jsArrayAdd; 319 }
320 } else if (input.isString(types)) {
321 if (selector.applies(backend.jsStringSplit, compiler)) {
322 if (node.inputs[2].isString(types)) {
323 target = backend.jsStringSplit;
324 }
325 } else if (selector.applies(backend.jsStringConcat, compiler)) {
326 if (node.inputs[2].isString(types)) {
327 target = backend.jsStringConcat;
328 }
329 } else if (selector.applies(backend.jsStringToString, compiler)) {
330 return node.inputs[1];
kasperl 2013/02/04 13:39:03 node.inputs[1] -> input
ngeoffray 2013/02/04 14:51:07 Done.
331 }
280 } 332 }
281 } else if (input.isString(types)) { 333 if (target != null) {
282 if (selectorName == backend.jsStringSplit.name 334 HInvokeDynamicMethod result = new HInvokeDynamicMethod(
283 && selector.argumentCount == 1 335 node.selector, node.inputs.getRange(1, node.inputs.length - 1));
284 && selector.namedArgumentCount == 0 336 result.element = target;
285 && node.inputs[2].isString(types)) { 337 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 } 338 }
293 } 339 } else if (selector.isGetter()) {
294 if (target != null) { 340 if (selector.applies(backend.jsArrayLength, compiler)) {
295 HInvokeDynamicMethod result = new HInvokeDynamicMethod( 341 return optimizeLengthInterceptedGetter(node);
296 node.selector, node.inputs.getRange(1, node.inputs.length - 1)); 342 }
297 result.element = target;
298 return result;
299 } 343 }
300 return node; 344 return node;
301 } 345 }
302 346
303 bool isFixedSizeListConstructor(HInvokeStatic node) { 347 bool isFixedSizeListConstructor(HInvokeStatic node) {
304 Element element = node.target.element; 348 Element element = node.target.element;
305 if (backend.fixedLengthListConstructor == null) { 349 if (backend.fixedLengthListConstructor == null) {
306 backend.fixedLengthListConstructor = 350 backend.fixedLengthListConstructor =
307 compiler.listClass.lookupConstructor( 351 compiler.listClass.lookupConstructor(
308 new Selector.callConstructor(const SourceString("fixedLength"), 352 new Selector.callConstructor(const SourceString("fixedLength"),
(...skipping 29 matching lines...) Expand all
338 node.element = element; 382 node.element = element;
339 } 383 }
340 // TODO(ngeoffray): If the method has optional parameters, 384 // TODO(ngeoffray): If the method has optional parameters,
341 // we should pass the default values here. 385 // we should pass the default values here.
342 } 386 }
343 } 387 }
344 } 388 }
345 return node; 389 return node;
346 } 390 }
347 391
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) { 392 HInstruction visitIntegerCheck(HIntegerCheck node) {
372 HInstruction value = node.value; 393 HInstruction value = node.value;
373 if (value.isInteger(types)) return value; 394 if (value.isInteger(types)) return value;
374 if (value.isConstant()) { 395 if (value.isConstant()) {
375 HConstant constantInstruction = value; 396 HConstant constantInstruction = value;
376 assert(!constantInstruction.constant.isInt()); 397 assert(!constantInstruction.constant.isInt());
377 if (!constantSystem.isInt(constantInstruction.constant)) { 398 if (!constantSystem.isInt(constantInstruction.constant)) {
378 // -0.0 is a double but will pass the runtime integer check. 399 // -0.0 is a double but will pass the runtime integer check.
379 node.alwaysFalse = true; 400 node.alwaysFalse = true;
380 } 401 }
(...skipping 196 matching lines...) Expand 10 before | Expand all | Expand 10 after
577 // [:new List.fixedLength(int):]. 598 // [:new List.fixedLength(int):].
578 HInvokeStatic call = node.receiver; 599 HInvokeStatic call = node.receiver;
579 if (isFixedSizeListConstructor(call)) { 600 if (isFixedSizeListConstructor(call)) {
580 return call.inputs[1]; 601 return call.inputs[1];
581 } 602 }
582 } 603 }
583 } 604 }
584 return node; 605 return node;
585 } 606 }
586 607
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) { 608 HInstruction visitInvokeDynamicGetter(HInvokeDynamicGetter node) {
622 if (node.selector.name == const SourceString('length') 609 if (node.isInterceptorCall) return handleInterceptorCall(node);
623 && node.isInterceptorCall) {
624 return optimizeLengthInterceptedCall(node);
625 }
626 610
627 Element field = 611 Element field =
628 findConcreteFieldForDynamicAccess(node.receiver, node.selector); 612 findConcreteFieldForDynamicAccess(node.receiver, node.selector);
629 if (field == null) return node; 613 if (field == null) return node;
630 614
631 Modifiers modifiers = field.modifiers; 615 Modifiers modifiers = field.modifiers;
632 bool isFinalOrConst = modifiers.isFinal() || modifiers.isConst(); 616 bool isFinalOrConst = modifiers.isFinal() || modifiers.isConst();
633 if (!compiler.resolverWorld.hasInvokedSetter(field, compiler)) { 617 if (!compiler.resolverWorld.hasInvokedSetter(field, compiler)) {
634 // If no setter is ever used for this field it is only initialized in the 618 // If no setter is ever used for this field it is only initialized in the
635 // initializer list. 619 // initializer list.
636 isFinalOrConst = true; 620 isFinalOrConst = true;
637 } 621 }
638 HFieldGet result = new HFieldGet( 622 HFieldGet result = new HFieldGet(
639 field, node.inputs[0], isAssignable: !isFinalOrConst); 623 field, node.inputs[0], isAssignable: !isFinalOrConst);
640 HType type = backend.optimisticFieldType(field); 624 HType type = backend.optimisticFieldType(field);
641 if (type != null) { 625 if (type != null) {
642 result.guaranteedType = type; 626 result.guaranteedType = type;
643 backend.registerFieldTypesOptimization( 627 backend.registerFieldTypesOptimization(
644 work.element, field, result.guaranteedType); 628 work.element, field, result.guaranteedType);
645 } 629 }
646 return result; 630 return result;
647 } 631 }
648 632
649 HInstruction visitInvokeDynamicSetter(HInvokeDynamicSetter node) { 633 HInstruction visitInvokeDynamicSetter(HInvokeDynamicSetter node) {
634 if (node.isInterceptorCall) return handleInterceptorCall(node);
635
650 Element field = 636 Element field =
651 findConcreteFieldForDynamicAccess(node.receiver, node.selector); 637 findConcreteFieldForDynamicAccess(node.receiver, node.selector);
652 if (field == null || !field.isAssignable()) return node; 638 if (field == null || !field.isAssignable()) return node;
653 HInstruction value = node.inputs[1]; 639 HInstruction value = node.inputs[1];
654 if (compiler.enableTypeAssertions) { 640 if (compiler.enableTypeAssertions) {
655 HInstruction other = value.convertType( 641 HInstruction other = value.convertType(
656 compiler, 642 compiler,
657 field.computeType(compiler), 643 field.computeType(compiler),
658 HTypeConversion.CHECKED_MODE_CHECK); 644 HTypeConversion.CHECKED_MODE_CHECK);
659 if (other != value) { 645 if (other != value) {
(...skipping 874 matching lines...) Expand 10 before | Expand all | Expand 10 after
1534 HBasicBlock block = user.block; 1520 HBasicBlock block = user.block;
1535 block.addAfter(user, interceptor); 1521 block.addAfter(user, interceptor);
1536 block.rewrite(user, interceptor); 1522 block.rewrite(user, interceptor);
1537 block.remove(user); 1523 block.remove(user);
1538 1524
1539 // The interceptor will be removed in the dead code elimination 1525 // The interceptor will be removed in the dead code elimination
1540 // phase. Note that removing it here would not work because of how 1526 // phase. Note that removing it here would not work because of how
1541 // the [visitBasicBlock] is implemented. 1527 // the [visitBasicBlock] is implemented.
1542 } 1528 }
1543 } 1529 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698