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

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

Issue 14404004: Throw NoSuchMethod or ArgumentError instead of generating a bailout, when we know the next instruct… (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 7 years, 8 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 class BailoutInfo { 7 class BailoutInfo {
8 int instructionId; 8 int instructionId;
9 int bailoutId; 9 int bailoutId;
10 BailoutInfo(this.instructionId, this.bailoutId); 10 BailoutInfo(this.instructionId, this.bailoutId);
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
60 * Visits the graph in dominator order and inserts TypeGuards in places where 60 * Visits the graph in dominator order and inserts TypeGuards in places where
61 * we consider the guard to be of value. This phase also does type 61 * we consider the guard to be of value. This phase also does type
62 * propagation to help find valuable type guards. 62 * propagation to help find valuable type guards.
63 */ 63 */
64 class SsaTypeGuardInserter extends SsaNonSpeculativeTypePropagator 64 class SsaTypeGuardInserter extends SsaNonSpeculativeTypePropagator
65 implements OptimizationPhase { 65 implements OptimizationPhase {
66 final String name = 'SsaTypeGuardInserter'; 66 final String name = 'SsaTypeGuardInserter';
67 final CodegenWorkItem work; 67 final CodegenWorkItem work;
68 bool calledInLoop = false; 68 bool calledInLoop = false;
69 bool isRecursiveMethod = false; 69 bool isRecursiveMethod = false;
70 bool hasInsertedChecks = false;
70 int stateId = 1; 71 int stateId = 1;
71 Map<HInstruction, HType> savedTypes = new Map<HInstruction, HType>(); 72 Map<HInstruction, HType> savedTypes = new Map<HInstruction, HType>();
72 73
73 SsaTypeGuardInserter(compiler, this.work) : super(compiler); 74 SsaTypeGuardInserter(compiler, this.work) : super(compiler);
74 75
75 void visitGraph(HGraph graph) { 76 void visitGraph(HGraph graph) {
76 // Run the speculative type propagator. This does in-place 77 // Run the speculative type propagator. This does in-place
77 // update of the type of the instructions, and saves the 78 // update of the type of the instructions, and saves the
78 // previous types in the [savedTypes] map. 79 // previous types in the [savedTypes] map.
79 SsaTypePropagator propagator = 80 SsaTypePropagator propagator =
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after
183 && typeGuardWouldBeValuable(user, speculativeType)) { 184 && typeGuardWouldBeValuable(user, speculativeType)) {
184 return true; 185 return true;
185 } 186 }
186 } 187 }
187 188
188 // Insert type guards if the method is likely to be called in a 189 // Insert type guards if the method is likely to be called in a
189 // loop. 190 // loop.
190 return calledInLoop; 191 return calledInLoop;
191 } 192 }
192 193
194 // Returns whether an invcation of [selector] on [receiver] will throw a
kasperl 2013/04/23 06:12:43 invcation -> invocation
ngeoffray 2013/04/23 08:06:04 Done.
195 // [ArgumentError] if the argument is not of the right type.
196 bool willThrowArgumentError(Selector selector, HInstruction receiver) {
197 if (receiver != null && (receiver.isInteger() || receiver.isString())) {
198 return selector.isOperator() && selector.name != const SourceString('==');
199 }
200 return false;
201 }
202
203 // Returns whether an invocation of [selector] will throw a
204 // [NoSuchMethodError] if the receiver is not of the type
205 // [speculativeType].
206 bool willThrowNoSuchMethodErrorIfNot(Selector selector,
207 HType speculativeType) {
208 return compiler.world.hasSingleMatch(selector)
209 // In some cases, we want the receiver to be an integer,
210 // but that does not mean we will get a NoSuchMethodError
211 // if it's not: the receiver could be a double.
212 && !speculativeType.isInteger()
213 // We speculate on the [operator==] instruction, but we know it
214 // will never throw a [NoSuchMethodError].
215 && selector.name != const SourceString('==');
216 }
217
193 bool shouldInsertTypeGuard(HInstruction instruction, HType speculativeType) { 218 bool shouldInsertTypeGuard(HInstruction instruction, HType speculativeType) {
194 if (!speculativeType.isUseful()) return false; 219 if (!speculativeType.isUseful()) return false;
195 // If the types agree we don't need to check. 220 // If the types agree we don't need to check.
196 if (speculativeType == instruction.instructionType) return false; 221 if (speculativeType == instruction.instructionType) return false;
197 // If a bailout check is more expensive than doing the actual operation 222 // If a bailout check is more expensive than doing the actual operation
198 // don't do it either. 223 // don't do it either.
199 return typeGuardWouldBeValuable(instruction, speculativeType); 224 return typeGuardWouldBeValuable(instruction, speculativeType);
200 } 225 }
201 226
202 bool updateType(HInstruction instruction) { 227 bool updateType(HInstruction instruction) {
203 bool hasChanged = super.updateType(instruction); 228 bool hasChanged = super.updateType(instruction);
204 HType speculativeType = savedTypes[instruction]; 229 HType speculativeType = savedTypes[instruction];
205 if (speculativeType == null) return hasChanged; 230 if (speculativeType == null) return hasChanged;
206 231
207 if (shouldInsertTypeGuard(instruction, speculativeType)) { 232 if (shouldInsertTypeGuard(instruction, speculativeType)) {
208 HInstruction insertionPoint; 233 HInstruction insertionPoint;
209 if (instruction is HPhi) { 234 if (instruction is HPhi) {
210 insertionPoint = instruction.block.first; 235 insertionPoint = instruction.block.first;
211 } else if (instruction is HParameterValue) { 236 } else if (instruction is HParameterValue) {
212 // We insert the type guard at the end of the entry block 237 // We insert the type guard at the end of the entry block
213 // because if a parameter is live, it must be kept in the live 238 // because if a parameter is live, it must be kept in the live
214 // environment. Not doing so would mean we could visit a 239 // environment. Not doing so would mean we could visit a
215 // parameter and remove it from the environment before 240 // parameter and remove it from the environment before
216 // visiting a type guard. 241 // visiting a type guard.
217 insertionPoint = instruction.block.last; 242 insertionPoint = instruction.block.last;
218 } else { 243 } else {
219 insertionPoint = instruction.next; 244 insertionPoint = instruction.next;
220 } 245 }
221 // If the previous instruction is also a type guard, then both 246
222 // guards have the same environment, and can therefore share the 247 // Find out if we should actually just emit a check for it. If
kasperl 2013/04/23 06:12:43 It would be nice if this could be refactored into
ngeoffray 2013/04/23 08:06:04 Done.
223 // same state id. 248 // there is a user of [instruction] in the same block (so that
224 HBailoutTarget target; 249 // we know it will be executed), and that user has a selector
225 int state; 250 // (meaning it will be a call), we can put a type check instead
226 if (insertionPoint.previous is HTypeGuard) { 251 // of a type guard, that will either throw a [NoSuchMethodError]
227 HTypeGuard other = insertionPoint.previous; 252 // or a [ArgumentError].
228 target = other.bailoutTarget; 253 bool willThrow = false;
254 Selector receiverSelectorOnThrow = null;
255 HInstruction firstUserWithSelector;
256 HInstruction firstUser;
257 // For a parameter, we look at the first block that contains
258 // user instructions.
259 HBasicBlock userMustBeInBlock = instruction is HParameterValue
260 ? instruction.block.successors[0]
261 : instruction.block;
262
263 // We distinguish between [firstUser] and
264 // [firstUserWithSelector] because we can often have the pattern
265 // that [firstUser] is a call to [: getInterceptor :] and
266 // [firstUserWithSelector] is the actual user of the
267 // instruction.
268 for (HInstruction user in instruction.usedBy) {
kasperl 2013/04/23 06:12:43 Somehow it feels like we could use a better strate
ngeoffray 2013/04/23 08:06:04 Agree. After discussing a bit about it, I'll work
269 if (user.block == userMustBeInBlock) {
270 if (firstUser == null || user.dominates(firstUser)) {
271 firstUser = user;
272 if (user.selector != null) {
273 firstUserWithSelector = user;
274 }
275 } else if (user.selector != null) {
276 if (firstUserWithSelector == null
277 || user.dominates(firstUserWithSelector)) {
278 firstUserWithSelector = user;
279 }
280 }
281 }
282 }
283
284 // If we have found a user with a selector, we find out if it
285 // will throw [NoSuchMethodError] or [ArgumentError].
286 if (firstUserWithSelector != null
287 && (firstUserWithSelector == firstUser
288 || firstUser.next == firstUserWithSelector)) {
289 assert(firstUser == firstUserWithSelector || !firstUser.hasSideEffects() );
kasperl 2013/04/23 06:12:43 Long line.
ngeoffray 2013/04/23 08:06:04 Done.
290 Selector selector = firstUserWithSelector.selector;
291 HInstruction receiver = firstUserWithSelector.getDartReceiver(compiler);
292 if (receiver == instruction) {
293 if (willThrowNoSuchMethodErrorIfNot(selector, speculativeType)) {
294 receiverSelectorOnThrow = firstUserWithSelector.selector;
295 willThrow = true;
296 }
297 } else if (willThrowArgumentError(selector, receiver)) {
298 willThrow = true;
299 }
300 }
301
302 var check;
303 if (willThrow) {
304 insertionPoint = firstUser;
305 check = new HTypeConversion(
306 null,
307 receiverSelectorOnThrow == null
308 ? HTypeConversion.ARGUMENT_TYPE_CHECK
309 : HTypeConversion.RECEIVER_TYPE_CHECK,
310 speculativeType,
311 instruction,
312 receiverSelectorOnThrow);
313 hasInsertedChecks = true;
229 } else { 314 } else {
230 state = stateId++; 315 // If the previous instruction is also a type guard, then both
231 target = new HBailoutTarget(state); 316 // guards have the same environment, and can therefore share the
232 insertionPoint.block.addBefore(insertionPoint, target); 317 // same state id.
318 HBailoutTarget target;
319 int state;
320 if (insertionPoint.previous is HTypeGuard) {
321 HTypeGuard other = insertionPoint.previous;
322 target = other.bailoutTarget;
323 } else {
324 state = stateId++;
325 target = new HBailoutTarget(state);
326 insertionPoint.block.addBefore(insertionPoint, target);
327 }
328 check = new HTypeGuard(speculativeType, instruction, target);
329 work.guards.add(check);
330 // By setting the type of the guard to the speculated type, we
331 // help the analysis find valuable type guards. This however
332 // requires to run a non-speculative type propagation again
333 // after this analysis.
334 check.instructionType = speculativeType;
233 } 335 }
234 HTypeGuard guard = new HTypeGuard(speculativeType, instruction, target); 336 instruction.block.rewrite(instruction, check);
235 work.guards.add(guard); 337 insertionPoint.block.addBefore(insertionPoint, check);
236 // By setting the type of the guard to the speculated type, we
237 // help the analysis find valuable type guards. This however
238 // requires to run a non-speculative type propagation again
239 // after this analysis.
240 guard.instructionType = speculativeType;
241 instruction.block.rewrite(instruction, guard);
242 insertionPoint.block.addBefore(insertionPoint, guard);
243 } 338 }
244 return hasChanged; 339 return hasChanged;
245 } 340 }
246 } 341 }
247 342
248 /** 343 /**
249 * Computes the environment for each SSA instruction: visits the graph 344 * Computes the environment for each SSA instruction: visits the graph
250 * in post-dominator order. Removes an instruction from the environment 345 * in post-dominator order. Removes an instruction from the environment
251 * and adds its inputs to the environment at the instruction's 346 * and adds its inputs to the environment at the instruction's
252 * definition. 347 * definition.
(...skipping 367 matching lines...) Expand 10 before | Expand all | Expand 10 after
620 hasComplexBailoutTargets = true; 715 hasComplexBailoutTargets = true;
621 } 716 }
622 } else { 717 } else {
623 hasComplexBailoutTargets = true; 718 hasComplexBailoutTargets = true;
624 blocks.forEach((HBasicBlock block) { 719 blocks.forEach((HBasicBlock block) {
625 block.bailoutTargets.add(target); 720 block.bailoutTargets.add(target);
626 }); 721 });
627 } 722 }
628 } 723 }
629 } 724 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698