123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389 |
- using System;
- using System.Collections.Generic;
- using Mono.Cecil;
- using Mono.Cecil.Cil;
- using Mono.Cecil.Rocks;
- using Unity.CompilationPipeline.Common.ILPostProcessing;
- using Unity.Jobs.LowLevel.Unsafe;
- using MethodAttributes = Mono.Cecil.MethodAttributes;
- using MethodBody = Mono.Cecil.Cil.MethodBody;
- using TypeAttributes = Mono.Cecil.TypeAttributes;
-
- namespace Unity.Jobs.CodeGen
- {
- internal partial class JobsILPostProcessor : ILPostProcessor
- {
- private static readonly string ProducerAttributeName = typeof(JobProducerTypeAttribute).FullName;
- private static readonly string RegisterGenericJobTypeAttributeName = typeof(RegisterGenericJobTypeAttribute).FullName;
-
- public static MethodReference AttributeConstructorReferenceFor(Type attributeType, ModuleDefinition module)
- {
- return module.ImportReference(attributeType.GetConstructor(Array.Empty<Type>()));
- }
-
- private TypeReference LaunderTypeRef(TypeReference r_)
- {
- ModuleDefinition mod = AssemblyDefinition.MainModule;
-
- TypeDefinition def = r_.Resolve();
-
- TypeReference result;
-
- if (r_ is GenericInstanceType git)
- {
- var gt = new GenericInstanceType(LaunderTypeRef(def));
-
- foreach (var gp in git.GenericParameters)
- {
- gt.GenericParameters.Add(gp);
- }
-
- foreach (var ga in git.GenericArguments)
- {
- gt.GenericArguments.Add(LaunderTypeRef(ga));
- }
-
- result = gt;
-
- }
- else
- {
- result = new TypeReference(def.Namespace, def.Name, def.Module, def.Scope, def.IsValueType);
-
- if (def.DeclaringType != null)
- {
- result.DeclaringType = LaunderTypeRef(def.DeclaringType);
- }
- }
-
- return mod.ImportReference(result);
- }
-
- // http://www.isthe.com/chongo/src/fnv/hash_64a.c
- static ulong StableHash_FNV1A64(string text)
- {
- ulong result = 14695981039346656037;
- foreach (var c in text)
- {
- result = 1099511628211 * (result ^ (byte)(c & 255));
- result = 1099511628211 * (result ^ (byte)(c >> 8));
- }
- return result;
- }
-
- bool PostProcessImpl()
- {
- bool anythingChanged = false;
-
- var asmDef = AssemblyDefinition;
- var funcDef = new MethodDefinition("CreateJobReflectionData",
- MethodAttributes.Static | MethodAttributes.Public | MethodAttributes.HideBySig,
- asmDef.MainModule.ImportReference(typeof(void)));
-
- // This must use a stable hash code function (do not using string.GetHashCode)
- var autoClassName = $"__JobReflectionRegistrationOutput__{StableHash_FNV1A64(asmDef.FullName)}";
-
- funcDef.Body.InitLocals = false;
-
- var classDef = new TypeDefinition("", autoClassName, TypeAttributes.Class, asmDef.MainModule.ImportReference(typeof(object)));
- classDef.IsBeforeFieldInit = false;
- classDef.CustomAttributes.Add(new CustomAttribute(AttributeConstructorReferenceFor(typeof(DOTSCompilerGeneratedAttribute), asmDef.MainModule)));
- classDef.Methods.Add(funcDef);
-
- var body = funcDef.Body;
- var processor = body.GetILProcessor();
-
- // Setup instructions used for try/catch wrapping all earlyinit calls
- // for this assembly's job types
- var workStartOp = processor.Create(OpCodes.Nop);
- var workDoneOp = Instruction.Create(OpCodes.Nop);
- var handler = Instruction.Create(OpCodes.Nop);
- var landingPad = Instruction.Create(OpCodes.Nop);
-
- processor.Append(workStartOp);
-
- var genericJobs = new List<TypeReference>();
- var visited = new HashSet<string>();
-
- foreach (var attr in asmDef.CustomAttributes)
- {
- if (attr.AttributeType.FullName != RegisterGenericJobTypeAttributeName)
- continue;
-
- var typeRef = (TypeReference)attr.ConstructorArguments[0].Value;
- var openType = typeRef.Resolve();
-
- if (!typeRef.IsGenericInstance || !openType.IsValueType)
- {
- DiagnosticMessages.Add(UserError.DC3001(openType));
- continue;
- }
-
- genericJobs.Add(typeRef);
- visited.Add(typeRef.FullName);
- }
-
- CollectGenericTypeInstances(AssemblyDefinition, genericJobs, visited);
-
- foreach (var t in asmDef.MainModule.Types)
- {
- anythingChanged |= VisitJobStructs(t, processor, body);
- }
-
- foreach (var t in genericJobs)
- {
- anythingChanged |= VisitJobStructs(t, processor, body);
- }
-
- // Now that we have generated all reflection info
- // finish wrapping the ops in a try catch now
- var lastWorkOp = processor.Body.Instructions[processor.Body.Instructions.Count-1];
- processor.Append(handler);
-
- var earlyInitHelpersDef = asmDef.MainModule.ImportReference(typeof(EarlyInitHelpers)).Resolve();
- MethodDefinition jobReflectionDataCreationFailedDef = null;
- foreach (var method in earlyInitHelpersDef.Methods)
- {
- if (method.Name == nameof(EarlyInitHelpers.JobReflectionDataCreationFailed))
- {
- jobReflectionDataCreationFailedDef = method;
- break;
- }
- }
-
- var errorHandler = asmDef.MainModule.ImportReference(jobReflectionDataCreationFailedDef);
- processor.Append(Instruction.Create(OpCodes.Call, errorHandler));
- processor.Append(landingPad);
-
- var leaveSuccess = Instruction.Create(OpCodes.Leave, landingPad);
- var leaveFail = Instruction.Create(OpCodes.Leave, landingPad);
- processor.InsertAfter(lastWorkOp, leaveSuccess);
- processor.InsertBefore(landingPad, leaveFail);
-
- var exc = new ExceptionHandler(ExceptionHandlerType.Catch);
- exc.TryStart = workStartOp;
- exc.TryEnd = leaveSuccess.Next;
- exc.HandlerStart = handler;
- exc.HandlerEnd = leaveFail.Next;
- exc.CatchType = asmDef.MainModule.ImportReference(typeof(Exception));
- body.ExceptionHandlers.Add(exc);
-
- processor.Emit(OpCodes.Ret);
-
- if (anythingChanged)
- {
- var ctorFuncDef = new MethodDefinition("EarlyInit", MethodAttributes.Static | MethodAttributes.Public | MethodAttributes.HideBySig, asmDef.MainModule.ImportReference(typeof(void)));
-
- if (!Defines.Contains("UNITY_EDITOR"))
- {
- // Needs to run automatically in the player, but we need to
- // exclude this attribute when building for the editor, or
- // it will re-run the registration for every enter play mode.
- var loadTypeEnumType = asmDef.MainModule.ImportReference(typeof(UnityEngine.RuntimeInitializeLoadType));
- var attributeCtor = asmDef.MainModule.ImportReference(typeof(UnityEngine.RuntimeInitializeOnLoadMethodAttribute).GetConstructor(new[] { typeof(UnityEngine.RuntimeInitializeLoadType) }));
- var attribute = new CustomAttribute(attributeCtor);
- attribute.ConstructorArguments.Add(new CustomAttributeArgument(loadTypeEnumType, UnityEngine.RuntimeInitializeLoadType.AfterAssembliesLoaded));
- ctorFuncDef.CustomAttributes.Add(attribute);
- }
- else
- {
- // Needs to run automatically in the editor.
- var attributeCtor2 = asmDef.MainModule.ImportReference(typeof(UnityEditor.InitializeOnLoadMethodAttribute).GetConstructor(Type.EmptyTypes));
- ctorFuncDef.CustomAttributes.Add(new CustomAttribute(attributeCtor2));
- }
-
- ctorFuncDef.Body.InitLocals = false;
-
- var p = ctorFuncDef.Body.GetILProcessor();
-
- p.Emit(OpCodes.Call, funcDef);
- p.Emit(OpCodes.Ret);
-
- classDef.Methods.Add(ctorFuncDef);
-
- asmDef.MainModule.Types.Add(classDef);
- }
-
- return anythingChanged;
- }
-
- private bool VisitJobStructInterfaces(TypeReference jobTypeRef, TypeDefinition jobType, TypeDefinition currentType, ILProcessor processor, MethodBody body)
- {
- bool didAnything = false;
-
- if (currentType.HasInterfaces && jobType.IsValueType)
- {
- foreach (var iface in currentType.Interfaces)
- {
- var idef = iface.InterfaceType.CheckedResolve();
-
- foreach (var attr in idef.CustomAttributes)
- {
- if (attr.AttributeType.FullName == ProducerAttributeName)
- {
- var producerRef = (TypeReference)attr.ConstructorArguments[0].Value;
- var launderedType = LaunderTypeRef(jobTypeRef);
- didAnything |= GenerateCalls(producerRef, launderedType, body, processor);
- }
-
- if (currentType.IsInterface)
- {
- // Generic jobs need to be either reference in fully closed form, or registered explicitly with an attribute.
- if (iface.InterfaceType.GenericParameters.Count == 0)
- didAnything |= VisitJobStructInterfaces(jobTypeRef, jobType, idef, processor, body);
- }
- }
- }
- }
-
- foreach (var nestedType in currentType.NestedTypes)
- {
- didAnything |= VisitJobStructs(nestedType, processor, body);
- }
-
- return didAnything;
- }
-
- private bool VisitJobStructs(TypeReference t, ILProcessor processor, MethodBody body)
- {
- if (t.GenericParameters.Count > 0)
- {
- // Generic jobs need to be either reference in fully closed form, or registered explicitly with an attribute.
- return false;
- }
-
- var rt = t.CheckedResolve();
-
- return VisitJobStructInterfaces(t, rt, rt, processor, body);
- }
-
- private bool GenerateCalls(TypeReference producerRef, TypeReference jobStructType, MethodBody body, ILProcessor processor)
- {
- try
- {
- var carrierType = producerRef.CheckedResolve();
- MethodDefinition methodToCall = null;
- while (carrierType != null)
- {
- methodToCall = null;
- foreach (var method in carrierType.GetMethods())
- {
- if(method.IsStatic && method.IsPublic && method.Parameters.Count == 0 && method.Name == "EarlyJobInit")
- {
- methodToCall = method;
- break;
- }
- }
-
- if (methodToCall != null)
- break;
-
- carrierType = carrierType.DeclaringType;
- }
-
- // Legacy jobs lazy initialize.
- if (methodToCall == null)
- return false;
-
- var asm = AssemblyDefinition.MainModule;
- var mref = asm.ImportReference(asm.ImportReference(methodToCall).MakeGenericInstanceMethod(jobStructType));
- processor.Append(Instruction.Create(OpCodes.Call, mref));
-
- return true;
- }
- catch (Exception ex)
- {
- DiagnosticMessages.Add(InternalCompilerError.DCICE300(producerRef, jobStructType, ex));
- }
-
- return false;
- }
-
- private static void CollectGenericTypeInstances(AssemblyDefinition assembly, List<TypeReference> types, HashSet<string> visited)
- {
- // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- // WARNING: THIS CODE HAS TO BE MAINTAINED IN SYNC WITH BurstReflection.cs in Unity.Burst package
- // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
- // From: https://gist.github.com/xoofx/710aaf86e0e8c81649d1261b1ef9590e
- if (assembly == null) throw new ArgumentNullException(nameof(assembly));
- const int mdMaxCount = 1 << 24;
- foreach (var module in assembly.Modules)
- {
- for (int i = 1; i < mdMaxCount; i++)
- {
- // Token base id for TypeSpec
- const int mdTypeSpec = 0x1B000000;
- var token = module.LookupToken(mdTypeSpec | i);
- if (token is GenericInstanceType type)
- {
- if (type.IsGenericInstance && !type.ContainsGenericParameter)
- {
- CollectGenericTypeInstances(type, types, visited);
- }
- } else if (token == null) break;
- }
-
- for (int i = 1; i < mdMaxCount; i++)
- {
- // Token base id for MethodSpec
- const int mdMethodSpec = 0x2B000000;
- var token = module.LookupToken(mdMethodSpec | i);
- if (token is GenericInstanceMethod method)
- {
- foreach (var argType in method.GenericArguments)
- {
- if (argType.IsGenericInstance && !argType.ContainsGenericParameter)
- {
- CollectGenericTypeInstances(argType, types, visited);
- }
- }
- }
- else if (token == null) break;
- }
-
- for (int i = 1; i < mdMaxCount; i++)
- {
- // Token base id for Field
- const int mdField = 0x04000000;
- var token = module.LookupToken(mdField | i);
- if (token is FieldReference field)
- {
- var fieldType = field.FieldType;
- if (fieldType.IsGenericInstance && !fieldType.ContainsGenericParameter)
- {
- CollectGenericTypeInstances(fieldType, types, visited);
- }
- }
- else if (token == null) break;
- }
- }
- }
-
- private static void CollectGenericTypeInstances(TypeReference type, List<TypeReference> types, HashSet<string> visited)
- {
- if (type.IsPrimitive) return;
- if (!visited.Add(type.FullName)) return;
-
- // Add only concrete types
- if (type.IsGenericInstance && !type.ContainsGenericParameter)
- {
- types.Add(type);
- }
-
- // Collect recursively generic type arguments
- var genericInstanceType = type as GenericInstanceType;
- if (genericInstanceType != null)
- {
- foreach (var genericTypeArgument in genericInstanceType.GenericArguments)
- {
- if (!genericTypeArgument.IsPrimitive)
- {
- CollectGenericTypeInstances(genericTypeArgument, types, visited);
- }
- }
- }
- }
- }
- }
|