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; }