Example #1
0
        /// <summary>
        /// Compiles Brainfuck source code down to a method and sets the
        /// assembly's entry point to that method.
        /// </summary>
        /// <param name="document">
        /// A Brainfuck source code document.
        /// </param>
        public void Compile(SourceDocument document)
        {
            // Compile the Brainfuck source code to a Flame IR method body.
            var sourceBody = CompileBody(new SourceReader(document));

            // Optimize the IR method body.
            var body = sourceBody.WithImplementation(
                sourceBody.Implementation.Transform(
                    AllocaToRegister.Instance,
                    CopyPropagation.Instance,
                    new ConstantPropagation(),
                    GlobalValueNumbering.Instance,
                    CopyPropagation.Instance,
                    InstructionSimplification.Instance,
                    DeadValueElimination.Instance,
                    MemoryAccessElimination.Instance,
                    CopyPropagation.Instance,
                    new ConstantPropagation(),
                    DeadValueElimination.Instance,
                    ReassociateOperators.Instance,
                    DeadValueElimination.Instance,
                    FuseMemoryAccesses.Instance));

            if (CompilerOptions.GetValue <bool>(Options.PrintIr))
            {
                PrintIr(sourceBody, body);
            }

            // Define a class.
            var program = new Mono.Cecil.TypeDefinition(
                "Brainfuck",
                "Program",
                Mono.Cecil.TypeAttributes.Class | Mono.Cecil.TypeAttributes.Public,
                Assembly.Definition.MainModule.ImportReference(Environment.Object));

            Assembly.Definition.MainModule.Types.Add(program);

            // Add an entry point method to that class.
            var main = new Mono.Cecil.MethodDefinition(
                "Main",
                Mono.Cecil.MethodAttributes.Static | Mono.Cecil.MethodAttributes.Public,
                Assembly.Definition.MainModule.ImportReference(Environment.Void));

            program.Methods.Add(main);

            // Compile the method body down to CIL and assign it to the method.
            var emitter = new ClrMethodBodyEmitter(main, body, Environment);

            main.Body = emitter.Compile();

            // Set the entry point.
            Assembly.Definition.MainModule.EntryPoint = main;
        }
Example #2
0
        private static async Task UpdateMethodBodyAsync(
            ClrMethodDefinition method,
            Optimizer optimizer,
            TypeEnvironment typeSystem,
            bool printIr)
        {
            var optBody = await optimizer.GetBodyAsync(method);

            if (optBody == null)
            {
                // Looks like the method either doesn't have a body or
                // we can't handle it for some reason.
                return;
            }

            // Lower the optimized method body.
            optBody = optBody.WithImplementation(
                optBody.Implementation.Transform(
                    JumpToEntryRemoval.Instance,
                    DeadBlockElimination.Instance,
                    new SwitchLowering(typeSystem),
                    CopyPropagation.Instance,
                    InstructionReordering.Instance,
                    FuseMemoryAccesses.Instance,
                    new LowerBox(typeSystem.Object.GetDefiningAssemblyOrNull()),
                    DeadValueElimination.Instance,
                    new JumpThreading(false),
                    LowerDelegates.Instance));

            if (printIr)
            {
                PrintIr(method, method.Body, optBody);
            }

            // Select CIL instructions for the optimized method body.
            var newCilBody = ClrMethodBodyEmitter.Compile(optBody, method.Definition, typeSystem);

            lock (method.Definition)
            {
                method.Definition.Body = newCilBody;
            }
        }
Example #3
0
        private static void OptimizeMethod(
            Mono.Cecil.MethodDefinition methodDefinition,
            ClrAssembly parentAssembly,
            Func <ClrMethodDefinition, MethodBody> optimizeBody)
        {
            if (methodDefinition.HasBody)
            {
                var flameMethod = (ClrMethodDefinition)parentAssembly.Resolve(methodDefinition);
                var irBody      = flameMethod.Body;

                var errors = irBody.Validate();
                if (errors.Count > 0)
                {
                    var sourceIr = FormatIr(irBody);
                    log.Log(
                        new LogEntry(
                            Severity.Warning,
                            "invalid IR",
                            Quotation.QuoteEvenInBold(
                                "the Flame IR produced by the CIL analyzer for ",
                                flameMethod.FullName.ToString(),
                                " is erroneous; skipping it."),

                            CreateRemark(
                                "errors in IR:",
                                new BulletedList(errors.Select(x => new Text(x)).ToArray())),

                            CreateRemark(
                                "generated Flame IR:",
                                new Paragraph(new WrapBox(sourceIr, 0, -sourceIr.Length)))));
                    return;
                }

                var optBody = optimizeBody(flameMethod);
                var emitter = new ClrMethodBodyEmitter(
                    methodDefinition,
                    optBody,
                    parentAssembly.Resolver.TypeEnvironment);
                var newCilBody = emitter.Compile();
                methodDefinition.Body = newCilBody;
            }
        }
Example #4
0
        /// <summary>
        /// Writes a CIL method body, analyzes it as Flame IR,
        /// emits that as CIL and checks that the outcome matches
        /// what we'd expect.
        /// </summary>
        /// <param name="returnType">
        /// The return type of the method body.
        /// </param>
        /// <param name="parameterTypes">
        /// The parameter types of the method body.
        /// </param>
        /// <param name="localTypes">
        /// The local variable types of the method body.
        /// </param>
        /// <param name="emitBody">
        /// A function that writes the method body.
        /// </param>
        /// <param name="oracle">
        /// A printed version of the expected method body.
        /// </param>
        private void RoundtripStaticMethodBody(
            TypeReference returnType,
            IReadOnlyList <TypeReference> parameterTypes,
            IReadOnlyList <TypeReference> localTypes,
            Action <Mono.Cecil.Cil.ILProcessor> emitBody,
            string oracle)
        {
            // Define a method.
            var methodDef = CreateStaticMethodDef(returnType, parameterTypes);

            // Emit the source CIL.
            var cilBody = new Mono.Cecil.Cil.MethodBody(methodDef);

            foreach (var localType in localTypes)
            {
                cilBody.Variables.Add(new Mono.Cecil.Cil.VariableDefinition(localType));
            }

            emitBody(cilBody.GetILProcessor());
            cilBody.Optimize();

            // Analyze it as Flame IR.
            var irBody = ClrMethodBodyAnalyzer.Analyze(
                cilBody,
                new Parameter(TypeHelpers.BoxIfReferenceType(corlib.Resolve(returnType))),
                default(Parameter),
                parameterTypes
                .Select((type, i) => new Parameter(TypeHelpers.BoxIfReferenceType(corlib.Resolve(type)), "param_" + i))
                .ToArray(),
                corlib);

            // Register analyses.
            irBody = new global::Flame.Compiler.MethodBody(
                irBody.ReturnParameter,
                irBody.ThisParameter,
                irBody.Parameters,
                irBody.Implementation
                .WithAnalysis(LazyBlockReachabilityAnalysis.Instance)
                .WithAnalysis(NullabilityAnalysis.Instance)
                .WithAnalysis(new EffectfulInstructionAnalysis())
                .WithAnalysis(PredecessorAnalysis.Instance)
                .WithAnalysis(RelatedValueAnalysis.Instance)
                .WithAnalysis(LivenessAnalysis.Instance)
                .WithAnalysis(InterferenceGraphAnalysis.Instance)
                .WithAnalysis(ValueUseAnalysis.Instance)
                .WithAnalysis(ConservativeInstructionOrderingAnalysis.Instance));

            // Optimize the IR a tiny bit.
            irBody = irBody.WithImplementation(
                irBody.Implementation.Transform(
                    AllocaToRegister.Instance,
                    CopyPropagation.Instance,
                    new ConstantPropagation(),
                    SwitchSimplification.Instance,
                    DeadValueElimination.Instance,
                    InstructionSimplification.Instance,
                    new JumpThreading(true),
                    DeadBlockElimination.Instance,
                    new SwitchLowering(corlib.Resolver.TypeEnvironment),
                    CopyPropagation.Instance,
                    InstructionSimplification.Instance,
                    DeadValueElimination.Instance,
                    InstructionReordering.Instance,
                    new JumpThreading(false)));

            // Turn Flame IR back into CIL.
            var newCilBody = ClrMethodBodyEmitter.Compile(irBody, methodDef, corlib.Resolver.TypeEnvironment);

            // Check that the resulting CIL matches the expected CIL.
            var actual = FormatMethodBody(newCilBody);

            actual = actual.Trim().Replace("\r", "");
            oracle = oracle.Trim().Replace("\r", "");
            if (actual != oracle)
            {
                var encoder     = new EncoderState();
                var encodedImpl = encoder.Encode(irBody.Implementation);

                var actualIr = Les2LanguageService.Value.Print(
                    encodedImpl,
                    options: new LNodePrinterOptions
                {
                    IndentString = new string(' ', 4)
                });

                log.Log(
                    new LogEntry(
                        Severity.Message,
                        "emitted CIL-oracle mismatch",
                        "round-tripped CIL does not match the oracle. CIL emit output:",
                        new Paragraph(new WrapBox(actual, 0, -actual.Length)),
                        DecorationSpan.MakeBold("remark: Flame IR:"),
                        new Paragraph(new WrapBox(actualIr, 0, -actualIr.Length))));
            }
            Assert.AreEqual(oracle, actual);
        }