Exemple #1
0
        public static List <FieldReference> ImportReferencesIntoAndMakeFieldPathPublic(this IEnumerable <FieldReference> fieldPath, AssemblyDefinition asm)
        {
            var importedFields = new List <FieldReference>();

            foreach (var field in fieldPath)
            {
                var importedField = field.ImportReferenceInto(asm);
                if (field.DeclaringType.HasGenericParameters)
                {
                    importedField = TypeRegGen.MakeGenericFieldSpecialization(importedField, field.DeclaringType.GenericParameters.ToArray());
                }

                importedFields.Add(importedField);
                var fieldDef = field.Resolve();
                fieldDef.IsPublic = true;

                // According to the C# Standard https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/compiler-messages/cs0052
                // Fields must have the same access level as their declared field access level so when we mark something
                // as being public we need to also make sure the generic params type within this field has the same access.
                // level which in this case is public
                if (fieldDef.FieldType is GenericInstanceType g)
                {
                    foreach (var parm in g.GenericArguments)
                    {
                        var resolveType = parm.Resolve();
                        if (resolveType != null && !resolveType.IsPublic)
                        {
                            resolveType.IsPublic = true;
                            if (resolveType.IsNested)
                            {
                                resolveType.IsNestedPublic = true;
                            }
                        }
                    }
                }

                if (field.DeclaringType != null)
                {
                    var d = field.DeclaringType.Resolve();
                    d.IsPublic = true;
                    if (d.IsNested)
                    {
                        d.IsNestedPublic = true;
                    }
                }
            }

            return(importedFields);
        }
Exemple #2
0
        /// Generates the outer, non-burstable method that calls the inner Execute_Gen.
        ///
        /// JobTyping_System and JobTyping_Query (in TestExecuteGen) show the source
        /// code template this IL is based on.
        ///
        /// Tries to remove as many generics as possible, and only instance types
        /// are passed to the Execute/Execute_Gen
        ///
        /// <param name="isQuery">If true, this uses the variant that has an EntityQuery as a parameter.
        /// If false, expects a ComponentSystem as a parameter.</param>
        /// <param name="usesEntity">If true, the Entity and Index will be passed to the Execute.</param>
        public MethodDefinition GenOuterMethod(string methodName,
                                               AssemblyDefinition asm,
                                               TypeDefinition jobStruct,
                                               MethodDefinition generatedExecute,
                                               List <ParameterDefinition> executeParams,
                                               bool isQuery,
                                               bool usesEntity)
        {
            var zeroJobsAssembly = m_assemblies.First(a => a.Name.Name == "Unity.ZeroJobs");
            var jobHandleDef     = zeroJobsAssembly.MainModule.Types.First(i => i.FullName == "Unity.Jobs.JobHandle");
            var jobHandleRef     = asm.MainModule.ImportReference(jobHandleDef);

            var entityDef        = m_entityAssembly.MainModule.Types.First(i => i.FullName == "Unity.Entities.Entity");
            var entityManagerDef =
                m_entityAssembly.MainModule.Types.First(i => i.FullName == "Unity.Entities.EntityManager");
            var worldDef                = m_entityAssembly.MainModule.Types.First(i => i.FullName == "Unity.Entities.World");
            var getActiveWorldFnDef     = worldDef.Methods.First(i => i.Name == "get_Active");
            var getEntityManagerFnDef   = worldDef.Methods.First(i => i.Name == "get_EntityManager");
            var getManagerACCCTypeFnRef = asm.MainModule.ImportReference(entityManagerDef.Methods.First(i => i.Name == "GetArchetypeChunkComponentType"));

            MethodDefinition getManagerArchetypeChunkEntityTypeFnDef =
                entityManagerDef.Methods.First(i => i.Name == "GetArchetypeChunkEntityType");
            MethodReference getManagerArchetypeChunkEntityTypeFnRef =
                asm.MainModule.ImportReference(getManagerArchetypeChunkEntityTypeFnDef);

            var componentTypeDef =
                m_entityAssembly.MainModule.Types.First(i => i.FullName == "Unity.Entities.ComponentType");
            var componentTypeRef = asm.MainModule.ImportReference(componentTypeDef);

            var openReadWriteRef = asm.MainModule.ImportReference(
                componentTypeDef.Methods.First(i => i.Name == "ReadWrite" && i.GenericParameters.Count == 1));

            var openReadOnlyRef = asm.MainModule.ImportReference(
                componentTypeDef.Methods.First(i => i.Name == "ReadOnly" && i.GenericParameters.Count == 1));

            var openExcludeRef = asm.MainModule.ImportReference(
                componentTypeDef.Methods.First(i => i.Name == "Exclude" && i.GenericParameters.Count == 1));

            var componentSystemDef =
                m_entityAssembly.MainModule.Types.First(i => i.FullName == "Unity.Entities.ComponentSystem");
            var componentSystemRef = asm.MainModule.ImportReference(componentSystemDef);

            var entityQueryDef =
                m_entityAssembly.MainModule.Types.First(i => i.FullName == "Unity.Entities.EntityQuery");
            var entityQueryRef = asm.MainModule.ImportReference(entityQueryDef);

            // Unity.Entities.ComponentSystemBase.GetArchetypeChunkComponentType
            var componentSystemBaseDef =
                m_entityAssembly.MainModule.Types.First(i => i.FullName == "Unity.Entities.ComponentSystemBase");
            var componentSystemBaseRef = asm.MainModule.ImportReference(componentSystemBaseDef);
            var getSystemACCCTypeFnRef = asm.MainModule.ImportReference(componentSystemBaseDef.Methods.First(i => i.Name == "GetArchetypeChunkComponentType"));

            // Unity.Entities.ComponentSystemBase.GetArchetypeChunkEntityType
            MethodDefinition getSystemArchetypeChunkEntityTypeFnDef =
                componentSystemBaseDef.Methods.First(i => i.Name == "GetArchetypeChunkEntityType");
            MethodReference getSystemArchetypeChunkEntityTypeFnRef =
                asm.MainModule.ImportReference(getSystemArchetypeChunkEntityTypeFnDef);

            var archetypeChunkEntityTypeDef =
                m_entityAssembly.MainModule.Types.First(i => i.FullName == "Unity.Entities.ArchetypeChunkEntityType");
            var archetypeChunkEntityTypeRef = asm.MainModule.ImportReference(archetypeChunkEntityTypeDef);

            // Unity.Entities.ComponentSystemBase.GetEntityQueryInternal(ComponentType* componentTypes, int count)
            var getEntityQueryInternalFnDef = componentSystemBaseDef.Methods.First(i =>
                                                                                   i.Name == "GetEntityQueryInternal" &&
                                                                                   i.Parameters.Count == 2);

            getEntityQueryInternalFnDef.IsPublic = true;

            var archetypeChunkDef =
                m_entityAssembly.MainModule.Types.First(i => i.FullName == "Unity.Entities.ArchetypeChunk");

            // Unity.Jobs.JobHandle.Complete
            MethodDefinition completeFnDef = jobHandleDef.Methods.First(n => n.FullName == "System.Void Unity.Jobs.JobHandle::Complete()");

            // Unity.Entities.ArchetypeChunk.GetNativeArray
            var getNativeArrayFnRef = asm.MainModule.ImportReference(archetypeChunkDef.Methods.First(m => m.Name == "GetNativeArray" && m.HasGenericParameters));

            MethodDefinition getNativeEntityArrayFnDef =
                archetypeChunkDef.Methods.First(m => m.Name == "GetNativeArray" && !m.HasGenericParameters);
            var getNativeEntityArrayFnRef = asm.MainModule.ImportReference(getNativeEntityArrayFnDef);

            var nativeArrayUnsafeUtilityDef = m_zeroJobsAssembly.MainModule.Types.First(i =>
                                                                                        i.FullName == "Unity.Collections.LowLevel.Unsafe.NativeArrayUnsafeUtility");

            var unsafePtrFnRef         = asm.MainModule.ImportReference(nativeArrayUnsafeUtilityDef.Methods.First(i => i.Name == "GetUnsafePtr"));
            var unsafeReadOnlyPtrFnRef = asm.MainModule.ImportReference(nativeArrayUnsafeUtilityDef.Methods.First(i => i.Name == "GetUnsafeReadOnlyPtr"));

            MethodDefinition getUnsafeReadOnlyPtrFnDef =
                nativeArrayUnsafeUtilityDef.Methods.First(i => i.Name == "GetUnsafeReadOnlyPtr");

            // Unity.Entities.EntityQuery.CreateArchetypeChunkArray
            var createArchetypeChunkArrayFnDef =
                entityQueryDef.Methods.First(i => i.Name == "CreateArchetypeChunkArray" && i.Parameters.Count == 1);

            TypeDefinition openNativeArrayDef =
                m_zeroJobsAssembly.MainModule.Types.First(i => i.FullName == "Unity.Collections.NativeArray`1");

            MethodDefinition scheduleMethodDef = jobStruct.Methods.First(n => n.Name == InterfaceGen.PrepareJobAtScheduleTimeFn);
            MethodDefinition executeMethodDef  = jobStruct.Methods.First(n => n.Name == InterfaceGen.PrepareJobAtExecuteTimeFn);
            MethodDefinition cleanupMethodDef  = jobStruct.Methods.First(n => n.Name == InterfaceGen.CleanupJobFn);

            // Unity.Collections.NativeArray.Dispose
            MethodDefinition disposeFnDef = openNativeArrayDef.Methods.First(m => m.Name == "Dispose");

            var getLengthFnDef = openNativeArrayDef.Methods.First(m => m.Name == "get_Length");
            var getLengthFnRef =
                asm.MainModule.ImportReference(getLengthFnDef.MakeHostInstanceGeneric(archetypeChunkDef));

            var getItemFnDef = openNativeArrayDef.Methods.First(m => m.Name == "get_Item");
            var getItemFnRef = asm.MainModule.ImportReference(getItemFnDef.MakeHostInstanceGeneric(archetypeChunkDef));

            var getCountFnDef = archetypeChunkDef.Methods.First(m => m.Name == "get_Count");
            var getCountFnRef = asm.MainModule.ImportReference(getCountFnDef);

            var method = new MethodDefinition(methodName,
                                              MethodAttributes.Static | MethodAttributes.Public | MethodAttributes.HideBySig, jobHandleRef);

            // -------- Parameters ---------
            var paramJob = new ParameterDefinition("job", ParameterAttributes.None,
                                                   asm.MainModule.ImportReference(jobStruct));

            method.Parameters.Add(paramJob);

            ParameterDefinition paramSystem = null;
            ParameterDefinition paramQuery  = null;

            if (isQuery)
            {
                paramQuery = new ParameterDefinition("query", ParameterAttributes.None, entityQueryRef);
                method.Parameters.Add(paramQuery);
            }
            else
            {
                paramSystem = new ParameterDefinition("system", ParameterAttributes.None, componentSystemBaseRef);
                method.Parameters.Add(paramSystem);
            }

            var paramJobHandle = new ParameterDefinition("dependsOn",
                                                         ParameterAttributes.Optional | ParameterAttributes.HasDefault, jobHandleRef);

            method.Parameters.Add(paramJobHandle);
            paramJobHandle.Constant = new ParameterDefinition(asm.MainModule.ImportReference(typeof(Nullable)));

            // -------- Variables -----------
            VariableDefinition vEntityManager = null;

            if (isQuery)
            {
                vEntityManager = new VariableDefinition(asm.MainModule.ImportReference(entityManagerDef));
                method.Body.Variables.Add(vEntityManager);
            }

            var vDelegateTypePtr =
                new VariableDefinition(asm.MainModule.ImportReference(componentTypeDef.MakePointerType()));

            if (!isQuery)
            {
                method.Body.Variables.Add(vDelegateTypePtr);
            }

            var vEntityQuery = new VariableDefinition(entityQueryRef);

            method.Body.Variables.Add(vEntityQuery);

            VariableDefinition vEntityType = null;

            if (usesEntity)
            {
                vEntityType = new VariableDefinition(archetypeChunkEntityTypeRef);
                method.Body.Variables.Add(vEntityType);
            }

            var openArchetypeCCTRef =
                asm.MainModule.ImportReference(m_entityAssembly.MainModule.Types.First(i =>
                                                                                       i.FullName == "Unity.Entities.ArchetypeChunkComponentType`1"));

            List <VariableDefinition> vChunkComponentType = new List <VariableDefinition>();

            for (int i = 0; i < executeParams.Count; ++i)
            {
                var param = executeParams[i].ParameterType.GetElementType();
                if (param.Module != asm.MainModule)
                {
                    asm.MainModule.ImportReference(param);
                }
                var special = openArchetypeCCTRef.MakeGenericInstanceType(param);
                var vd      = new VariableDefinition(special);
                vChunkComponentType.Add(vd);
                method.Body.Variables.Add(vd);
            }

            var           archetypeNativeArray    = openNativeArrayDef.MakeGenericInstanceType(archetypeChunkDef);
            TypeReference archetypeNativeArrayRef = asm.MainModule.ImportReference(archetypeNativeArray);
            var           vChunks = new VariableDefinition(archetypeNativeArrayRef);

            method.Body.Variables.Add(vChunks);

            var vJ = new VariableDefinition(asm.MainModule.ImportReference(typeof(int)));

            method.Body.Variables.Add(vJ);
            var vChunkCount = new VariableDefinition(asm.MainModule.ImportReference(typeof(int)));

            method.Body.Variables.Add(vChunkCount);
            var vChunk = new VariableDefinition(asm.MainModule.ImportReference(archetypeChunkDef));

            method.Body.Variables.Add(vChunk);

            VariableDefinition vEntityArray = null;

            if (usesEntity)
            {
                vEntityArray = new VariableDefinition(asm.MainModule.ImportReference(entityDef.MakePointerType()));
                method.Body.Variables.Add(vEntityArray);
            }

            List <VariableDefinition> vArray = new List <VariableDefinition>();

            for (int i = 0; i < executeParams.Count; ++i)
            {
                var vd = new VariableDefinition(asm.MainModule.ImportReference(typeof(void *)));
                vArray.Add(vd);
                method.Body.Variables.Add(vd);
            }

            var vJobHandle = new VariableDefinition(jobHandleRef);

            method.Body.Variables.Add(vJobHandle);

            method.Body.InitLocals = true;
            var bc = method.Body.Instructions;

            // -------- Body -----------
            // dependsOn.Complete(); // Until proper jobs are supported, use this to make sure we've flushed out previous work.
            bc.Add(Instruction.Create(OpCodes.Ldarga, paramJobHandle));
            bc.Add(Instruction.Create(OpCodes.Call, asm.MainModule.ImportReference(completeFnDef)));

            // ScheduleJob_Gen()
            bc.Add(Instruction.Create(OpCodes.Ldarga, paramJob));
            bc.Add(Instruction.Create(OpCodes.Conv_U));
            bc.Add(Instruction.Create(OpCodes.Call, asm.MainModule.ImportReference(scheduleMethodDef)));
            bc.Add(Instruction.Create(OpCodes.Pop));    // The return code does nothing here. In other jobs code,
            // the expected return code is used to validate the generated
            // code was called and is working correctly.

            // ExecuteJob_Gen(0)
            bc.Add(Instruction.Create(OpCodes.Ldarga, paramJob));
            bc.Add(Instruction.Create(OpCodes.Conv_U));
            bc.Add(Instruction.Create(OpCodes.Ldc_I4_0));
            bc.Add(Instruction.Create(OpCodes.Call, asm.MainModule.ImportReference(executeMethodDef)));

            if (isQuery)
            {
                // entityQuery = _query
                bc.Add(Instruction.Create(OpCodes.Ldarg, paramQuery));
                bc.Add(Instruction.Create(OpCodes.Stloc, vEntityQuery));

                // EntityManager entityManager = World.Active.EntityManager;
                bc.Add(Instruction.Create(OpCodes.Call, asm.MainModule.ImportReference(getActiveWorldFnDef)));
                bc.Add(Instruction.Create(OpCodes.Callvirt, asm.MainModule.ImportReference(getEntityManagerFnDef)));
                bc.Add(Instruction.Create(OpCodes.Stloc, vEntityManager));
            }
            else
            {
                // ---------------------------------------------------
                // var delegateTypes = stackalloc ComponentType[1]
                // {
                //    ComponentType.ReadWrite<Position>(),
                // };

                List <Component> comps = FindComponents(jobStruct, executeParams);

                // Allocate nParams ComponentTypes
                bc.Add(Instruction.Create(OpCodes.Ldc_I4, comps.Count));
                bc.Add(Instruction.Create(OpCodes.Conv_U));
                bc.Add(Instruction.Create(OpCodes.Sizeof, componentTypeRef));
                bc.Add(Instruction.Create(OpCodes.Mul_Ovf_Un));
                bc.Add(Instruction.Create(OpCodes.Localloc));

                // Initialize them.
                for (int i = 0; i < comps.Count; ++i)
                {
                    MethodReference methodRef = null;
                    if (comps[i].access == Access.ReadOnly)
                    {
                        methodRef = openReadOnlyRef;
                    }
                    else if (comps[i].access == Access.ReadWrite)
                    {
                        methodRef = openReadWriteRef;
                    }
                    else if (comps[i].access == Access.Exclude)
                    {
                        methodRef = openExcludeRef;
                    }

                    var genericArg = comps[i].type;
                    if (genericArg.Module != asm.MainModule)
                    {
                        asm.MainModule.ImportReference(comps[i].type);
                    }

                    MethodReference special = TypeRegGen.MakeGenericMethodSpecialization(methodRef, genericArg);

                    bc.Add(Instruction.Create(OpCodes.Dup)); // duplicate the address we will add to.
                    bc.Add(Instruction.Create(OpCodes.Ldc_I4, i));
                    bc.Add(Instruction.Create(OpCodes.Sizeof, componentTypeRef));
                    bc.Add(Instruction.Create(OpCodes.Mul));
                    bc.Add(Instruction.Create(OpCodes.Add));
                    bc.Add(Instruction.Create(OpCodes.Call, special));
                    bc.Add(Instruction.Create(OpCodes.Stobj, componentTypeRef));
                }

                bc.Add(Instruction.Create(OpCodes.Stloc, vDelegateTypePtr));

                // ---------------------------------------------------
                // EntityQuery query = system.GetEntityQueryInternal(delegateTypes, 1);
                bc.Add(Instruction.Create(OpCodes.Ldarg, paramSystem));
                bc.Add(Instruction.Create(OpCodes.Ldloc, vDelegateTypePtr));
                bc.Add(Instruction.Create(OpCodes.Ldc_I4, comps.Count));
                bc.Add(Instruction.Create(OpCodes.Callvirt,
                                          asm.MainModule.ImportReference(getEntityQueryInternalFnDef)));
                bc.Add(Instruction.Create(OpCodes.Stloc, vEntityQuery));
            }

            // ArchetypeChunkEntityType archetypeChunkEntityType = system.GetArchetypeChunkEntityType();
            if (usesEntity)
            {
                if (isQuery)
                {
                    bc.Add(Instruction.Create(OpCodes.Ldloc, vEntityManager));
                    bc.Add(Instruction.Create(OpCodes.Call, getManagerArchetypeChunkEntityTypeFnRef));
                    bc.Add(Instruction.Create(OpCodes.Stloc, vEntityType));
                }
                else
                {
                    bc.Add(Instruction.Create(OpCodes.Ldarg, paramSystem));
                    bc.Add(Instruction.Create(OpCodes.Callvirt, getSystemArchetypeChunkEntityTypeFnRef));
                    bc.Add(Instruction.Create(OpCodes.Stloc, vEntityType));
                }
            }

            // Query:    var chunkComponentType0 = entityManager.GetArchetypeChunkComponentType<Position>(false);
            // System:   var chunkComponentType0 = system.GetArchetypeChunkComponentType<Position>(false);
            for (int i = 0; i < executeParams.Count; ++i)
            {
                if (isQuery)
                {
                    bc.Add(Instruction.Create(OpCodes.Ldloc, vEntityManager));
                }
                else
                {
                    bc.Add(Instruction.Create(OpCodes.Ldarg, paramSystem));
                }

                bc.Add(Instruction.Create(ParamIsReadOnly(executeParams[i]) ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0));

                var genericDefinitionType = isQuery ? getManagerACCCTypeFnRef : getSystemACCCTypeFnRef;
                var parameterType         = executeParams[i].ParameterType.GetElementType();
                if (parameterType.Module != asm.MainModule)
                {
                    parameterType = asm.MainModule.ImportReference(parameterType);
                }

                var specialGetACCCTypeMethodRef = TypeRegGen.MakeGenericMethodSpecialization(genericDefinitionType, parameterType);
                bc.Add(Instruction.Create(OpCodes.Callvirt, specialGetACCCTypeMethodRef));
                bc.Add(Instruction.Create(OpCodes.Stloc, vChunkComponentType[i]));
            }

            // var chunks = query.CreateArchetypeChunkArray(Allocator.TempJob);
            bc.Add(Instruction.Create(OpCodes.Ldloc, vEntityQuery));
            bc.Add(Instruction.Create(OpCodes.Ldc_I4_3)); // Allocator.TempJob = 3
            bc.Add(Instruction.Create(OpCodes.Callvirt,
                                      asm.MainModule.ImportReference(createArchetypeChunkArrayFnDef)));
            bc.Add(Instruction.Create(OpCodes.Stloc, vChunks));

            // for(int j=0, chunkCount = chunks.Length; j<chunkCount; ++j)
            bc.Add(Instruction.Create(OpCodes.Ldc_I4_0));
            bc.Add(Instruction.Create(OpCodes.Stloc, vJ));

            bc.Add(Instruction.Create(OpCodes.Ldloca, vChunks));
            bc.Add(Instruction.Create(OpCodes.Call, getLengthFnRef));
            bc.Add(Instruction.Create(OpCodes.Stloc, vChunkCount));

            var jPrefix = bc.Count;

            bc.Add(Instruction.Create(OpCodes.Nop)); // will become branch
            var jLoopStart = Instruction.Create(OpCodes.Nop);

            bc.Add(jLoopStart);

            {
                // var chunk = chunks[j];
                bc.Add(Instruction.Create(OpCodes.Ldloca, vChunks));
                bc.Add(Instruction.Create(OpCodes.Ldloc, vJ));
                bc.Add(Instruction.Create(OpCodes.Call, getItemFnRef));
                bc.Add(Instruction.Create(OpCodes.Stloc, vChunk));

                if (usesEntity)
                {
                    // Entity* entityArray = (Entity*)chunk.GetNativeArray(entityType).GetUnsafeReadOnlyPtr();
                    bc.Add(Instruction.Create(OpCodes.Ldloca, vChunk));
                    bc.Add(Instruction.Create(OpCodes.Ldloc, vEntityType));
                    bc.Add(Instruction.Create(OpCodes.Call, getNativeEntityArrayFnRef));

                    var special = TypeRegGen.MakeGenericMethodSpecialization(getUnsafeReadOnlyPtrFnDef, entityDef);
                    bc.Add(Instruction.Create(OpCodes.Call, asm.MainModule.ImportReference(special)));
                    bc.Add(Instruction.Create(OpCodes.Stloc, vEntityArray));
                }

                // var array0 = chunk.GetNativeArray(chunkComponentType0).GetUnsafePtr();
                for (int i = 0; i < executeParams.Count; ++i)
                {
                    bc.Add(Instruction.Create(OpCodes.Ldloca, vChunk));
                    bc.Add(Instruction.Create(OpCodes.Ldloc, vChunkComponentType[i]));

                    var element = executeParams[i].ParameterType.GetElementType();
                    if (element.Module != asm.MainModule)
                    {
                        element = asm.MainModule.ImportReference(element);
                    }

                    var specialGetNativeArrayFnRef =
                        TypeRegGen.MakeGenericMethodSpecialization(getNativeArrayFnRef, element);

                    bc.Add(Instruction.Create(OpCodes.Call, specialGetNativeArrayFnRef));

                    MethodReference special = null;
                    if (ParamIsReadOnly(executeParams[i]))
                    {
                        special = TypeRegGen.MakeGenericMethodSpecialization(unsafeReadOnlyPtrFnRef, element);
                    }
                    else
                    {
                        special = TypeRegGen.MakeGenericMethodSpecialization(unsafePtrFnRef, element);
                    }

                    bc.Add(Instruction.Create(OpCodes.Call, special));

                    bc.Add(Instruction.Create(OpCodes.Stloc, vArray[i]));
                }

                // Execute_Gen(&job, chunk.Count, (Position*) array0);
                bc.Add(Instruction.Create(OpCodes.Ldarga, paramJob));
                bc.Add(Instruction.Create(OpCodes.Conv_U));
                bc.Add(Instruction.Create(OpCodes.Ldloca, vChunk));
                bc.Add(Instruction.Create(OpCodes.Call, getCountFnRef));
                if (usesEntity)
                {
                    bc.Add(Instruction.Create(OpCodes.Ldloc, vEntityArray));
                }

                for (int i = 0; i < executeParams.Count; i++)
                {
                    bc.Add(Instruction.Create(OpCodes.Ldloc, vArray[i]));
                }

                bc.Add(Instruction.Create(OpCodes.Call, generatedExecute));

                // Tail of j loop
                var jLoopTail = Instruction.Create(OpCodes.Ldloc, vJ);
                bc.Add(jLoopTail);
                bc.Add(Instruction.Create(OpCodes.Ldc_I4_1));
                bc.Add(Instruction.Create(OpCodes.Add));
                bc.Add(Instruction.Create(OpCodes.Stloc, vJ));
            }
            // Compare
            var jCompare = Instruction.Create(OpCodes.Ldloc, vJ);

            bc.Add(jCompare);
            bc.Add(Instruction.Create(OpCodes.Ldloc, vChunkCount));
            bc.Add(Instruction.Create(OpCodes.Clt));
            bc.Add(Instruction.Create(OpCodes.Brtrue, jLoopStart));

            bc[jPrefix] = Instruction.Create(OpCodes.Br, jCompare);

            // Dispose
            bc.Add(Instruction.Create(OpCodes.Ldloca, vChunks));
            bc.Add(Instruction.Create(OpCodes.Call,
                                      asm.MainModule.ImportReference(disposeFnDef.MakeHostInstanceGeneric(archetypeChunkDef))));

            // CleanupJob()
            bc.Add(Instruction.Create(OpCodes.Ldarga, paramJob));
            bc.Add(Instruction.Create(OpCodes.Conv_U));
            // This calls the CleanupMethod, but disables the "wrapper cleanup" which makes no sense in this version
            // of IJobForEach. Correct fix coming in a future PR, when IJobForEach is moved back to the
            // normal Job code with the other jobs.
            bc.Add(Instruction.Create(OpCodes.Ldc_I4_0));
            bc.Add(Instruction.Create(OpCodes.Conv_U));
            bc.Add(Instruction.Create(OpCodes.Call, asm.MainModule.ImportReference(cleanupMethodDef)));

            // Return a jobHandle
            bc.Add(Instruction.Create(OpCodes.Ldloca, vJobHandle));
            bc.Add(Instruction.Create(OpCodes.Initobj, jobHandleRef));
            bc.Add(Instruction.Create(OpCodes.Ldloc, vJobHandle));
            bc.Add(Instruction.Create(OpCodes.Ret));

            method.Body.Optimize();
            jobStruct.Methods.Add(method);
            return(method);
        }