private MethodBody AnalyzeBody() { if (Definition.HasBody) { return(ClrMethodBodyAnalyzer.Analyze( Definition.Body, this)); } else { return(null); } }
/// <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); }
/// <summary> /// Writes a CIL method body, analyzes it as Flame IR /// and checks that the result is 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="emitBody"> /// A function that writes the method body. /// </param> /// <param name="oracle"> /// The expected Flame IR flow graph, as LESv2. /// </param> private void AnalyzeStaticMethodBody( TypeReference returnType, IReadOnlyList <TypeReference> parameterTypes, IReadOnlyList <TypeReference> localTypes, Action <Mono.Cecil.Cil.ILProcessor> emitBody, string oracle) { var methodDef = new MethodDefinition( "f", MethodAttributes.Public | MethodAttributes.Static, returnType); foreach (var type in parameterTypes) { methodDef.Parameters.Add(new ParameterDefinition(type)); int index = methodDef.Parameters.Count - 1; methodDef.Parameters[index].Name = "param_" + index; } 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()); 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); var encoder = new EncoderState(); var encodedImpl = encoder.Encode(irBody.Implementation); var actual = Les2LanguageService.Value.Print( encodedImpl, options: new LNodePrinterOptions { IndentString = new string(' ', 4) }); if (actual.Trim() != oracle.Trim()) { log.Log( new LogEntry( Severity.Message, "CIL analysis-oracle mismatch", "analyzed CIL does not match the oracle. CIL analysis output:")); // TODO: ugly hack to work around wrapping. Console.Error.WriteLine(actual.Trim()); } Assert.AreEqual( actual.Trim(), oracle.Trim()); }