private MethodDefinition MakeExecuteMethod_Entities(LambdaParameterValueInformations providerInformations) { var executeMethod = CecilHelpers.AddMethodImplementingInterfaceMethod(TypeDefinition.Module, TypeDefinition, typeof(IJobChunk).GetMethod(nameof(IJobChunk.Execute))); providerInformations.EmitInvocationToPrepareToRunOnEntitiesInIntoJobChunkExecute(executeMethod); var ilProcessor = executeMethod.Body.GetILProcessor(); var iterateOnEntitiesMethod = CreateIterateEntitiesMethod(providerInformations); ilProcessor.Emit(OpCodes.Ldarg_0); ilProcessor.Emit(OpCodes.Ldarga, 1); ilProcessor.Emit(OpCodes.Ldarg_0); ilProcessor.Emit(OpCodes.Ldfld, providerInformations._runtimesField); ilProcessor.Emit(OpCodes.Call, iterateOnEntitiesMethod); EmitCallToDeallocateOnCompletion(ilProcessor); ilProcessor.Emit(OpCodes.Ret); return(executeMethod); }
LambdaParameterValueInformations MakeLambdaParameterValueProviderInformations() { switch (LambdaJobDescriptionConstruction.Kind) { case LambdaJobDescriptionKind.Entities: return(LambdaParameterValueInformations.For(this)); case LambdaJobDescriptionKind.Job: return(null); case LambdaJobDescriptionKind.Chunk: var allUsedParametersOfEntitiesForEachInvocations = ClonedMethods.SelectMany( m => m.Body.Instructions.Where(IsChunkEntitiesForEachInvocation).Select(i => (m, LambdaJobDescriptionConstruction.AnalyzeForEachInvocationInstruction(m, i) .MethodLambdaWasEmittedAs))) .SelectMany(m_and_dem => m_and_dem.MethodLambdaWasEmittedAs.Parameters.Select(p => (m_and_dem.m, p))) .ToArray(); return(LambdaParameterValueInformations.For(this)); default: throw new ArgumentOutOfRangeException(); } }
private MethodDefinition CreateIterateEntitiesMethod(LambdaParameterValueInformations lambdaParameterValueInformations) { var iterateEntitiesMethod = new MethodDefinition("IterateEntities", MethodAttributes.Public, TypeSystem.Void) { Parameters = { new ParameterDefinition("chunk", ParameterAttributes.None, new ByReferenceType(ImportReference(typeof(ArchetypeChunk)))), new ParameterDefinition("runtimes", ParameterAttributes.None, new ByReferenceType(lambdaParameterValueInformations.RuntimesType)), } }; TypeDefinition.Methods.Add(iterateEntitiesMethod); var ilProcessor = iterateEntitiesMethod.Body.GetILProcessor(); var loopTerminator = new VariableDefinition(TypeSystem.Int32); iterateEntitiesMethod.Body.Variables.Add(loopTerminator); ilProcessor.Emit(OpCodes.Ldarg_1); ilProcessor.Emit(OpCodes.Call, ImportReference(typeof(ArchetypeChunk).GetMethod("get_" + nameof(ArchetypeChunk.Count)))); ilProcessor.Emit(OpCodes.Stloc, loopTerminator); var loopCounter = new VariableDefinition(TypeSystem.Int32); iterateEntitiesMethod.Body.Variables.Add(loopCounter); ilProcessor.Emit(OpCodes.Ldc_I4_0); ilProcessor.Emit(OpCodes.Stloc, loopCounter); var beginLoopInstruction = Instruction.Create(OpCodes.Ldloc, loopCounter); ilProcessor.Append(beginLoopInstruction); ilProcessor.Emit(OpCodes.Ldloc, loopTerminator); ilProcessor.Emit(OpCodes.Ceq); var exitDestination = Instruction.Create(OpCodes.Nop); ilProcessor.Emit(OpCodes.Brtrue, exitDestination); ilProcessor.Emit(OpCodes.Ldarg_0); foreach (var parameterDefinition in ClonedLambdaBody.Parameters) { lambdaParameterValueInformations.EmitILToLoadValueForParameterOnStack(parameterDefinition, ilProcessor, loopCounter); } ilProcessor.Emit(OpCodes.Call, ClonedLambdaBody); ilProcessor.Emit(OpCodes.Ldloc, loopCounter); ilProcessor.Emit(OpCodes.Ldc_I4_1); ilProcessor.Emit(OpCodes.Add); ilProcessor.Emit(OpCodes.Stloc, loopCounter); ilProcessor.Emit(OpCodes.Br, beginLoopInstruction); ilProcessor.Append(exitDestination); ilProcessor.Emit(OpCodes.Ret); return(iterateEntitiesMethod); }
private MethodDefinition MakeExecuteMethod(LambdaParameterValueInformations lambdaParameterValueInformations) { switch (LambdaJobDescriptionConstruction.Kind) { case LambdaJobDescriptionKind.Entities: return(MakeExecuteMethod_Entities(lambdaParameterValueInformations)); case LambdaJobDescriptionKind.Job: return(MakeExecuteMethod_Job()); case LambdaJobDescriptionKind.Chunk: return(MakeExecuteMethod_Chunk(lambdaParameterValueInformations)); default: throw new ArgumentOutOfRangeException(); } }
private MethodDefinition MakeExecuteMethod_Chunk(LambdaParameterValueInformations lambdaParameterValueInformations) { var executeMethod = CecilHelpers.AddMethodImplementingInterfaceMethod(TypeDefinition.Module, TypeDefinition, typeof(IJobChunk).GetMethod(nameof(IJobChunk.Execute))); lambdaParameterValueInformations.EmitInvocationToPrepareToRunOnEntitiesInIntoJobChunkExecute(executeMethod); var executeIL = executeMethod.Body.GetILProcessor(); executeIL.Emit(OpCodes.Ldarg_0); executeIL.Emit(OpCodes.Ldarg_1); executeIL.Emit(OpCodes.Ldarg_2); executeIL.Emit(OpCodes.Ldarg_3); executeIL.Emit(OpCodes.Call, ClonedLambdaBody); EmitCallToDeallocateOnCompletion(executeIL); executeIL.Emit(OpCodes.Ret); return(executeMethod); }
private MethodDefinition MakeScheduleTimeInitializeMethod(LambdaParameterValueInformations lambdaParameterValueInformations) { var scheduleTimeInitializeMethod = new MethodDefinition("ScheduleTimeInitialize", MethodAttributes.Public, TypeDefinition.Module.TypeSystem.Void) { HasThis = true, Parameters = { new ParameterDefinition("componentSystem", ParameterAttributes.None, LambdaJobDescriptionConstruction.ContainingMethod.DeclaringType), }, }; if (ReadFromDisplayClassMethod != null) { scheduleTimeInitializeMethod.Parameters.Add(ReadFromDisplayClassMethod.Parameters.Last()); } lambdaParameterValueInformations?.EmitInvocationToScheduleTimeInitializeIntoJobChunkScheduleTimeInitialize(scheduleTimeInitializeMethod); var scheduleIL = scheduleTimeInitializeMethod.Body.GetILProcessor(); if (ReadFromDisplayClassMethod != null) { scheduleIL.Emit(OpCodes.Ldarg_0); scheduleIL.Emit(OpCodes.Ldarg_2); scheduleIL.Emit(OpCodes.Call, ReadFromDisplayClassMethod); } if (SystemInstanceField != null) { scheduleIL.Emit(OpCodes.Ldarg_0); scheduleIL.Emit(OpCodes.Ldarg_1); scheduleIL.Emit(OpCodes.Stfld, SystemInstanceField); } scheduleIL.Emit(OpCodes.Ret); return(scheduleTimeInitializeMethod); }
private void ApplyPostProcessingOnJobCode(MethodDefinition[] methodUsedByLambdaJobs, LambdaParameterValueInformations lambdaParameterValueInformations) { var forEachInvocations = new List <(MethodDefinition, Instruction)>(); var methodDefinition = methodUsedByLambdaJobs.First(); forEachInvocations.AddRange(methodDefinition.Body.Instructions.Where(IsChunkEntitiesForEachInvocation).Select(i => (methodDefinition, i))); foreach (var methodUsedByLambdaJob in methodUsedByLambdaJobs) { var methodBody = methodUsedByLambdaJob.Body; var displayClassVariable = methodBody.Variables.SingleOrDefault(v => v.VariableType.Name.Contains("DisplayClass")); if (displayClassVariable != null) { TypeDefinition displayClass = displayClassVariable.VariableType.Resolve(); bool allDelegatesAreGuaranteedNotToOutliveMethod = displayClass.IsValueType || CecilHelpers.AllDelegatesAreGuaranteedNotToOutliveMethodFor(methodUsedByLambdaJob); if (!displayClass.IsValueType && allDelegatesAreGuaranteedNotToOutliveMethod) { CecilHelpers.PatchMethodThatUsedDisplayClassToTreatItAsAStruct(methodBody, displayClassVariable, displayClass); CecilHelpers.PatchDisplayClassToBeAStruct(displayClass); } } } int counter = 1; foreach (var(methodUsedByLambdaJob, instruction) in forEachInvocations) { var methodBody = methodUsedByLambdaJob.Body; var(ldFtn, newObj) = FindClosureCreatingInstructions(methodBody, instruction); var newType = new TypeDefinition("", "InlineEntitiesForEachInvocation" + counter++, TypeAttributes.NestedPublic | TypeAttributes.SequentialLayout, methodUsedByLambdaJob.Module.ImportReference(typeof(ValueType))) { DeclaringType = methodUsedByLambdaJob.DeclaringType }; methodUsedByLambdaJob.DeclaringType.NestedTypes.Add(newType); CloneLambdaMethodAndItsLocalMethods(); var iterateEntitiesMethod = CreateIterateEntitiesMethod(lambdaParameterValueInformations); var variable = new VariableDefinition(newType); methodBody.Variables.Add(variable); InstructionExtensions.MakeNOP(ldFtn.Previous); InstructionExtensions.MakeNOP(ldFtn); newObj.OpCode = OpCodes.Ldnull; newObj.Operand = null; var displayClassVariable = methodBody.Variables.SingleOrDefault(v => v.VariableType.Name.Contains("DisplayClass")); if (displayClassVariable == null) { continue; } var ilProcessor = methodBody.GetILProcessor(); ilProcessor.InsertAfter(instruction, new List <Instruction> { //no need to drop the delegate from the stack, because we just removed the function that placed it on the stack in the first place. //do not drop the description from the stack, as the original method returns it, and we want to maintain stack behaviour. //call our new method Instruction.Create(OpCodes.Ldloca, variable), Instruction.Create(OpCodes.Initobj, newType), Instruction.Create(OpCodes.Ldloca, variable), Instruction.Create(OpCodes.Ldloca, displayClassVariable), Instruction.Create(OpCodes.Call, ReadFromDisplayClassMethod), Instruction.Create(OpCodes.Ldloca, variable), Instruction.Create(OpCodes.Ldarga, methodBody.Method.Parameters.First(p => p.ParameterType.Name == nameof(ArchetypeChunk))), Instruction.Create(OpCodes.Ldarg_0), Instruction.Create(OpCodes.Ldfld, lambdaParameterValueInformations._runtimesField), Instruction.Create(OpCodes.Call, (MethodReference)iterateEntitiesMethod), }); #if ENABLE_DOTS_COMPILER_CHUNKS var chunkEntitiesInvocation = LambdaJobDescriptionConstruction.FindInstructionThatPushedArg(methodBody.Method, 0, instruction); if (chunkEntitiesInvocation.Operand is MethodReference mr && mr.Name == "get_" + nameof(ArchetypeChunk.Entities) && mr.DeclaringType.Name == nameof(ArchetypeChunk)) { CecilHelpers.EraseMethodInvocationFromInstructions(ilProcessor, chunkEntitiesInvocation); } #endif CecilHelpers.EraseMethodInvocationFromInstructions(ilProcessor, instruction); } }