public void ReferenceTypeAttributes() { // Grab all references from TypeSystem. var ts = corlib.Definition.MainModule.TypeSystem; var refTypes = new[] { ts.Object, ts.String }; var valTypes = new[] { ts.Char, ts.Boolean, ts.IntPtr, ts.UIntPtr, ts.SByte, ts.Int16, ts.Int32, ts.Int64, ts.Byte, ts.UInt16, ts.UInt32, ts.UInt64, ts.Single, ts.Double }; foreach (var item in refTypes) { Assert.IsTrue(corlib.Resolve(item).IsReferenceType()); } foreach (var item in valTypes) { Assert.IsFalse(corlib.Resolve(item).IsReferenceType()); } }
public void ResolveStringEmpty() { var ts = corlib.Definition.MainModule.TypeSystem; var emptyFieldRef = ts.String.Resolve().Fields.Single(f => f.Name == "Empty"); var emptyField = corlib.Resolve(emptyFieldRef); Assert.IsNotNull(emptyField); Assert.AreEqual(emptyField.Name.ToString(), emptyFieldRef.Name); Assert.IsTrue(emptyField.IsStatic); Assert.AreEqual(emptyField.FieldType.FullName.ToString(), StringBoxName); }
private static Task <AssemblyContentDescription> CreateContentDescriptionAsync(ClrAssembly assembly) { var typeSystem = assembly.Resolver.TypeEnvironment; var pipeline = new Optimization[] { new ConstantPropagation(), MemoryAccessElimination.Instance, DeadValueElimination.Instance, new JumpThreading(true), SwitchSimplification.Instance, DuplicateReturns.Instance, TailRecursionElimination.Instance, BlockFusion.Instance }; var optimizer = new OnDemandOptimizer( pipeline, method => GetInitialMethodBody(method, typeSystem)); return(AssemblyContentDescription.CreateTransitiveAsync( assembly.FullName, assembly.Attributes, assembly.Resolve(assembly.Definition.EntryPoint), optimizer)); }
public void ResolveTypeSystem() { // Grab all references from TypeSystem. var ts = Corlib.Definition.MainModule.TypeSystem; var refs = new[] { ts.Object, ts.String, ts.Void, ts.Char, ts.Boolean, ts.IntPtr, ts.UIntPtr, ts.SByte, ts.Int16, ts.Int32, ts.Int64, ts.Byte, ts.UInt16, ts.UInt32, ts.UInt64, ts.Single, ts.Double }; // Resolve all references in TypeSystem. foreach (var item in refs) { Assert.IsNotNull(Corlib.Resolve(item)); } }
public void RoundtripCheckedConversions() { var typeSys = corlib.Definition.MainModule.TypeSystem; Func <TypeReference, bool> isSigned = type => corlib.Resolve(type).GetIntegerSpecOrNull().IsSigned; Func <TypeReference, int> getSize = type => corlib.Resolve(type).GetIntegerSpecOrNull().Size; var checkedOps = new Dictionary <TypeReference, Tuple <Mono.Cecil.Cil.OpCode, Mono.Cecil.Cil.OpCode> >() { { typeSys.Byte, Tuple.Create(OpCodes.Conv_Ovf_U1, OpCodes.Conv_Ovf_U1_Un) }, { typeSys.SByte, Tuple.Create(OpCodes.Conv_Ovf_I1, OpCodes.Conv_Ovf_I1_Un) }, { typeSys.Int16, Tuple.Create(OpCodes.Conv_Ovf_I2, OpCodes.Conv_Ovf_I2_Un) }, { typeSys.UInt16, Tuple.Create(OpCodes.Conv_Ovf_U2, OpCodes.Conv_Ovf_U2_Un) }, { typeSys.Int32, Tuple.Create(OpCodes.Conv_Ovf_I4, OpCodes.Conv_Ovf_I4_Un) }, { typeSys.UInt32, Tuple.Create(OpCodes.Conv_Ovf_U4, OpCodes.Conv_Ovf_U4_Un) }, { typeSys.Int64, Tuple.Create(OpCodes.Conv_Ovf_I8, OpCodes.Conv_Ovf_I8_Un) }, { typeSys.UInt64, Tuple.Create(OpCodes.Conv_Ovf_U8, OpCodes.Conv_Ovf_U8_Un) } }; foreach (var sourceType in checkedOps.Keys) { foreach (var targetType in checkedOps.Keys) { var signedAndUnsignedOp = checkedOps[targetType]; var op = isSigned(sourceType) ? signedAndUnsignedOp.Item1 : signedAndUnsignedOp.Item2; var requiresConv = !InstructionSimplification.CanAlwaysConvert( corlib.Resolve(sourceType), corlib.Resolve(targetType)); RoundtripStaticMethodBody( targetType, new[] { sourceType }, EmptyArray <TypeReference> .Value, ilProc => { ilProc.Emit(OpCodes.Ldarg_0); ilProc.Emit(op); ilProc.Emit(OpCodes.Ret); }, ilProc => { ilProc.Emit(OpCodes.Ldarg_0); if (requiresConv) { ilProc.Emit(op); } else if (getSize(targetType) > 32 && getSize(sourceType) <= 32) { if (isSigned(sourceType)) { ilProc.Emit(OpCodes.Conv_I8); } else { ilProc.Emit(OpCodes.Conv_U8); } } ilProc.Emit(OpCodes.Ret); }); } } }
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, new JumpThreading(true), DeadBlockElimination.Instance, new SwitchLowering(corlib.Resolver.TypeEnvironment), CopyPropagation.Instance, DeadValueElimination.Instance, new JumpThreading(false))); // Turn Flame IR back into CIL. var emitter = new ClrMethodBodyEmitter(methodDef, irBody, corlib.Resolver.TypeEnvironment); var newCilBody = emitter.Compile(); // 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()); }