2 // Copyright (c) 2003, 2004, 2005 Rodrigo B. de Oliveira (rbo@acm.org)
3 // All rights reserved.
5 // Redistribution and use in source and binary forms, with or without modification,
6 // are permitted provided that the following conditions are met:
8 // * Redistributions of source code must retain the above copyright notice,
9 // this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above copyright notice,
11 // this list of conditions and the following disclaimer in the documentation
12 // and/or other materials provided with the distribution.
13 // * Neither the name of Rodrigo B. de Oliveira nor the names of its
14 // contributors may be used to endorse or promote products derived from this
15 // software without specific prior written permission.
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
21 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 //Authored by Cameron Kenneth Knight: http://jira.codehaus.org/browse/BOO-137
31 namespace Boo
.Lang
.Compiler
.Steps
34 using Boo
.Lang
.Compiler
.Ast
;
35 using Boo
.Lang
.Compiler
.TypeSystem
;
38 /// AST semantic evaluation.
40 public class OptimizeIterationStatements
: AbstractTransformerCompilerStep
42 static readonly System
.Reflection
.MethodInfo Array_get_Length
= Types
.Array
.GetProperty("Length").GetGetMethod();
43 static readonly System
.Reflection
.MethodInfo System_Math_Ceiling
= typeof(System
.Math
).GetMethod("Ceiling", new System
.Type
[] { typeof(double) }
);
44 static readonly System
.Reflection
.ConstructorInfo System_ArgumentOutOfRangeException_ctor
= typeof(System
.ArgumentOutOfRangeException
).GetConstructor(new System
.Type
[] { typeof(string) }
);
47 IMethod _range_Begin_End
;
48 IMethod _range_Begin_End_Step
;
50 Method _currentMethod
;
52 public OptimizeIterationStatements()
56 override public void Initialize(CompilerContext context
)
58 base.Initialize(context
);
60 Type builtins
= typeof(Boo
.Lang
.Builtins
);
61 _range_End
= Map(builtins
.GetMethod("range", new Type
[] { Types.Int }
));
62 _range_Begin_End
= Map(builtins
.GetMethod("range", new Type
[] { Types.Int, Types.Int }
));
63 _range_Begin_End_Step
= Map(builtins
.GetMethod("range", new Type
[] { Types.Int, Types.Int, Types.Int }
));
66 IMethod
Map(System
.Reflection
.MethodInfo method
)
68 return TypeSystemServices
.Map(method
);
71 override public void Run()
76 override public void OnMethod(Method node
)
78 _currentMethod
= node
;
82 override public void OnConstructor(Constructor node
)
87 override public void OnDestructor(Destructor node
)
92 override public void OnBlockExpression(BlockExpression node
)
94 // ignore closure's body since it will be visited
95 // through the closure's newly created method
98 override public void LeaveForStatement(ForStatement node
)
100 CheckForItemInRangeLoop(node
);
101 CheckForItemInArrayLoop(node
);
104 bool IsRangeInvocation(MethodInvocationExpression mi
)
106 IEntity entity
= mi
.Target
.Entity
;
107 return entity
== _range_End
108 || entity
== _range_Begin_End
109 || entity
== _range_Begin_End_Step
;
113 /// Optimize the <c>for item in range()</c> construct
115 /// <param name="node">the for statement to check</param>
116 private void CheckForItemInRangeLoop(ForStatement node
)
118 MethodInvocationExpression mi
= node
.Iterator
as MethodInvocationExpression
;
119 if (null == mi
) return;
120 if (!IsRangeInvocation(mi
)) return;
122 DeclarationCollection declarations
= node
.Declarations
;
123 if (declarations
.Count
!= 1) return;
125 ExpressionCollection args
= mi
.Arguments
;
126 Block body
= new Block(node
.LexicalInfo
);
134 min
= CodeBuilder
.CreateIntegerLiteral(0);
136 step
= CodeBuilder
.CreateIntegerLiteral(1);
138 else if (args
.Count
== 2)
142 step
= CodeBuilder
.CreateIntegerLiteral(1);
151 InternalLocal numVar
= CodeBuilder
.DeclareTempLocal(
153 TypeSystemServices
.IntType
);
154 Expression numRef
= CodeBuilder
.CreateReference(numVar
);
158 CodeBuilder
.CreateAssignment(
164 if (max
.NodeType
== NodeType
.IntegerLiteralExpression
)
170 InternalLocal endVar
= CodeBuilder
.DeclareTempLocal(
172 TypeSystemServices
.IntType
);
173 endRef
= CodeBuilder
.CreateReference(endVar
);
177 CodeBuilder
.CreateAssignment(
184 if (max
.NodeType
== NodeType
.IntegerLiteralExpression
)
186 if (((IntegerLiteralExpression
)max
).Value
< 0)
188 // raise ArgumentOutOfRangeException("max") (if <max> < 0)
189 Statement statement
= CodeBuilder
.RaiseException(
191 TypeSystemServices
.Map(System_ArgumentOutOfRangeException_ctor
),
192 CodeBuilder
.CreateStringLiteral("max"));
199 IfStatement ifStatement
= new IfStatement(body
.LexicalInfo
);
200 ifStatement
.TrueBlock
= new Block();
202 // raise ArgumentOutOfRangeException("max") if __end < 0
203 Statement statement
= CodeBuilder
.RaiseException(
205 TypeSystemServices
.Map(System_ArgumentOutOfRangeException_ctor
),
206 CodeBuilder
.CreateStringLiteral("max"));
208 ifStatement
.Condition
= CodeBuilder
.CreateBoundBinaryExpression(
209 TypeSystemServices
.BoolType
,
210 BinaryOperatorType
.LessThan
,
212 CodeBuilder
.CreateIntegerLiteral(0));
214 ifStatement
.TrueBlock
.Add(statement
);
216 body
.Add(ifStatement
);
225 stepRef
= CodeBuilder
.CreateIntegerLiteral(1);
228 if ((min
.NodeType
== NodeType
.IntegerLiteralExpression
) &&
229 (max
.NodeType
== NodeType
.IntegerLiteralExpression
) &&
230 (((IntegerLiteralExpression
)max
).Value
< ((IntegerLiteralExpression
)min
).Value
))
233 stepRef
= CodeBuilder
.CreateIntegerLiteral(-1);
235 else if ((min
.NodeType
== NodeType
.IntegerLiteralExpression
) &&
236 (max
.NodeType
== NodeType
.IntegerLiteralExpression
))
239 stepRef
= CodeBuilder
.CreateIntegerLiteral(1);
243 InternalLocal stepVar
= CodeBuilder
.DeclareTempLocal(
245 TypeSystemServices
.IntType
);
246 stepRef
= CodeBuilder
.CreateReference(stepVar
);
250 CodeBuilder
.CreateAssignment(
252 CodeBuilder
.CreateIntegerLiteral(1)));
254 // __step = -1 if __end < __num
255 IfStatement ifStatement
= new IfStatement(node
.LexicalInfo
);
257 ifStatement
.Condition
= CodeBuilder
.CreateBoundBinaryExpression(
258 TypeSystemServices
.BoolType
,
259 BinaryOperatorType
.LessThan
,
263 ifStatement
.TrueBlock
= new Block();
265 ifStatement
.TrueBlock
.Add(
266 CodeBuilder
.CreateAssignment(
268 CodeBuilder
.CreateIntegerLiteral(-1)));
270 body
.Add(ifStatement
);
274 if (step
.NodeType
== NodeType
.IntegerLiteralExpression
)
280 InternalLocal stepVar
= CodeBuilder
.DeclareTempLocal(
282 TypeSystemServices
.IntType
);
283 stepRef
= CodeBuilder
.CreateReference(stepVar
);
287 CodeBuilder
.CreateAssignment(
297 Expression condition
= null;
300 if (step
.NodeType
== NodeType
.IntegerLiteralExpression
)
302 if (((IntegerLiteralExpression
)step
).Value
< 0)
304 if ((max
.NodeType
== NodeType
.IntegerLiteralExpression
) &&
305 (min
.NodeType
== NodeType
.IntegerLiteralExpression
))
307 run
= (((IntegerLiteralExpression
)max
).Value
> ((IntegerLiteralExpression
)min
).Value
);
311 condition
= CodeBuilder
.CreateBoundBinaryExpression(
312 TypeSystemServices
.BoolType
,
313 BinaryOperatorType
.GreaterThan
,
320 if ((max
.NodeType
== NodeType
.IntegerLiteralExpression
) &&
321 (min
.NodeType
== NodeType
.IntegerLiteralExpression
))
323 run
= (((IntegerLiteralExpression
)max
).Value
< ((IntegerLiteralExpression
)min
).Value
);
327 condition
= CodeBuilder
.CreateBoundBinaryExpression(
328 TypeSystemServices
.BoolType
,
329 BinaryOperatorType
.LessThan
,
337 if ((max
.NodeType
== NodeType
.IntegerLiteralExpression
) &&
338 (min
.NodeType
== NodeType
.IntegerLiteralExpression
))
340 if (((IntegerLiteralExpression
)max
).Value
< ((IntegerLiteralExpression
)min
).Value
)
342 condition
= CodeBuilder
.CreateBoundBinaryExpression(
343 TypeSystemServices
.BoolType
,
344 BinaryOperatorType
.GreaterThan
,
346 CodeBuilder
.CreateIntegerLiteral(0));
350 condition
= CodeBuilder
.CreateBoundBinaryExpression(
351 TypeSystemServices
.BoolType
,
352 BinaryOperatorType
.LessThan
,
354 CodeBuilder
.CreateIntegerLiteral(0));
359 condition
= CodeBuilder
.CreateBoundBinaryExpression(
360 TypeSystemServices
.BoolType
,
361 BinaryOperatorType
.Or
,
362 CodeBuilder
.CreateBoundBinaryExpression(
363 TypeSystemServices
.BoolType
,
364 BinaryOperatorType
.And
,
365 CodeBuilder
.CreateBoundBinaryExpression(
366 TypeSystemServices
.BoolType
,
367 BinaryOperatorType
.LessThan
,
369 CodeBuilder
.CreateIntegerLiteral(0)),
370 CodeBuilder
.CreateBoundBinaryExpression(
371 TypeSystemServices
.BoolType
,
372 BinaryOperatorType
.GreaterThan
,
375 CodeBuilder
.CreateBoundBinaryExpression(
376 TypeSystemServices
.BoolType
,
377 BinaryOperatorType
.And
,
378 CodeBuilder
.CreateBoundBinaryExpression(
379 TypeSystemServices
.BoolType
,
380 BinaryOperatorType
.GreaterThan
,
382 CodeBuilder
.CreateIntegerLiteral(0)),
383 CodeBuilder
.CreateBoundBinaryExpression(
384 TypeSystemServices
.BoolType
,
385 BinaryOperatorType
.LessThan
,
391 // raise ArgumentOutOfRangeException("step") if (__step < 0 and __end > __begin) or (__step > 0 and __end < __begin)
392 Statement statement
= CodeBuilder
.RaiseException(
394 TypeSystemServices
.Map(System_ArgumentOutOfRangeException_ctor
),
395 CodeBuilder
.CreateStringLiteral("step"));
397 if (condition
!= null)
399 IfStatement ifStatement
= new IfStatement(body
.LexicalInfo
);
400 ifStatement
.TrueBlock
= new Block();
402 ifStatement
.Condition
= condition
;
404 ifStatement
.TrueBlock
.Add(statement
);
406 body
.Add(ifStatement
);
413 // __end = __num + __step * cast(int, Math.Ceiling((__end - __num)/cast(double, __step)))
414 if ((step
.NodeType
== NodeType
.IntegerLiteralExpression
) &&
415 (max
.NodeType
== NodeType
.IntegerLiteralExpression
) &&
416 (min
.NodeType
== NodeType
.IntegerLiteralExpression
))
418 int stepVal
= (int)((IntegerLiteralExpression
)step
).Value
;
419 int maxVal
= (int)((IntegerLiteralExpression
)max
).Value
;
420 int minVal
= (int)((IntegerLiteralExpression
)min
).Value
;
421 endRef
= CodeBuilder
.CreateIntegerLiteral(
422 minVal
+ stepVal
* (int)System
.Math
.Ceiling((maxVal
- minVal
) / ((double)stepVal
)));
426 Expression endBak
= endRef
;
427 if (max
.NodeType
== NodeType
.IntegerLiteralExpression
)
429 InternalLocal endVar
= CodeBuilder
.DeclareTempLocal(
431 TypeSystemServices
.IntType
);
432 endRef
= CodeBuilder
.CreateReference(endVar
);
436 CodeBuilder
.CreateAssignment(
438 CodeBuilder
.CreateBoundBinaryExpression(
439 TypeSystemServices
.IntType
,
440 BinaryOperatorType
.Addition
,
442 CodeBuilder
.CreateBoundBinaryExpression(
443 TypeSystemServices
.IntType
,
444 BinaryOperatorType
.Multiply
,
446 CodeBuilder
.CreateCast(
447 TypeSystemServices
.IntType
,
448 CodeBuilder
.CreateMethodInvocation(
449 TypeSystemServices
.Map(System_Math_Ceiling
),
450 CodeBuilder
.CreateBoundBinaryExpression(
451 TypeSystemServices
.DoubleType
,
452 BinaryOperatorType
.Division
,
453 CodeBuilder
.CreateBoundBinaryExpression(
454 TypeSystemServices
.IntType
,
455 BinaryOperatorType
.Subtraction
,
458 CodeBuilder
.CreateCast(
459 TypeSystemServices
.DoubleType
,
464 // while __num != __end:
465 WhileStatement ws
= new WhileStatement(node
.LexicalInfo
);
467 BinaryOperatorType op
= BinaryOperatorType
.Inequality
;
469 if (stepRef
.NodeType
== NodeType
.IntegerLiteralExpression
)
471 if (((IntegerLiteralExpression
)stepRef
).Value
> 0)
473 op
= BinaryOperatorType
.LessThan
;
477 op
= BinaryOperatorType
.GreaterThan
;
481 ws
.Condition
= CodeBuilder
.CreateBoundBinaryExpression(
482 TypeSystemServices
.BoolType
,
486 ws
.Condition
.LexicalInfo
= node
.LexicalInfo
;
490 CodeBuilder
.CreateAssignment(
491 CodeBuilder
.CreateReference((InternalLocal
)declarations
[0].Entity
),
494 Block rawBlock
= new Block();
495 rawBlock
["checked"] = false;
499 CodeBuilder
.CreateAssignment(
501 CodeBuilder
.CreateBoundBinaryExpression(
502 TypeSystemServices
.IntType
,
503 BinaryOperatorType
.Addition
,
507 ws
.Block
.Add(rawBlock
as Statement
);
510 ws
.Block
.Add(node
.Block
);
512 ws
.OrBlock
= node
.OrBlock
;
513 ws
.ThenBlock
= node
.ThenBlock
;
517 ReplaceCurrentNode(body
);
520 private class EntityPredicate
522 private IEntity _entity
;
524 public EntityPredicate(IEntity entity
)
529 public bool Matches(Node node
)
531 return _entity
== TypeSystemServices
.GetOptionalEntity(node
);
536 /// Optimize the <c>for item in array</c> construct
538 /// <param name="node">the for statement to check</param>
539 private void CheckForItemInArrayLoop(ForStatement node
)
541 ArrayType enumeratorType
= GetExpressionType(node
.Iterator
) as ArrayType
;
542 if (enumeratorType
== null || enumeratorType
.GetArrayRank() > 1) return;
543 IType elementType
= enumeratorType
.GetElementType();
544 if (elementType
is InternalCallableType
) return;
546 Block body
= new Block(node
.LexicalInfo
);
548 InternalLocal indexVariable
= DeclareTempLocal(TypeSystemServices
.IntType
);
549 Expression indexReference
= CodeBuilder
.CreateReference(indexVariable
);
553 CodeBuilder
.CreateAssignment(
555 CodeBuilder
.CreateIntegerLiteral(0)));
558 InternalLocal arrayVar
= DeclareTempLocal(node
.Iterator
.ExpressionType
);
559 ReferenceExpression arrayRef
= CodeBuilder
.CreateReference(arrayVar
);
563 CodeBuilder
.CreateAssignment(
567 InternalLocal endVar
= CodeBuilder
.DeclareTempLocal(
569 TypeSystemServices
.IntType
);
570 ReferenceExpression endRef
= CodeBuilder
.CreateReference(endVar
);
572 // __end = __arr.Length
574 CodeBuilder
.CreateAssignment(
575 node
.Iterator
.LexicalInfo
,
577 CodeBuilder
.CreateMethodInvocation(
581 // while __num < __end:
582 WhileStatement ws
= new WhileStatement(node
.LexicalInfo
);
584 ws
.Condition
= CodeBuilder
.CreateBoundBinaryExpression(
585 TypeSystemServices
.BoolType
,
586 BinaryOperatorType
.LessThan
,
590 if (1 == node
.Declarations
.Count
)
592 IEntity loopVariable
= node
.Declarations
[0].Entity
;
593 node
.Block
.ReplaceNodes(
594 new NodePredicate(new EntityPredicate(loopVariable
).Matches
),
595 CreateRawArraySlicing(arrayRef
, indexReference
, elementType
));
599 // alpha, bravo, charlie = arr[__num]
602 CreateRawArraySlicing(arrayRef
, indexReference
, elementType
),
607 ws
.Block
.Add(node
.Block
);
609 FixContinueStatements(node
, ws
);
612 BinaryExpression assignment
= CodeBuilder
.CreateAssignment(
614 CodeBuilder
.CreateBoundBinaryExpression(
615 TypeSystemServices
.IntType
,
616 BinaryOperatorType
.Addition
,
618 CodeBuilder
.CreateIntegerLiteral(1)));
619 AstAnnotations
.MarkUnchecked(assignment
);
621 ws
.Block
.Add(assignment
);
622 ws
.OrBlock
= node
.OrBlock
;
623 ws
.ThenBlock
= node
.ThenBlock
;
625 ReplaceCurrentNode(body
);
628 private void FixContinueStatements(ForStatement node
, WhileStatement ws
)
631 LabelStatement label
= CreateUpdateLabel(node
);
632 GotoOnTopLevelContinue continueFixup
= new GotoOnTopLevelContinue(label
);
633 node
.Block
.Accept(continueFixup
);
634 if (continueFixup
.UsageCount
> 0) ws
.Block
.Add(label
);
637 private LabelStatement
CreateUpdateLabel(ForStatement node
)
639 return new LabelStatement(LexicalInfo
.Empty
, "$label$" + _context
.AllocIndex());
642 private static SlicingExpression
CreateRawArraySlicing(ReferenceExpression arrayRef
, Expression numRef
, IType elementType
)
644 SlicingExpression expression
= new SlicingExpression(arrayRef
.CloneNode(), numRef
.CloneNode());
645 expression
.ExpressionType
= elementType
;
646 AstAnnotations
.MarkRawArrayIndexing(expression
);
650 private InternalLocal
DeclareTempLocal(IType type
)
652 return CodeBuilder
.DeclareTempLocal(
658 /// Unpacks an expression onto a list of declarations.
660 /// <param name="block">Block this takes place in</param>
661 /// <param name="expression">expression to explode</param>
662 /// <param name="declarations">list of declarations to set</param>
663 void UnpackExpression(Block block
, Expression expression
, DeclarationCollection declarations
)
665 NormalizeIterationStatements
.UnpackExpression(CodeBuilder
, _currentMethod
, block
, expression
, declarations
);