private static FunctionSpecializationResult SpecializeFunction(
		                                                               ILFunction function, 
		                                                               Dictionary<string, object> pars, 
		                                                               List<string> forceDynamic)
        {
            var source = new StringBuilder(1024 * 1024);
            int LabelId = 1;

            var visited = new SpecPointContainer();
            var q = new Queue<SpecPoint>();
            var initEnv = new AbstractEnvironment();
            foreach (var kvp in pars) {
                initEnv.SetValue(kvp.Key, kvp.Value);
            }
            foreach (var kvp in function.LocalTypes) {
                if (pars.ContainsKey(kvp.Key))
                    continue;

                object val = null;
                if (kvp.Value.TypeEnum == L1Runtime.SyntaxTree.VariableTypeEnum.Integer)
                    val = 0;
                if (kvp.Value.TypeEnum == L1Runtime.SyntaxTree.VariableTypeEnum.Bool)
                    val = false;
                if (kvp.Value.TypeEnum == L1Runtime.SyntaxTree.VariableTypeEnum.Char)
                    val = (char)0;
                initEnv.SetValue(kvp.Key, val);
            }
            foreach (var d in forceDynamic) {
                initEnv.SetValue(d, Dynamic.Value);
            }

            initEnv = _bindTimeAnalyze(function, initEnv);
            initEnv.Trace();

            Dictionary<VariableType, List<string>> localVarsInfo = new Dictionary<VariableType, List<string>>();
            foreach (var kvp in function.LocalTypes) {
                if (initEnv.IsDynamic(kvp.Key) && !function.Parameters.Contains(kvp.Key)) {
                    if (localVarsInfo.ContainsKey(kvp.Value) == false) {
                        localVarsInfo.Add(kvp.Value, new List<string>());
                    }
                    localVarsInfo[kvp.Value].Add(kvp.Key);
                }
            }
            foreach (var kvp in localVarsInfo) {
                source.Append("\t"); source.Append(kvp.Key.ToCompileableString()); source.Append(" ");
                for (int i = 0; i < kvp.Value.Count; ++i) {
                    source.Append(kvp.Value[i]);
                    if (i != kvp.Value.Count - 1)
                        source.Append(", ");
                }
                source.Append(";"); source.Append(System.Environment.NewLine);
            }

            foreach (var kvp in pars) {
                if (kvp.Value != Dynamic.Value && initEnv.IsDynamic(kvp.Key)) {
                    source.Append("\t");
                    source.Append(function.LocalTypes[kvp.Key].ToCompileableString());
                    source.Append(" ");
                    source.Append(kvp.Key); source.Append(" := "); source.Append(RenderConst(kvp.Value));
                    source.Append(";"); source.AppendLine();
                }
            }

            source.Append("* Begin of function specialized body"); source.Append(System.Environment.NewLine);

            var initialSp = new SpecPoint { Env = initEnv, P = 1, L = LabelId++ } ;
            q.Enqueue(initialSp);
            initialSp.Env.Freeze();
            visited.AddSpecPoint(initialSp);

            int operations = 0;
            var calls = new List<FunctionCallContainer>();
            while (q.Count != 0) {
                operations++;

                var sp = q.Dequeue();
                var env = sp.Env.Clone();

                source.Append("L_"); source.Append(sp.L); source.Append(":"); source.Append(System.Environment.NewLine);

                bool stopped = false;
                int p = sp.P;

                while (!stopped) {
                    var instr = function.Body[p - 1];

                    if (instr is ILExpression) {
                        var expr = instr as ILExpression;

                        stopped = SpecializeExpression(function, expr, env, source, calls);

                        p++;
                    }
                    else if (instr is ILBranch) {
                        var br = (instr as ILBranch);

                        var condVar = br.Condition.Const.ToString();
                        if (env.IsDynamic(condVar)) {

                            env.Freeze();
                            var succSp = visited.GetSpecPoint(br.SuccessJump, env);
                            var failSp = visited.GetSpecPoint(br.FailJump, env);

                            if (succSp == null) {
                                succSp = new SpecPoint { Env = env, P = br.SuccessJump, L = LabelId++ };
                                q.Enqueue(succSp);
                                visited.AddSpecPoint(succSp);
                            }
                            if (failSp == null) {
                                failSp = new SpecPoint { Env = env, P = br.FailJump, L = LabelId++ };
                                q.Enqueue(failSp);
                                visited.AddSpecPoint(failSp);
                            }

                            source.Append("\tif "); source.Append(condVar); source.Append(" then ");
                            source.Append("goto "); source.Append("L_"); source.Append(succSp.L);
                            source.Append(" else ");
                            source.Append("goto "); source.Append("L_"); source.Append(failSp.L);
                            source.Append(" end;"); source.Append(System.Environment.NewLine);

                            stopped = true;
                        }
                        else {
                            var cond = (bool)env.GetValue(condVar);
                            if (cond)
                                p = br.SuccessJump;
                            else
                                p = br.FailJump;
                        }
                    }
                    else if (instr is ILGoto) {
                        p = (instr as ILGoto).GoTo;
                    }
                    else if (instr is ILReturn) {
                        var ret = (instr as ILReturn);

                        if (ret.Return == null) {
                            source.Append("\treturn;"); source.Append(System.Environment.NewLine);
                        }
                        else {
                            source.Append("\treturn ");
                            var retVar = ret.Return.Const.ToString();
                            if (env.IsDynamic(retVar)) {
                                source.Append(retVar);
                            }
                            else {
                                m_renderConst(source, env.GetValue(retVar));
                            }
                            source.Append(";"); source.Append(System.Environment.NewLine);
                        }

                        stopped = true;
                    }
                }

                //TODO: Stop if too big query and try with more dynamic variables

                if (operations >= MaximimOperations) {
                    var ch = visited.GetMostChangeableVariables();
                    forceDynamic = forceDynamic.Union(ch).ToList();

                    var res1 = SpecializeFunction(function, pars, forceDynamic);
                    return res1;
                }
            }
            source.Append("0").AppendLine();

            //System.Console.WriteLine(source.ToString());

            var r = new FunctionSpecializationResult { FunctionCallsNeedToResolve = calls, Source = source, Function = function, SpecializationRules = pars };
            return r;
        }
        private static void SpecializeFunctionCall(ILFunction context, 
		                                           ILExpression fcall, 
		                                           string resultVar, 
		                                           StringBuilder source, 
		                                           AbstractEnvironment env, 
		                                           List<FunctionCallContainer> calls)
        {
            var f = fcall.Function;
            //Stdlib function
            if (f.EmbeddedBody != null) {
                if (f.CanBeCalculatedWithoutRun && !fcall.VAList.Any(pexpr => env.IsDynamic(pexpr.Const.ToString()))) {

                    var args = fcall.VAList.Select(ilexpr => ilexpr.Eval(env))
                        .Select(o => (o is Int32) ? o : ( (o is Boolean) ? o : L1Runtime.L1Runtime.GetArrayFromObjectArray((object[])o))).ToArray();
                    var result = f.EmbeddedBody.Invoke(null, args);

                    if (env.IsDynamic(resultVar)) {
                        source.Append("\t"); source.Append(resultVar); source.Append(" := "); RenderConst(result); source.Append(");"); source.Append(System.Environment.NewLine);
                    } else {
                        if (result is L1Runtime.L1Array<int>) {
                            var l1arr = result as L1Runtime.L1Array<int>;
                            var a = new object[l1arr.GetLength()];
                            for (int i = 0; i < a.Length; ++i) {
                                a[i] = l1arr.GetValue(i);
                            }
                            result = a;
                        }
                        env.SetValue(resultVar, result);
                    }
                } else {
                    source.Append("\t"); source.Append(resultVar); source.Append(" := "); source.Append(f.Name); source.Append("(");
                    for (int i = 0; i < fcall.VAList.Count; ++i) {
                        var p = fcall.VAList[i].Const.ToString();
                        if (env.IsDynamic(p)) {
                            source.Append(p);
                        } else {
                            source.Append(RenderConst(env.GetValue(p)));
                        }
                        if (i != fcall.VAList.Count - 1)
                            source.Append(", ");
                    }
                    source.Append(");"); source.Append(System.Environment.NewLine);
                }
            } else {
                if (f.CanBeCalculatedWithoutRun && !fcall.VAList.Any(pexpr => env.IsDynamic(pexpr.Const.ToString()))) {
                    var args = fcall.VAList.Select(ilexpr => ilexpr.Eval(env)).ToArray();
                    var res = f.Call(args);

                    if (env.IsDynamic(resultVar)) {
                        source.Append("\t").Append(resultVar).Append(" := ").Append(RenderConst(res)).Append(";").AppendLine();
                    } else {
                        env.SetValue(resultVar, res);
                    }
                }
                else {
                    var functionCallRef = new FunctionCallContainer {
                        Function = fcall.Function,
                        SourceFunction = context,
                        ResultVariable = resultVar,
                        Parameters = fcall.VAList.Select(ilexpr => env.IsDynamic(ilexpr.Const.ToString()) ? ilexpr.Const.ToString() : env.GetValue(ilexpr.Const.ToString())).ToList()
                    };
                    calls.Add(functionCallRef);
                    source.Append("\t").Append(functionCallRef.ToString()).AppendLine();
                }
            }
        }
        private static bool SpecializeExpression(ILFunction f, ILExpression expr, AbstractEnvironment env, StringBuilder source, List<FunctionCallContainer> calls)
        {
            if (expr.Type == ILExpressionType.Assign) {

                var left = expr.LeftNode;
                var right = expr.RightNode;
                if (left.Type == ILExpressionType.VariableAccess) {
                    var varName = left.Const.ToString();
                    try {
                        var rightRed = right.AbstactReduce(varName, env, f.LocalTypes);
                        if (rightRed is ILExpression) {
                                var fcall = rightRed as ILExpression;

                                SpecializeFunctionCall(f, fcall, varName, source, env, calls);
                                //source.Append("<<FUNCTION_CALL>>");
                        }
                        else if (env.IsDynamic(varName)) {

                            //source.Append(varName); source.Append(" := ");

                            if (rightRed is string) {
                                source.Append("\t"); source.Append(varName); source.Append(" := ");
                                source.Append((string)rightRed);
                                source.Append(";"); source.Append(System.Environment.NewLine);
                            }
                            else {
                                source.Append("\t"); source.Append(varName); source.Append(" := ");
                                source.Append(RenderConst(rightRed));
                                source.Append(";"); source.Append(System.Environment.NewLine);
                            }
                        }
                        else {
                            object val = rightRed;

                            System.Diagnostics.Debug.Assert(val is string == false);
                            System.Diagnostics.Debug.Assert(val is ILExpression == false);

                            env.SetValue(varName, val);
                        }
                    }
                    catch (NullReferenceException) {
                        source.Append("\t__spec_raise_null_ref_exception();"); source.Append(System.Environment.NewLine);
                    }
                    catch (IndexOutOfRangeException) {
                        source.Append("\t__spec_raise_index_out_of_range_exception();"); source.Append(System.Environment.NewLine);
                    }
                }
                else {
                    System.Diagnostics.Debug.Assert(left.Type == ILExpressionType.ArrayAccess);

                    try {
                        var arrayName = left.LeftNode.Const.ToString();
                        object index = null;
                        if (env.IsDynamic(left.RightNode.Const.ToString())) {
                            index = left.RightNode.Const.ToString();
                        } else {
                            index = env.GetValue(left.RightNode.Const.ToString());
                        }

                        object rightVar = null;
                        if (env.IsDynamic(right.Const.ToString())) {
                            rightVar = right.Const.ToString();
                        } else {
                            rightVar = env.GetValue(right.Const.ToString());
                        }

                        if (env.IsDynamic(arrayName)) {
                            source.Append("\t"); source.Append(arrayName); source.Append("["); source.Append(index); source.Append("] := ");
                            source.Append(rightVar); source.Append(";"); source.Append(System.Environment.NewLine);
                        }
                        else {
                            object[] a = (object[])env.GetValue(arrayName);
                            a[Convert.ToInt32(index)] = rightVar;
                        }
                    }
                    catch (NullReferenceException) {
                        source.Append("\t__spec_raise_null_ref_exception();"); source.Append(System.Environment.NewLine);
                    }
                    catch (IndexOutOfRangeException) {
                        source.Append("\t__spec_raise_index_out_of_range_exception();"); source.Append(System.Environment.NewLine);
                    }
                }

            }
            else if (expr.Type == ILExpressionType.FunctionCall) {
                source.Append("\t"); source.Append(expr.Const.ToString()); source.Append("(");
                for (int i = 0; i < expr.VAList.Count; ++i) {
                    string p = null;
                    if (env.IsDynamic(expr.VAList[i].Const.ToString())) {
                        p = expr.VAList[i].Const.ToString();
                    } else {
                        p = RenderConst(env.GetValue(expr.VAList[i].Const.ToString()));
                    }
                    source.Append(p);
                    if (i != expr.VAList.Count - 1)
                        source.Append(", ");
                }
                source.Append(");"); source.Append(System.Environment.NewLine);
            }

            return false;
        }
        internal static AbstractEnvironment _bindTimeAnalyze(ILFunction func, AbstractEnvironment initialEnvironment)
        {
            var env = initialEnvironment;
            int dynamicAdded = 0;

            do {
                dynamicAdded = 0;
                foreach (var instr in func.Body) {
                    if (instr is ILExpression) {
                        var expr = instr as ILExpression;
                        if (expr.Type == ILExpressionType.Assign) {

                            //Case 1 - assign to local variable
                            if (expr.LeftNode.Type == ILExpressionType.VariableAccess) {
                                if (m_simpleExprContainsDynamicVariables(expr.RightNode, env)) {
                                    //If reading from static array by dynamic index
                                    if (expr.RightNode.Type == ILExpressionType.ArrayAccess) {
                                        if (env.IsDynamic(expr.RightNode.LeftNode.Const.ToString()) == false) {
                                            env.SetValue(expr.RightNode.LeftNode.Const.ToString(), Dynamic.Value);
                                            dynamicAdded++;
                                        }
                                    }
                                    if (env.IsDynamic(expr.LeftNode.Const.ToString()) == false) {
                                        env.SetValue(expr.LeftNode.Const.ToString(), Dynamic.Value);
                                        dynamicAdded++;
                                    }
                                }
                            }
                            //Case 2 - assign to array
                            else if (expr.LeftNode.Type == ILExpressionType.ArrayAccess) {

                                // A(s,d)[D] = S, D -> A:=D
                                if (m_simpleExprContainsDynamicVariables(expr.LeftNode, env)) {
                                    if (env.IsDynamic(expr.LeftNode.LeftNode.Const.ToString()) == false) {
                                        env.SetValue(expr.LeftNode.LeftNode.Const.ToString(), Dynamic.Value);
                                        dynamicAdded++;
                                    }
                                }
                                // A(s)[S] = D -> A:=D
                                else if (m_simpleExprContainsDynamicVariables(expr.RightNode, env)) {
                                    if (env.IsDynamic(expr.LeftNode.LeftNode.Const.ToString()) == false) {
                                        env.SetValue(expr.LeftNode.LeftNode.Const.ToString(), Dynamic.Value);
                                        dynamicAdded++;
                                    }
                                }
                                // A(s)[S] = S - do nothing, array is static (in this scope

                            }

                        }

                    }
                }
            } while (dynamicAdded != 0);

            return env;
        }
        public static List<ILFunction> EmitProgram(L1Program program)
        {
            f_functionFindMap = new Dictionary<FunctionDefinition, ILFunction>();
            f_ilProgram = new List<ILFunction>();
            foreach (var fDef in program.Functions)
            {
                ILFunction ilFun = new ILFunction();
                ilFun.ReturnType = fDef.Header.ReturnType;
                if (fDef.Header.ReturnType == null) {
                    ilFun.IsVoidReturn = true;
                }
                ilFun.Name = fDef.Header.FunctionName;
                foreach (var p in fDef.Header.Parameters)
                {
                    ilFun.Parameters.Add(p.Name);
                }
                if (fDef.IsEmbedded)
                {
                    ilFun.EmbeddedBody = fDef.Body;
                    bool t = !L1Runtime.DynamicResultAttribute.IsDynamic(fDef.Body);
                    ilFun.CanBeCalculatedWithoutRun = t;
                }
                else
                {
                    f_ilProgram.Add(ilFun);
                }
                f_functionFindMap.Add(fDef, ilFun);
            }

            foreach (var KV in f_functionFindMap)
            {
                if (KV.Key.IsEmbedded == false)
                {
                    //EmitStatementList(KV.Key.Statements, KV.Value.Body);
                    CurrentFunction = KV.Value;

                    foreach (var p in KV.Key.Header.Parameters) {
                        CurrentFunction.AddLocal(p.Name, p.Type);
                    }

                    _EmitStatementList(KV.Key.Statements, KV.Value.Body);
                    KV.Value.Body = ReduceList(KV.Value.Body);
                    CurrentFunction = null;
                }
            }

            MarkDynamicFunctions();

            //Debug output
            foreach (var KV in f_functionFindMap)
            {
                if (KV.Key.IsEmbedded == false)
                {
                    Console.WriteLine (String.Format(".specentry {0} {1}", KV.Value.Name, KV.Value.CanBeCalculatedWithoutRun ? "" : ".dynamic"));
                    foreach (ILInstuction inst in KV.Value.Body)
                        Console.WriteLine(String.Format("{0}\t\t{1}", inst.Line, inst));

                    Console.WriteLine(".end");
                    Console.WriteLine("");
                    Console.WriteLine (".f_calls " + KV.Value.Name);
                    var fcalls = FindAllFunctionCalls(KV.Value);
                    foreach (var fcall in fcalls)
                        Console.WriteLine(String.Format("{0}", fcall));
                    Console.WriteLine(".end");
                    Console.WriteLine("");
                }
            }

            return f_ilProgram;
        }
 public static List<ILExpression> FindAllFunctionCalls(ILFunction function, Func<ILExpression, bool> predicate)
 {
     List<ILExpression> result = new List<ILExpression>();
     foreach (ILInstuction inst in function.Body)
     {
         if (inst is ILExpression)
             result.AddRange(FindAllFunctionCallsInExpr((ILExpression)inst).Where(predicate));
         else if (inst is ILBranch)
             result.AddRange(FindAllFunctionCallsInExpr((inst as ILBranch).Condition).Where(predicate));
         else if (inst is ILReturn)
             result.AddRange(FindAllFunctionCallsInExpr((inst as ILReturn).Return).Where(predicate));
     }
     return result;
 }
 public static List<ILExpression> FindAllFunctionCalls(ILFunction function)
 {
     return FindAllFunctionCalls(function, ilExpr => true);
 }