#region license // Copyright (c) 2003, 2004, 2005 Rodrigo B. de Oliveira (rbo@acm.org) // All rights reserved. // // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: // // * Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // * Neither the name of Rodrigo B. de Oliveira nor the names of its // contributors may be used to endorse or promote products derived from this // software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #endregion namespace Boo.Lang.Compiler.Steps { using Boo.Lang.Compiler.Ast; using Boo.Lang.Compiler.TypeSystem; /// /// AST semantic evaluation. /// public class OptimizeIterationStatements : AbstractTransformerCompilerStep { static readonly System.Reflection.MethodInfo Array_get_Length = Types.Array.GetProperty("Length").GetGetMethod(); static readonly System.Reflection.MethodInfo System_Math_Ceiling = typeof(System.Math).GetMethod("Ceiling", new System.Type[] { typeof(double) }); static readonly System.Reflection.ConstructorInfo System_ArgumentOutOfRangeException_ctor = typeof(System.ArgumentOutOfRangeException).GetConstructor(new System.Type[] { typeof(string) }); Method _current; public OptimizeIterationStatements() { } override public void Run() { Visit(CompileUnit); } override public void OnMethod(Method node) { _current = node; Visit(node.Body); } override public void OnConstructor(Constructor node) { OnMethod(node); } override public void OnDestructor(Destructor node) { OnMethod(node); } override public void OnCallableBlockExpression(CallableBlockExpression node) { // ignore closure's body since it will be visited // through the closure's newly created method } override public void LeaveForStatement(ForStatement node) { CheckForItemInRangeLoop(node); CheckForItemInArrayLoop(node); } /// /// Optimize the for item in range() construct /// /// the for statement to check private void CheckForItemInRangeLoop(ForStatement node) { MethodInvocationExpression mi = node.Iterator as MethodInvocationExpression; DeclarationCollection declarations = node.Declarations; Block body = new Block(node.LexicalInfo); if ((mi == null) || (mi.Target.ToCodeString() != "Boo.Lang.Builtins.range")) { return; } ExpressionCollection args = mi.Arguments; if ((args.Count < 1) || (args.Count > 3) || (declarations.Count != 1)) { return; } Expression min; Expression max; Expression step; if (args.Count == 1) { min = CodeBuilder.CreateIntegerLiteral(0); max = args[0]; step = CodeBuilder.CreateIntegerLiteral(1); } else if (args.Count == 2) { min = args[0]; max = args[1]; step = CodeBuilder.CreateIntegerLiteral(1); } else { min = args[0]; max = args[1]; step = args[2]; } InternalLocal numVar = CodeBuilder.DeclareTempLocal( _current, TypeSystemServices.IntType); Expression numRef = CodeBuilder.CreateReference(numVar); // __num = body.Add( CodeBuilder.CreateAssignment( numRef, min)); Expression endRef; if (max.NodeType == NodeType.IntegerLiteralExpression) { endRef = max; } else { InternalLocal endVar = CodeBuilder.DeclareTempLocal( _current, TypeSystemServices.IntType); endRef = CodeBuilder.CreateReference(endVar); // __end = body.Add( CodeBuilder.CreateAssignment( endRef, max)); } if (args.Count == 1) { if (max.NodeType == NodeType.IntegerLiteralExpression) { if (((IntegerLiteralExpression)max).Value < 0) { // raise ArgumentOutOfRangeException("max") (if < 0) Statement statement = CodeBuilder.RaiseException( body.LexicalInfo, TypeSystemServices.Map(System_ArgumentOutOfRangeException_ctor), CodeBuilder.CreateStringLiteral("max")); body.Add(statement); } } else { IfStatement ifStatement = new IfStatement(body.LexicalInfo); ifStatement.TrueBlock = new Block(); // raise ArgumentOutOfRangeException("max") if __end < 0 Statement statement = CodeBuilder.RaiseException( body.LexicalInfo, TypeSystemServices.Map(System_ArgumentOutOfRangeException_ctor), CodeBuilder.CreateStringLiteral("max")); ifStatement.Condition = CodeBuilder.CreateBoundBinaryExpression( TypeSystemServices.BoolType, BinaryOperatorType.LessThan, endRef, CodeBuilder.CreateIntegerLiteral(0)); ifStatement.TrueBlock.Add(statement); body.Add(ifStatement); } } Expression stepRef; switch (args.Count) { case 1: stepRef = CodeBuilder.CreateIntegerLiteral(1); break; case 2: if ((min.NodeType == NodeType.IntegerLiteralExpression) && (max.NodeType == NodeType.IntegerLiteralExpression) && (((IntegerLiteralExpression)max).Value < ((IntegerLiteralExpression)min).Value)) { // __step = -1 stepRef = CodeBuilder.CreateIntegerLiteral(-1); } else if ((min.NodeType == NodeType.IntegerLiteralExpression) && (max.NodeType == NodeType.IntegerLiteralExpression)) { // __step = 1 stepRef = CodeBuilder.CreateIntegerLiteral(1); } else { InternalLocal stepVar = CodeBuilder.DeclareTempLocal( _current, TypeSystemServices.IntType); stepRef = CodeBuilder.CreateReference(stepVar); // __step = 1 body.Add( CodeBuilder.CreateAssignment( stepRef, CodeBuilder.CreateIntegerLiteral(1))); // __step = -1 if __end < __num IfStatement ifStatement = new IfStatement(node.LexicalInfo); ifStatement.Condition = CodeBuilder.CreateBoundBinaryExpression( TypeSystemServices.BoolType, BinaryOperatorType.LessThan, endRef, numRef); ifStatement.TrueBlock = new Block(); ifStatement.TrueBlock.Add( CodeBuilder.CreateAssignment( stepRef, CodeBuilder.CreateIntegerLiteral(-1))); body.Add(ifStatement); } break; default: if (step.NodeType == NodeType.IntegerLiteralExpression) { stepRef = step; } else { InternalLocal stepVar = CodeBuilder.DeclareTempLocal( _current, TypeSystemServices.IntType); stepRef = CodeBuilder.CreateReference(stepVar); // __step = body.Add( CodeBuilder.CreateAssignment( stepRef, step)); } break; } if (args.Count == 3) { Expression condition = null; bool run = false; if (step.NodeType == NodeType.IntegerLiteralExpression) { if (((IntegerLiteralExpression)step).Value < 0) { if ((max.NodeType == NodeType.IntegerLiteralExpression) && (min.NodeType == NodeType.IntegerLiteralExpression)) { run = (((IntegerLiteralExpression)max).Value > ((IntegerLiteralExpression)min).Value); } else { condition = new BinaryExpression( BinaryOperatorType.GreaterThan, endRef, numRef); } } else { if ((max.NodeType == NodeType.IntegerLiteralExpression) && (min.NodeType == NodeType.IntegerLiteralExpression)) { run = (((IntegerLiteralExpression)max).Value < ((IntegerLiteralExpression)min).Value); } else { condition = new BinaryExpression( BinaryOperatorType.LessThan, endRef, numRef); } } } else { if ((max.NodeType == NodeType.IntegerLiteralExpression) && (min.NodeType == NodeType.IntegerLiteralExpression)) { if (((IntegerLiteralExpression)max).Value < ((IntegerLiteralExpression)min).Value) { condition = new BinaryExpression( BinaryOperatorType.GreaterThan, stepRef, CodeBuilder.CreateIntegerLiteral(0)); } else { condition = new BinaryExpression( BinaryOperatorType.LessThan, stepRef, CodeBuilder.CreateIntegerLiteral(0)); } } else { condition = new BinaryExpression( BinaryOperatorType.Or, new BinaryExpression( BinaryOperatorType.And, new BinaryExpression( BinaryOperatorType.LessThan, stepRef, CodeBuilder.CreateIntegerLiteral(0)), new BinaryExpression( BinaryOperatorType.GreaterThan, endRef, numRef)), new BinaryExpression( BinaryOperatorType.And, new BinaryExpression( BinaryOperatorType.GreaterThan, stepRef, CodeBuilder.CreateIntegerLiteral(0)), new BinaryExpression( BinaryOperatorType.LessThan, endRef, numRef))); } } // raise ArgumentOutOfRangeException("step") if (__step < 0 and __end > __begin) or (__step > 0 and __end < __begin) Statement statement = CodeBuilder.RaiseException( body.LexicalInfo, TypeSystemServices.Map(System_ArgumentOutOfRangeException_ctor), CodeBuilder.CreateStringLiteral("step")); if (condition != null) { IfStatement ifStatement = new IfStatement(body.LexicalInfo); ifStatement.TrueBlock = new Block(); ifStatement.Condition = condition; ifStatement.TrueBlock.Add(statement); body.Add(ifStatement); } else if (run) { body.Add(statement); } // __end = __num + __step * cast(int, Math.Ceiling((__end - __num)/cast(double, __step))) if ((step.NodeType == NodeType.IntegerLiteralExpression) && (max.NodeType == NodeType.IntegerLiteralExpression) && (min.NodeType == NodeType.IntegerLiteralExpression)) { int stepVal = (int)((IntegerLiteralExpression)step).Value; int maxVal = (int)((IntegerLiteralExpression)max).Value; int minVal = (int)((IntegerLiteralExpression)min).Value; endRef = CodeBuilder.CreateIntegerLiteral( minVal + stepVal * (int)System.Math.Ceiling((maxVal - minVal) / ((double)stepVal))); } else { Expression endBak = endRef; if (max.NodeType == NodeType.IntegerLiteralExpression) { InternalLocal endVar = CodeBuilder.DeclareTempLocal( _current, TypeSystemServices.IntType); endRef = CodeBuilder.CreateReference(endVar); } body.Add( CodeBuilder.CreateAssignment( endRef, CodeBuilder.CreateBoundBinaryExpression( TypeSystemServices.IntType, BinaryOperatorType.Addition, numRef, CodeBuilder.CreateBoundBinaryExpression( TypeSystemServices.IntType, BinaryOperatorType.Multiply, stepRef, CodeBuilder.CreateCast( TypeSystemServices.IntType, CodeBuilder.CreateMethodInvocation( TypeSystemServices.Map(System_Math_Ceiling), CodeBuilder.CreateBoundBinaryExpression( TypeSystemServices.DoubleType, BinaryOperatorType.Division, CodeBuilder.CreateBoundBinaryExpression( TypeSystemServices.IntType, BinaryOperatorType.Subtraction, endBak, numRef), CodeBuilder.CreateCast( TypeSystemServices.DoubleType, stepRef)))))))); } } // while __num != __end: WhileStatement ws = new WhileStatement(node.LexicalInfo); BinaryOperatorType op = BinaryOperatorType.Inequality; if (stepRef.NodeType == NodeType.IntegerLiteralExpression) { if (((IntegerLiteralExpression)stepRef).Value > 0) { op = BinaryOperatorType.LessThan; } else { op = BinaryOperatorType.GreaterThan; } } ws.Condition = new BinaryExpression( op, numRef, endRef); // item = __num ws.Block.Add( CodeBuilder.CreateAssignment( CodeBuilder.CreateReference((InternalLocal)declarations[0].Entity), numRef)); Block rawBlock = new Block(); rawBlock["checked"] = false; // __num += __step rawBlock.Add( CodeBuilder.CreateAssignment( numRef, CodeBuilder.CreateBoundBinaryExpression( TypeSystemServices.IntType, BinaryOperatorType.Addition, numRef, stepRef))); ws.Block.Add(rawBlock as Statement); // ws.Block.Add(node.Block); body.Add(ws); ReplaceCurrentNode(body); } /// /// Optimize the for item in array construct /// /// the for statement to check private void CheckForItemInArrayLoop(ForStatement node) { IType enumeratorType = GetExpressionType(node.Iterator); IType enumeratorItemType = TypeSystemServices.GetEnumeratorItemType(enumeratorType); if ((!enumeratorType.IsArray) || (((ArrayType)enumeratorType).GetArrayRank() > 1) || (enumeratorItemType is InternalCallableType)) { return; } DeclarationCollection declarations = node.Declarations; Block body = new Block(node.LexicalInfo); InternalLocal numVar = CodeBuilder.DeclareTempLocal( _current, TypeSystemServices.IntType); Expression numRef = CodeBuilder.CreateReference(numVar); // __num = 0 body.Add( CodeBuilder.CreateAssignment( numRef, CodeBuilder.CreateIntegerLiteral(0))); InternalLocal arrayVar = CodeBuilder.DeclareTempLocal( _current, node.Iterator.ExpressionType); ReferenceExpression arrayRef = CodeBuilder.CreateReference(arrayVar); // __arr = body.Add( CodeBuilder.CreateAssignment( arrayRef, node.Iterator)); InternalLocal endVar = CodeBuilder.DeclareTempLocal( _current, TypeSystemServices.IntType); ReferenceExpression endRef = CodeBuilder.CreateReference(endVar); // __end = __arr.Length body.Add( CodeBuilder.CreateAssignment( endRef, CodeBuilder.CreateMethodInvocation( arrayRef, Array_get_Length))); // while __num < __end: WhileStatement ws = new WhileStatement(node.LexicalInfo); ws.Condition = new BinaryExpression( BinaryOperatorType.LessThan, numRef, endRef); Block rawBlock = new Block(); rawBlock["rawarrayindexing"] = true; if (1 == declarations.Count) { // item = arr[__num] rawBlock.Add( CodeBuilder.CreateAssignment( CodeBuilder.CreateReference((InternalLocal)declarations[0].Entity), new SlicingExpression( arrayRef, numRef))); } else { // alpha, bravo, charlie = arr[__num] UnpackExpression( rawBlock, CodeBuilder.CreateCast( enumeratorItemType, new SlicingExpression( arrayRef, numRef)), node.Declarations); } ws.Block.Add(rawBlock as Statement); rawBlock = new Block(); rawBlock["checked"] = false; // __num += 1 rawBlock.Add( CodeBuilder.CreateAssignment( numRef, CodeBuilder.CreateBoundBinaryExpression( TypeSystemServices.IntType, BinaryOperatorType.Addition, numRef, CodeBuilder.CreateIntegerLiteral(1)))); ws.Block.Add(rawBlock as Statement); // ws.Block.Add(node.Block); body.Add(ws); ReplaceCurrentNode(body); } /// /// Unpacks an expression onto a list of declarations. /// /// Block this takes place in /// expression to explode /// list of declarations to set void UnpackExpression(Block block, Expression expression, DeclarationCollection declarations) { NormalizeIterationStatements.UnpackExpression(CodeBuilder, _current, block, expression, declarations); } } }