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