示例#1
0
        private void ApplyTransformations(ILCompilationUnit result, VMConstants constants)
        {
            var pipeline = new IILAstTransform[]
            {
                new StackFrameTransform(),
                new SsaTransform(),
                new TransformLoop("Expression Simplification", 5, new IChangeAwareILAstTransform[]
                {
                    new VariableInliner(),
                    new PushMinimizer(),
                    new LogicSimplifier(),
                    new FlagOperationSimplifier(constants),
                }),
                new PhiRemovalTransform(),
            };

            foreach (var transform in pipeline)
            {
                if (transform is TransformLoop loop)
                {
                    loop.TransformStart += (sender, args) => OnTransformStart(args);
                    loop.TransformEnd   += (sender, args) => OnTransformEnd(args);
                }

                Logger.Debug2(Tag, $"Applying {transform.Name}...");
                OnTransformStart(new ILTransformEventArgs(result, transform, 1));
                transform.ApplyTransformation(result, Logger);
                OnTransformEnd(new ILTransformEventArgs(result, transform, 1));
            }
        }
示例#2
0
        private static void IntroduceRegisterVariables(ILCompilationUnit result)
        {
            for (int i = 0; i < (int)VMRegisters.Max; i++)
            {
                var register    = (VMRegisters)i;
                var registerVar = result.GetOrCreateVariable(register.ToString());

                registerVar.VariableType = register == VMRegisters.FL
                    ? VMType.Byte
                    : VMType.Object;
            }
        }
示例#3
0
        public IEnumerable <ILVariableExpression> VisitCompilationUnit(ILCompilationUnit unit)
        {
            var result = new List <ILVariableExpression>();

            foreach (var node in unit.ControlFlowGraph.Nodes)
            {
                var block = (ILAstBlock)node.UserData[ILAstBlock.AstBlockProperty];
                result.AddRange(block.AcceptVisitor(this));
            }

            return(result);
        }
示例#4
0
        private ILExpression BuildExpression(ILInstruction instruction, ILCompilationUnit result)
        {
            IILArgumentsProvider expression;

            switch (instruction.OpCode.Code)
            {
            case ILCode.VCALL:
                expression = new ILVCallExpression((VCallAnnotation)instruction.Annotation);
                break;

            case ILCode.PUSHR_BYTE:
            case ILCode.PUSHR_WORD:
            case ILCode.PUSHR_DWORD:
            case ILCode.PUSHR_QWORD:
            case ILCode.PUSHR_OBJECT:
                expression = BuildPushrExpression(instruction, result);
                break;

            default:
                expression = new ILInstructionExpression(instruction);
                break;
            }


            for (int i = 0; i < instruction.Dependencies.Count; i++)
            {
                ILExpression argument;
                var          firstDataSource = instruction.Dependencies[i].DataSources.First();
                if (firstDataSource.Offset == InstructionProcessor.PushExceptionOffset)
                {
                    var exceptionType = (ITypeDefOrRef)firstDataSource.Operand;
                    argument = new ILExceptionExpression(exceptionType);
                }
                else
                {
                    // Get the variable containing the value of the argument and add it as an argument to the expression.
                    string variableName = GetOperandVariableName(instruction, i);
                    if (_sharedVariables.TryGetValue(variableName, out string realName))
                    {
                        variableName = realName;
                    }

                    argument = new ILVariableExpression(
                        result.GetOrCreateVariable(variableName));
                }

                expression.Arguments.Add(argument);
            }

            return((ILExpression)expression);
        }
示例#5
0
        private static ILStatement BuildCallStatement(ILCompilationUnit result, ILInstruction instruction,
                                                      ILExpression expression)
        {
            // CALL instructions that call non-void methods store the result in R0.
            // TODO: Respect frame layout instead of hardcoding R0 as return value.

            var callAnnotation = (CallAnnotation)instruction.Annotation;

            var statement = callAnnotation.Function.FrameLayout.ReturnsValue
                ? (ILStatement) new ILAssignmentStatement(
                result.GetOrCreateVariable(nameof(VMRegisters.R0)), expression)
                : new ILExpressionStatement(expression);

            return(statement);
        }
示例#6
0
        private void BuildAstBlocks(ILCompilationUnit result, IDictionary <int, ILVariable> resultVariables)
        {
            foreach (var node in result.ControlFlowGraph.Nodes)
            {
                var ilBlock  = (ILBasicBlock)node.UserData[ILBasicBlock.BasicBlockProperty];
                var astBlock = new ILAstBlock(node);

                foreach (var instruction in ilBlock.Instructions)
                {
                    astBlock.Statements.Add(BuildStatement(result, resultVariables, instruction));
                }

                node.UserData[ILAstBlock.AstBlockProperty] = astBlock;
            }
        }
示例#7
0
        private ILCompilationUnit BuildBasicAst(ControlFlowGraph graph, IFrameLayout frameLayout)
        {
            var result = new ILCompilationUnit(graph, frameLayout);

            // Introduce variables:
            Logger.Debug2(Tag, "Determining variables...");
            var resultVariables = DetermineVariables(result);

            // Build AST blocks.
            Logger.Debug2(Tag, "Building AST blocks...");
            BuildAstBlocks(result, resultVariables);

            Logger.Debug2(Tag, "Marking expressions affecting flags...");
            var marker = new FlagDataSourceMarker();

            result.AcceptVisitor(marker);

            return(result);
        }
示例#8
0
        private static ILStatement BuildPopStatement(ILCompilationUnit result, ILInstruction instruction,
                                                     ILExpression expression)
        {
            // Since we treat registers as variables, we should treat POP instructions as assignment
            // statements instead of a normal ILExpressionStatement. This makes it easier to apply
            // analysis and transformations (such as variable inlining) later, in the same way we do
            // that with normal variables.
            var register = (VMRegisters)instruction.Operand;

            // Get the variable associated to the register. FL registers need special care, as they
            // are used in various optimisation stages.
            var registerVar = register == VMRegisters.FL
                ? result.GetOrCreateFlagsVariable(new[] { instruction.Offset })
                : result.GetOrCreateVariable(register.ToString());

            var value = (ILExpression)((IILArgumentsProvider)expression).Arguments[0].Remove();

            return(new ILAssignmentStatement(registerVar, value));
        }
示例#9
0
        private ILStatement BuildStatement(ILCompilationUnit result, IDictionary <int, ILVariable> resultVariables, ILInstruction instruction)
        {
            // Build expression.
            var expression = BuildExpression(instruction, result);

            switch (instruction.OpCode.Code)
            {
            case ILCode.POP:
                return(BuildPopStatement(result, instruction, expression));

            case ILCode.CALL:
                return(BuildCallStatement(result, instruction, expression));

            case ILCode.RET:
                return(BuildRetStatement(result, instruction, expression));

            default:
                return(BuildAssignment(resultVariables, instruction, expression));
            }
        }
示例#10
0
        private static ILStatement BuildRetStatement(ILCompilationUnit result, ILInstruction instruction,
                                                     ILExpression expression)
        {
            // TODO: Respect frame layout instead of hardcoding R0 as return value.
            var returnExpr = (IILArgumentsProvider)expression;

            foreach (var use in expression.AcceptVisitor(VariableUsageCollector.Instance))
            {
                use.Variable = null;
            }

            returnExpr.Arguments.Clear();

            if (result.FrameLayout.ReturnsValue && !instruction.ProgramState.IgnoreExitKey)
            {
                var registerVar = result.GetOrCreateVariable(nameof(VMRegisters.R0));
                returnExpr.Arguments.Add(new ILVariableExpression(registerVar));
            }

            return(new ILExpressionStatement(expression));
        }
示例#11
0
 private IDictionary <int, ILVariable> DetermineVariables(ILCompilationUnit result)
 {
     IntroduceRegisterVariables(result);
     return(IntroduceResultVariables(result));
 }
示例#12
0
 protected virtual void OnInitialAstBuilt(ILCompilationUnit result)
 {
     InitialAstBuilt?.Invoke(this, result);
 }
示例#13
0
        private ILExpression BuildExpression(ILInstruction instruction, ILCompilationUnit result)
        {
            IILArgumentsProvider expression;

            switch (instruction.OpCode.Code)
            {
            case ILCode.VCALL:
                expression = new ILVCallExpression((VCallAnnotation)instruction.Annotation);
                break;

            case ILCode.PUSHR_BYTE:
            case ILCode.PUSHR_WORD:
            case ILCode.PUSHR_DWORD:
            case ILCode.PUSHR_QWORD:
            case ILCode.PUSHR_OBJECT:
                // Since we treat registers as variables, we should interpret the operand as a variable and add it
                // as an argument to the expression instead of keeping it just as an operand. This makes it easier
                // to apply analysis and transformations (such as variable inlining) later, in the same way we do
                // that with normal variables.

                expression = new ILInstructionExpression(instruction);

                ILVariable registerVar;
                if (instruction.Operand is VMRegisters.FL)
                {
                    var dataSources = instruction.ProgramState.Registers[VMRegisters.FL].DataSources
                                      .Select(i => i.Offset)
                                      .ToArray();

                    var flagsVariable = result.GetOrCreateFlagsVariable(dataSources);
                    registerVar = flagsVariable;
                }
                else
                {
                    registerVar = result.GetOrCreateVariable(instruction.Operand.ToString());
                }

                var varExpression = new ILVariableExpression(registerVar);
                expression.Arguments.Add(varExpression);
                break;

            default:
                expression = new ILInstructionExpression(instruction);
                break;
            }


            for (int i = 0; i < instruction.Dependencies.Count; i++)
            {
                ILExpression argument;
                var          firstDataSource = instruction.Dependencies[i].DataSources.First();
                if (firstDataSource.Offset == InstructionProcessor.PushExceptionOffset)
                {
                    var exceptionType = (ITypeDefOrRef)firstDataSource.Operand;
                    argument = new ILExceptionExpression(exceptionType);
                }
                else
                {
                    // Get the variable containing the value of the argument and add it as an argument to the expression.
                    string variableName = GetOperandVariableName(instruction, i);
                    if (_sharedVariables.TryGetValue(variableName, out string realName))
                    {
                        variableName = realName;
                    }

                    argument = new ILVariableExpression(
                        result.GetOrCreateVariable(variableName));
                }

                expression.Arguments.Add(argument);
            }

            return((ILExpression)expression);
        }
示例#14
0
        private void BuildAstBlocks(ILCompilationUnit result, IDictionary <int, ILVariable> resultVariables)
        {
            foreach (var node in result.ControlFlowGraph.Nodes)
            {
                var ilBlock  = (ILBasicBlock)node.UserData[ILBasicBlock.BasicBlockProperty];
                var astBlock = new ILAstBlock(node);

                foreach (var instruction in ilBlock.Instructions)
                {
                    // Build expression.
                    var expression = BuildExpression(instruction, result);

                    switch (instruction.OpCode.Code)
                    {
                    case ILCode.POP:
                    {
                        // Since we treat registers as variables, we should treat POP instructions as assignment
                        // statements instead of a normal ILExpressionStatement. This makes it easier to apply
                        // analysis and transformations (such as variable inlining) later, in the same way we do
                        // that with normal variables.

                        var registerVar = result.GetOrCreateVariable(instruction.Operand.ToString());
                        var value       = (ILExpression)((IILArgumentsProvider)expression).Arguments[0].Remove();

                        var assignment = new ILAssignmentStatement(registerVar, value);
                        astBlock.Statements.Add(assignment);
                        break;
                    }

                    case ILCode.CALL:
                    {
                        // CALL instructions that call non-void methods store the result in R0.
                        // TODO: Respect frame layout instead of hardcoding R0 as return value.

                        var callAnnotation = (CallAnnotation)instruction.Annotation;

                        var statement = callAnnotation.Function.FrameLayout.ReturnsValue
                                ? (ILStatement) new ILAssignmentStatement(
                            result.GetOrCreateVariable(VMRegisters.R0.ToString()), expression)
                                : new ILExpressionStatement(expression);

                        astBlock.Statements.Add(statement);

                        break;
                    }

                    case ILCode.RET:
                    {
                        // TODO: Respect frame layout instead of hardcoding R0 as return value.
                        var returnExpr = (IILArgumentsProvider)expression;

                        foreach (var use in expression.AcceptVisitor(VariableUsageCollector.Instance))
                        {
                            use.Variable = null;
                        }

                        returnExpr.Arguments.Clear();

                        if (result.FrameLayout.ReturnsValue && !instruction.ProgramState.IgnoreExitKey)
                        {
                            var registerVar = result.GetOrCreateVariable(VMRegisters.R0.ToString());
                            returnExpr.Arguments.Add(new ILVariableExpression(registerVar));
                        }

                        astBlock.Statements.Add(new ILExpressionStatement(expression));
                        break;
                    }

                    default:
                    {
                        // Build statement around expression.
                        var statement = resultVariables.TryGetValue(instruction.Offset, out var resultVariable)
                                ? (ILStatement) new ILAssignmentStatement(resultVariable, expression)
                                : new ILExpressionStatement(expression);

                        astBlock.Statements.Add(statement);
                        break;
                    }
                    }
                }

                node.UserData[ILAstBlock.AstBlockProperty] = astBlock;
            }
        }
示例#15
0
        private IDictionary <int, ILVariable> IntroduceResultVariables(ILCompilationUnit result)
        {
            // Determine result variables based on where the value is used by other instructions.
            // Find for each instruction the dependent instructions and assign to each of those dependent instructions
            // the same variable.

            var resultVariables = new Dictionary <int, ILVariable>();
            var instructions    = result.ControlFlowGraph.Nodes
                                  .Select(x => (ILBasicBlock)x.UserData[ILBasicBlock.BasicBlockProperty])
                                  .SelectMany(x => x.Instructions);

            foreach (var instruction in instructions)
            {
                if (instruction.OpCode.Code == ILCode.CALL)
                {
                    // Calls have implicit dependencies to the parameters pushed onto the stack.
                    // We need to add them for the expression builder to work properly.

                    var callAnnotation  = (CallAnnotation)instruction.Annotation;
                    int parametersCount = callAnnotation.Function.FrameLayout.Parameters.Count;

                    // Create a copy of the stack and pop the first dependency (the call address) from it.
                    var stackCopy = instruction.ProgramState.Stack.Copy();
                    stackCopy.Pop();

                    // TODO: Respect the frame layout rather than hardcoding it.

                    // Pop all arguments from the stack.
                    var arguments = new SymbolicValue[parametersCount];
                    for (int i = parametersCount - 1; i >= 0; i--)
                    {
                        arguments[i] = stackCopy.Pop();
                    }

                    // Add new dependencies.
                    for (int i = 0; i < parametersCount; i++)
                    {
                        instruction.Dependencies.AddOrMerge(i + 1, arguments[i]);
                    }
                }

                for (int i = 0; i < instruction.Dependencies.Count; i++)
                {
                    var dep = instruction.Dependencies[i];

                    // Check if the pushed value already has a result variable assigned to it.
                    var resultVar = GetExistingVariableForStackSlot(resultVariables, dep);

                    string variableName = GetOperandVariableName(instruction, i);
                    if (resultVar == null)
                    {
                        // Introduce variable for dependency.
                        resultVar = result.GetOrCreateVariable(variableName);
                        resultVar.VariableType = dep.Type;

                        // Assign this variable to all instructions that determine the value of this dependency.
                        foreach (var source in dep.DataSources)
                        {
                            resultVariables[source.Offset] = resultVar;
                        }
                    }
                    else
                    {
                        _sharedVariables[variableName] = resultVar.Name;
                    }
                }
            }

            return(resultVariables);
        }
示例#16
0
        private static IILArgumentsProvider BuildPushrExpression(ILInstruction instruction, ILCompilationUnit result)
        {
            // Since we treat registers as variables, we should interpret the operand as a variable and add it
            // as an argument to the expression instead of keeping it just as an operand. This makes it easier
            // to apply analysis and transformations (such as variable inlining) later, in the same way we do
            // that with normal variables.

            IILArgumentsProvider expression = new ILInstructionExpression(instruction);

            ILVariable registerVar;

            if (instruction.Operand is VMRegisters.FL)
            {
                var dataSources = instruction.ProgramState.Registers[VMRegisters.FL].DataSources
                                  .Select(i => i.Offset)
                                  .ToArray();

                var flagsVariable = result.GetOrCreateFlagsVariable(dataSources);
                registerVar = flagsVariable;
            }
            else
            {
                registerVar = result.GetOrCreateVariable(instruction.Operand.ToString());
            }

            var varExpression = new ILVariableExpression(registerVar);

            expression.Arguments.Add(varExpression);

            return(expression);
        }