Beispiel #1
0
        List<ByteCode> StackAnalysis(MethodDefinition methodDef)
        {
            // Create temporary structure for the stack analysis
            List<ByteCode> body = new List<ByteCode>(methodDef.Body.Instructions.Count);
            foreach(Instruction inst in methodDef.Body.Instructions) {
                OpCode opCode  = inst.OpCode;
                object operand = inst.Operand;
                MethodBodyRocks.ExpandMacro(ref opCode, ref operand, methodDef.Body);
                ByteCode byteCode = new ByteCode() {
                    Offset      = inst.Offset,
                    EndOffset   = inst.Next != null ? inst.Next.Offset : methodDef.Body.CodeSize,
                    OpCode      = opCode,
                    Operand     = operand,
                    PopCount    = inst.GetPopCount(),
                    PushCount   = inst.GetPushCount()
                };
                instrToByteCode[inst] = byteCode;
                body.Add(byteCode);
            }
            for (int i = 0; i < body.Count - 1; i++) {
                body[i].Next = body[i + 1];
            }

            Queue<ByteCode> agenda = new Queue<ByteCode>();

            // Add known states
            body[0].StackBefore = new List<StackSlot>();
            agenda.Enqueue(body[0]);

            if(methodDef.Body.HasExceptionHandlers) {
                foreach(ExceptionHandler ex in methodDef.Body.ExceptionHandlers) {
                    ByteCode tryStart = instrToByteCode[ex.TryStart];
                    tryStart.StackBefore = new List<StackSlot>();
                    agenda.Enqueue(tryStart);

                    ByteCode handlerStart = instrToByteCode[ex.HandlerType == ExceptionHandlerType.Filter ? ex.FilterStart : ex.HandlerStart];
                    handlerStart.StackBefore = new List<StackSlot>();
                    if (ex.HandlerType == ExceptionHandlerType.Catch || ex.HandlerType == ExceptionHandlerType.Filter) {
                        handlerStart.StackBefore.Add(new StackSlot(null));
                    }
                    agenda.Enqueue(handlerStart);

                    // Control flow is not required to reach endfilter
                    if (ex.HandlerType == ExceptionHandlerType.Filter) {
                        ByteCode endFilter = instrToByteCode[ex.FilterEnd.Previous];
                        endFilter.StackBefore = new List<StackSlot>();
                    }
                }
            }

            // Process agenda
            while(agenda.Count > 0) {
                ByteCode byteCode = agenda.Dequeue();

                // Calculate new stack
                List<StackSlot> newStack = byteCode.CloneStack(byteCode.PopCount);
                for (int i = 0; i < byteCode.PushCount; i++) {
                    newStack.Add(new StackSlot(byteCode));
                }

                // Apply the state to any successors
                List<ByteCode> branchTargets = new List<ByteCode>();
                if (byteCode.OpCode.CanFallThough()) {
                    branchTargets.Add(byteCode.Next);
                }
                if (byteCode.OpCode.IsBranch()) {
                    if (byteCode.Operand is Instruction[]) {
                        foreach(Instruction inst in (Instruction[])byteCode.Operand) {
                            ByteCode target = instrToByteCode[inst];
                            branchTargets.Add(target);
                            // The target of a branch must have label
                            if (target.Label == null) {
                                target.Label = new ILLabel() { Name = target.Name };
                            }
                        }
                    } else {
                        ByteCode target = instrToByteCode[(Instruction)byteCode.Operand];
                        branchTargets.Add(target);
                        // The target of a branch must have label
                        if (target.Label == null) {
                            target.Label = new ILLabel() { Name = target.Name };
                        }
                    }
                }
                foreach (ByteCode branchTarget in branchTargets) {
                    if (branchTarget.StackBefore == null) {
                        branchTarget.StackBefore = newStack;
                        // Do not share one stack for several bytecodes
                        if (branchTargets.Count > 1) {
                            branchTarget.StackBefore = branchTarget.CloneStack(0);
                        }
                        agenda.Enqueue(branchTarget);
                    } else {
                        if (branchTarget.StackBefore.Count != newStack.Count) {
                            throw new Exception("Inconsistent stack size at " + byteCode.Name);
                        }

                        // Merge stacks
                        bool modified = false;
                        for (int i = 0; i < newStack.Count; i++) {
                            List<ByteCode> oldPushedBy = branchTarget.StackBefore[i].PushedBy;
                            List<ByteCode> newPushedBy = oldPushedBy.Union(newStack[i].PushedBy).ToList();
                            if (newPushedBy.Count > oldPushedBy.Count) {
                                branchTarget.StackBefore[i].PushedBy = newPushedBy;
                                modified = true;
                            }
                        }

                        if (modified) {
                            agenda.Enqueue(branchTarget);
                        }
                    }
                }
            }

            // Genertate temporary variables to replace stack
            foreach(ByteCode byteCode in body) {
                int argIdx = 0;
                int popCount = byteCode.PopCount ?? byteCode.StackBefore.Count;
                for (int i = byteCode.StackBefore.Count - popCount; i < byteCode.StackBefore.Count; i++) {
                    StackSlot arg = byteCode.StackBefore[i];
                    ILVariable tmpVar = new ILVariable() { Name = string.Format("arg_{0:X2}_{1}", byteCode.Offset, argIdx), IsGenerated = true };
                    arg.LoadFrom = tmpVar;
                    foreach(ByteCode pushedBy in arg.PushedBy) {
                        // TODO: Handle exception variables
                        if (pushedBy != null) {
                            if (pushedBy.StoreTo == null) {
                                pushedBy.StoreTo = new List<ILVariable>(1);
                            }
                            pushedBy.StoreTo.Add(tmpVar);
                        }
                    }
                    if (arg.PushedBy.Count == 1) {
                        allowInline[tmpVar] = true;
                    }
                    argIdx++;
                }
            }

            // Convert local varibles
            Variables = methodDef.Body.Variables.Select(v => new ILVariable() { Name = string.IsNullOrEmpty(v.Name) ?  "var_" + v.Index : v.Name, Type = v.VariableType }).ToList();
            int[] numReads  = new int[Variables.Count];
            int[] numWrites = new int[Variables.Count];
            foreach(ByteCode byteCode in body) {
                if (byteCode.OpCode == OpCodes.Ldloc) {
                    int index = ((VariableDefinition)byteCode.Operand).Index;
                    byteCode.Operand = Variables[index];
                    numReads[index]++;
                }
                if (byteCode.OpCode == OpCodes.Stloc) {
                    int index = ((VariableDefinition)byteCode.Operand).Index;
                    byteCode.Operand = Variables[index];
                    numWrites[index]++;
                }
            }

            // Find which variables we can inline
            if (this.optimize) {
                for (int i = 0; i < Variables.Count; i++) {
                    if (numReads[i] == 1 && numWrites[i] == 1) {
                        allowInline[Variables[i]] = true;
                    }
                }
            }

            // Convert branch targets to labels
            foreach(ByteCode byteCode in body) {
                if (byteCode.Operand is Instruction[]) {
                    List<ILLabel> newOperand = new List<ILLabel>();
                    foreach(Instruction target in (Instruction[])byteCode.Operand) {
                        newOperand.Add(instrToByteCode[target].Label);
                    }
                    byteCode.Operand = newOperand.ToArray();
                } else if (byteCode.Operand is Instruction) {
                    byteCode.Operand = instrToByteCode[(Instruction)byteCode.Operand].Label;
                }
            }

            return body;
        }