Aucune description
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

ILPostProcessing.cs 36KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using Mono.Cecil;
  6. using Mono.Cecil.Cil;
  7. namespace zzzUnity.Burst.CodeGen
  8. {
  9. internal delegate void LogDelegate(string message);
  10. internal delegate void ErrorDiagnosticDelegate(MethodDefinition method, Instruction instruction, string message);
  11. /// <summary>
  12. /// Main class for post processing assemblies. The post processing is currently performing:
  13. /// - Replace C# call from C# to Burst functions with attributes [BurstCompile] to a call to the compiled Burst function
  14. /// In both editor and standalone scenarios. For DOTS Runtime, this is done differently at BclApp level by patching
  15. /// DllImport.
  16. /// </summary>
  17. internal class ILPostProcessing
  18. {
  19. private AssemblyDefinition _burstAssembly;
  20. private TypeDefinition _burstCompilerTypeDefinition;
  21. private MethodReference _burstCompilerIsEnabledMethodDefinition;
  22. private MethodReference _burstCompilerCompileILPPMethod;
  23. private MethodReference _burstCompilerGetILPPMethodFunctionPointer;
  24. private MethodReference _burstDiscardAttributeConstructor;
  25. private MethodReference _burstCompilerCompileUnsafeStaticMethodReinitialiseAttributeCtor;
  26. private TypeSystem _typeSystem;
  27. private TypeReference _systemType;
  28. private TypeReference _systemDelegateType;
  29. private TypeReference _systemASyncCallbackType;
  30. private TypeReference _systemIASyncResultType;
  31. private AssemblyDefinition _assemblyDefinition;
  32. private bool _modified;
  33. private readonly List<MethodDefinition> _directCallInitializeMethods = new List<MethodDefinition>();
  34. private const string PostfixBurstDirectCall = "$BurstDirectCall";
  35. private const string PostfixBurstDelegate = "$PostfixBurstDelegate";
  36. private const string PostfixManaged = "$BurstManaged";
  37. private const string GetFunctionPointerName = "GetFunctionPointer";
  38. private const string GetFunctionPointerDiscardName = "GetFunctionPointerDiscard";
  39. private const string InvokeName = "Invoke";
  40. public ILPostProcessing(AssemblyLoader loader, bool isForEditor, ErrorDiagnosticDelegate error, LogDelegate log = null, int logLevel = 0)
  41. {
  42. Loader = loader;
  43. IsForEditor = isForEditor;
  44. }
  45. /// <summary>
  46. /// Not used, but we cannot remove the public API.
  47. /// </summary>
  48. /// <param name="typeDefinition">Declaring type of a direct call</param>
  49. public static MethodReference RecoverManagedMethodFromDirectCall(TypeDefinition typeDefinition) => null;
  50. public bool IsForEditor { get; private set; }
  51. public AssemblyLoader Loader { get; }
  52. /// <summary>
  53. /// Checks the specified method is a direct call.
  54. /// </summary>
  55. /// <param name="method">The method being called</param>
  56. public static bool IsDirectCall(MethodDefinition method)
  57. {
  58. // Method can be null so we early exit without a NullReferenceException
  59. return method != null && method.IsStatic && method.Name == InvokeName && method.DeclaringType.Name.EndsWith(ILPostProcessing.PostfixBurstDirectCall);
  60. }
  61. public unsafe bool Run(IntPtr peData, int peSize, IntPtr pdbData, int pdbSize, out AssemblyDefinition assemblyDefinition)
  62. {
  63. if (peData == IntPtr.Zero) throw new ArgumentNullException(nameof(peData));
  64. if (peSize <= 0) throw new ArgumentOutOfRangeException(nameof(peSize));
  65. if (pdbData != IntPtr.Zero && pdbSize <= 0) throw new ArgumentOutOfRangeException(nameof(pdbSize));
  66. var peStream = new UnmanagedMemoryStream((byte*)peData, peSize);
  67. Stream pdbStream = null;
  68. if (pdbData != IntPtr.Zero)
  69. {
  70. pdbStream = new UnmanagedMemoryStream((byte*)pdbData, pdbSize);
  71. }
  72. assemblyDefinition = Loader.LoadFromStream(peStream, pdbStream);
  73. return Run(assemblyDefinition);
  74. }
  75. public bool Run(AssemblyDefinition assemblyDefinition)
  76. {
  77. _assemblyDefinition = assemblyDefinition;
  78. _typeSystem = assemblyDefinition.MainModule.TypeSystem;
  79. _modified = false;
  80. var types = assemblyDefinition.MainModule.GetTypes().ToArray();
  81. foreach (var type in types)
  82. {
  83. ProcessType(type);
  84. }
  85. // If we processed any direct-calls, then generate a single [RuntimeInitializeOnLoadMethod] method
  86. // for the whole assembly, which will initialize each individual direct-call class.
  87. if (_directCallInitializeMethods.Count > 0)
  88. {
  89. GenerateInitializeOnLoadMethod();
  90. }
  91. return _modified;
  92. }
  93. private void GenerateInitializeOnLoadMethod()
  94. {
  95. // [UnityEngine.RuntimeInitializeOnLoadMethod(UnityEngine.RuntimeInitializeLoadType.AfterAssembliesLoaded)]
  96. // [UnityEditor.InitializeOnLoadMethod] // When its an editor assembly
  97. // private static void Initialize()
  98. // {
  99. // DirectCallA.Initialize();
  100. // DirectCallB.Initialize();
  101. // }
  102. const string initializeOnLoadClassName = "$BurstDirectCallInitializer";
  103. var initializeOnLoadClass = _assemblyDefinition.MainModule.Types.FirstOrDefault(x => x.Name == initializeOnLoadClassName);
  104. if (initializeOnLoadClass != null)
  105. {
  106. // If there's already a class with this name, remove it,
  107. // This would mean that we're postprocessing an already-postprocessed assembly;
  108. // I don't think that ever happens, but no sense in breaking if it does.
  109. _assemblyDefinition.MainModule.Types.Remove(initializeOnLoadClass);
  110. }
  111. initializeOnLoadClass = new TypeDefinition(
  112. "",
  113. initializeOnLoadClassName,
  114. TypeAttributes.NotPublic |
  115. TypeAttributes.AutoLayout |
  116. TypeAttributes.AnsiClass |
  117. TypeAttributes.Abstract |
  118. TypeAttributes.Sealed |
  119. TypeAttributes.BeforeFieldInit)
  120. {
  121. BaseType = _typeSystem.Object
  122. };
  123. _assemblyDefinition.MainModule.Types.Add(initializeOnLoadClass);
  124. var initializeOnLoadMethod = new MethodDefinition("Initialize", MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.Static, _typeSystem.Void)
  125. {
  126. ImplAttributes = MethodImplAttributes.IL | MethodImplAttributes.Managed,
  127. DeclaringType = initializeOnLoadClass
  128. };
  129. var processor = initializeOnLoadMethod.Body.GetILProcessor();
  130. foreach (var initializeMethod in _directCallInitializeMethods)
  131. {
  132. processor.Emit(OpCodes.Call, initializeMethod);
  133. }
  134. processor.Emit(OpCodes.Ret);
  135. initializeOnLoadClass.Methods.Add(FixDebugInformation(initializeOnLoadMethod));
  136. var attribute = new CustomAttribute(_unityEngineInitializeOnLoadAttributeCtor);
  137. attribute.ConstructorArguments.Add(new CustomAttributeArgument(_unityEngineRuntimeInitializeLoadType, _unityEngineRuntimeInitializeLoadAfterAssemblies.Constant));
  138. initializeOnLoadMethod.CustomAttributes.Add(attribute);
  139. if (IsForEditor)
  140. {
  141. // Need to ensure the editor tag for initialize on load is present, otherwise edit mode tests will not call Initialize
  142. attribute = new CustomAttribute(_unityEditorInitilizeOnLoadAttributeCtor);
  143. initializeOnLoadMethod.CustomAttributes.Add(attribute);
  144. }
  145. }
  146. private void ProcessType(TypeDefinition type)
  147. {
  148. if (!type.HasGenericParameters && TryGetBurstCompilerAttribute(type, out _))
  149. {
  150. // Make a copy because we are going to modify it
  151. var methodCount = type.Methods.Count;
  152. for (var j = 0; j < methodCount; j++)
  153. {
  154. var method = type.Methods[j];
  155. if (!method.IsStatic || method.HasGenericParameters || !TryGetBurstCompilerAttribute(method, out var methodBurstCompileAttribute)) continue;
  156. bool isDirectCallDisabled = false;
  157. bool foundProperty = false;
  158. if (methodBurstCompileAttribute.HasProperties)
  159. {
  160. foreach (var property in methodBurstCompileAttribute.Properties)
  161. {
  162. if (property.Name == "DisableDirectCall")
  163. {
  164. isDirectCallDisabled = (bool)property.Argument.Value;
  165. foundProperty = true;
  166. break;
  167. }
  168. }
  169. }
  170. // If the method doesn't have a direct call specified, try the assembly level, do one last check for any assembly level [BurstCompile] instead.
  171. if (foundProperty == false && TryGetBurstCompilerAttribute(method.Module.Assembly, out var assemblyBurstCompileAttribute))
  172. {
  173. if (assemblyBurstCompileAttribute.HasProperties)
  174. {
  175. foreach (var property in assemblyBurstCompileAttribute.Properties)
  176. {
  177. if (property.Name == "DisableDirectCall")
  178. {
  179. isDirectCallDisabled = (bool)property.Argument.Value;
  180. break;
  181. }
  182. }
  183. }
  184. }
  185. #if !UNITY_DOTSPLAYER // Direct call is not Supported for dots runtime via this pre-processor, its handled elsewhere, this code assumes a Unity Editor based burst
  186. if (!isDirectCallDisabled)
  187. {
  188. if (_burstAssembly == null)
  189. {
  190. var resolved = methodBurstCompileAttribute.Constructor.DeclaringType.Resolve();
  191. InitializeBurstAssembly(resolved.Module.Assembly);
  192. }
  193. ProcessMethodForDirectCall(method);
  194. _modified = true;
  195. }
  196. #endif
  197. }
  198. }
  199. }
  200. private TypeDefinition InjectDelegate(TypeDefinition declaringType, string originalName, MethodDefinition managed, string uniqueSuffix)
  201. {
  202. var injectedDelegateType = new TypeDefinition(declaringType.Namespace, $"{originalName}{uniqueSuffix}{PostfixBurstDelegate}",
  203. TypeAttributes.NestedPublic |
  204. TypeAttributes.AutoLayout |
  205. TypeAttributes.AnsiClass |
  206. TypeAttributes.Sealed
  207. )
  208. {
  209. DeclaringType = declaringType,
  210. BaseType = _systemDelegateType
  211. };
  212. declaringType.NestedTypes.Add(injectedDelegateType);
  213. {
  214. var constructor = new MethodDefinition(".ctor",
  215. MethodAttributes.Public |
  216. MethodAttributes.HideBySig |
  217. MethodAttributes.SpecialName |
  218. MethodAttributes.RTSpecialName,
  219. _typeSystem.Void)
  220. {
  221. HasThis = true,
  222. IsManaged = true,
  223. IsRuntime = true,
  224. DeclaringType = injectedDelegateType
  225. };
  226. constructor.Parameters.Add(new ParameterDefinition(_typeSystem.Object));
  227. constructor.Parameters.Add(new ParameterDefinition(_typeSystem.IntPtr));
  228. injectedDelegateType.Methods.Add(constructor);
  229. }
  230. {
  231. var invoke = new MethodDefinition("Invoke",
  232. MethodAttributes.Public |
  233. MethodAttributes.HideBySig |
  234. MethodAttributes.NewSlot |
  235. MethodAttributes.Virtual,
  236. managed.ReturnType)
  237. {
  238. HasThis = true,
  239. IsManaged = true,
  240. IsRuntime = true,
  241. DeclaringType = injectedDelegateType
  242. };
  243. foreach (var parameter in managed.Parameters)
  244. {
  245. invoke.Parameters.Add(parameter);
  246. }
  247. injectedDelegateType.Methods.Add(invoke);
  248. }
  249. {
  250. var beginInvoke = new MethodDefinition("BeginInvoke",
  251. MethodAttributes.Public |
  252. MethodAttributes.HideBySig |
  253. MethodAttributes.NewSlot |
  254. MethodAttributes.Virtual,
  255. _systemIASyncResultType)
  256. {
  257. HasThis = true,
  258. IsManaged = true,
  259. IsRuntime = true,
  260. DeclaringType = injectedDelegateType
  261. };
  262. foreach (var parameter in managed.Parameters)
  263. {
  264. beginInvoke.Parameters.Add(parameter);
  265. }
  266. beginInvoke.Parameters.Add(new ParameterDefinition(_systemASyncCallbackType));
  267. beginInvoke.Parameters.Add(new ParameterDefinition(_typeSystem.Object));
  268. injectedDelegateType.Methods.Add(beginInvoke);
  269. }
  270. {
  271. var endInvoke = new MethodDefinition("EndInvoke",
  272. MethodAttributes.Public |
  273. MethodAttributes.HideBySig |
  274. MethodAttributes.NewSlot |
  275. MethodAttributes.Virtual,
  276. managed.ReturnType)
  277. {
  278. HasThis = true,
  279. IsManaged = true,
  280. IsRuntime = true,
  281. DeclaringType = injectedDelegateType
  282. };
  283. endInvoke.Parameters.Add(new ParameterDefinition(_systemIASyncResultType));
  284. injectedDelegateType.Methods.Add(endInvoke);
  285. }
  286. return injectedDelegateType;
  287. }
  288. private MethodDefinition CreateGetFunctionPointerDiscardMethod(TypeDefinition cls, FieldDefinition pointerField, FieldDefinition deferredCompilationField, MethodDefinition burstCompileMethod)
  289. {
  290. // Create GetFunctionPointer method:
  291. //
  292. // [BurstDiscard]
  293. // public static void GetFunctionPointerDiscard(ref IntPtr ptr) {
  294. // if (Pointer == null) {
  295. // Pointer = BurstCompiler.GetILPPMethodFunctionPointer(DeferredCompilation);
  296. // }
  297. //
  298. // ptr = Pointer
  299. // }
  300. var getFunctionPointerDiscardMethod = new MethodDefinition(GetFunctionPointerDiscardName, MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.Static, _typeSystem.Void)
  301. {
  302. ImplAttributes = MethodImplAttributes.IL | MethodImplAttributes.Managed,
  303. DeclaringType = cls
  304. };
  305. getFunctionPointerDiscardMethod.Parameters.Add(new ParameterDefinition(new ByReferenceType(_typeSystem.IntPtr)));
  306. var processor = getFunctionPointerDiscardMethod.Body.GetILProcessor();
  307. processor.Emit(OpCodes.Ldsfld, pointerField);
  308. var branchPosition = processor.Body.Instructions[processor.Body.Instructions.Count - 1];
  309. processor.Emit(OpCodes.Ldsfld, deferredCompilationField);
  310. processor.Emit(OpCodes.Call, _burstCompilerGetILPPMethodFunctionPointer);
  311. processor.Emit(OpCodes.Stsfld, pointerField);
  312. processor.Emit(OpCodes.Ldarg_0);
  313. processor.InsertAfter(branchPosition, Instruction.Create(OpCodes.Brtrue, processor.Body.Instructions[processor.Body.Instructions.Count - 1]));
  314. processor.Emit(OpCodes.Ldsfld, pointerField);
  315. processor.Emit(OpCodes.Stind_I);
  316. processor.Emit(OpCodes.Ret);
  317. cls.Methods.Add(FixDebugInformation(getFunctionPointerDiscardMethod));
  318. getFunctionPointerDiscardMethod.CustomAttributes.Add(new CustomAttribute(_burstDiscardAttributeConstructor));
  319. return getFunctionPointerDiscardMethod;
  320. }
  321. private MethodDefinition CreateGetFunctionPointerMethod(TypeDefinition cls, MethodDefinition getFunctionPointerDiscardMethod)
  322. {
  323. // Create GetFunctionPointer method:
  324. //
  325. // public static IntPtr GetFunctionPointer() {
  326. // var ptr;
  327. // GetFunctionPointerDiscard(ref ptr);
  328. // return ptr;
  329. // }
  330. var getFunctionPointerMethod = new MethodDefinition(GetFunctionPointerName, MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.Static, _typeSystem.IntPtr)
  331. {
  332. ImplAttributes = MethodImplAttributes.IL | MethodImplAttributes.Managed,
  333. DeclaringType = cls
  334. };
  335. getFunctionPointerMethod.Body.Variables.Add(new VariableDefinition(_typeSystem.IntPtr));
  336. getFunctionPointerMethod.Body.InitLocals = true;
  337. var processor = getFunctionPointerMethod.Body.GetILProcessor();
  338. processor.Emit(OpCodes.Ldc_I4_0);
  339. processor.Emit(OpCodes.Conv_I);
  340. processor.Emit(OpCodes.Stloc_0);
  341. processor.Emit(OpCodes.Ldloca_S, (byte)0);
  342. processor.Emit(OpCodes.Call, getFunctionPointerDiscardMethod);
  343. processor.Emit(OpCodes.Ldloc_0);
  344. processor.Emit(OpCodes.Ret);
  345. cls.Methods.Add(FixDebugInformation(getFunctionPointerMethod));
  346. return getFunctionPointerMethod;
  347. }
  348. private void ProcessMethodForDirectCall(MethodDefinition burstCompileMethod)
  349. {
  350. var declaringType = burstCompileMethod.DeclaringType;
  351. var uniqueSuffix = $"_{burstCompileMethod.MetadataToken.RID:X8}";
  352. var injectedDelegate = InjectDelegate(declaringType, burstCompileMethod.Name, burstCompileMethod, uniqueSuffix);
  353. // Create a copy of the original method that will be the actual managed method
  354. // The original method is patched at the end of this method to call
  355. // the dispatcher that will go to the Burst implementation or the managed method (if in the editor and Burst is disabled)
  356. var managedFallbackMethod = new MethodDefinition($"{burstCompileMethod.Name}{PostfixManaged}", burstCompileMethod.Attributes, burstCompileMethod.ReturnType)
  357. {
  358. DeclaringType = declaringType,
  359. ImplAttributes = burstCompileMethod.ImplAttributes,
  360. MetadataToken = burstCompileMethod.MetadataToken,
  361. };
  362. declaringType.Methods.Add(managedFallbackMethod);
  363. foreach (var parameter in burstCompileMethod.Parameters)
  364. {
  365. managedFallbackMethod.Parameters.Add(parameter);
  366. }
  367. foreach (var customAttr in burstCompileMethod.CustomAttributes)
  368. {
  369. managedFallbackMethod.CustomAttributes.Add(customAttr);
  370. }
  371. // Remove the [BurstCompile] on the new managed function.
  372. if (TryGetBurstCompilerAttribute(managedFallbackMethod, out var managedBurstCompileAttribute))
  373. {
  374. managedFallbackMethod.CustomAttributes.Remove(managedBurstCompileAttribute);
  375. }
  376. // Copy the body from the original burst method to the managed fallback, we'll replace the burstCompileMethod body later.
  377. managedFallbackMethod.Body.InitLocals = burstCompileMethod.Body.InitLocals;
  378. managedFallbackMethod.Body.LocalVarToken = burstCompileMethod.Body.LocalVarToken;
  379. managedFallbackMethod.Body.MaxStackSize = burstCompileMethod.Body.MaxStackSize;
  380. managedFallbackMethod.Body.Scope = burstCompileMethod.Body.Scope;
  381. foreach (var variable in burstCompileMethod.Body.Variables)
  382. {
  383. managedFallbackMethod.Body.Variables.Add(variable);
  384. }
  385. foreach (var instruction in burstCompileMethod.Body.Instructions)
  386. {
  387. managedFallbackMethod.Body.Instructions.Add(instruction);
  388. }
  389. foreach (var exceptionHandler in burstCompileMethod.Body.ExceptionHandlers)
  390. {
  391. managedFallbackMethod.Body.ExceptionHandlers.Add(exceptionHandler);
  392. }
  393. managedFallbackMethod.ImplAttributes &= MethodImplAttributes.NoInlining;
  394. // 0x0100 is AggressiveInlining
  395. managedFallbackMethod.ImplAttributes |= (MethodImplAttributes)0x0100;
  396. // The method needs to be public because we query for it in the ILPP code.
  397. managedFallbackMethod.Attributes &= ~MethodAttributes.Private;
  398. managedFallbackMethod.Attributes |= MethodAttributes.Public;
  399. // private static class (Name_RID.$Postfix)
  400. var cls = new TypeDefinition(declaringType.Namespace, $"{burstCompileMethod.Name}{uniqueSuffix}{PostfixBurstDirectCall}",
  401. TypeAttributes.NestedAssembly |
  402. TypeAttributes.AutoLayout |
  403. TypeAttributes.AnsiClass |
  404. TypeAttributes.Abstract |
  405. TypeAttributes.Sealed |
  406. TypeAttributes.BeforeFieldInit
  407. )
  408. {
  409. DeclaringType = declaringType,
  410. BaseType = _typeSystem.Object
  411. };
  412. declaringType.NestedTypes.Add(cls);
  413. // Create Field:
  414. //
  415. // private static IntPtr Pointer;
  416. var pointerField = new FieldDefinition("Pointer", FieldAttributes.Static | FieldAttributes.Private, _typeSystem.IntPtr)
  417. {
  418. DeclaringType = cls
  419. };
  420. cls.Fields.Add(pointerField);
  421. // Create Field:
  422. //
  423. // private static IntPtr DeferredCompilation;
  424. var deferredCompilationField = new FieldDefinition("DeferredCompilation", FieldAttributes.Static | FieldAttributes.Private, _typeSystem.IntPtr)
  425. {
  426. DeclaringType = cls
  427. };
  428. cls.Fields.Add(deferredCompilationField);
  429. var getFunctionPointerDiscardMethod = CreateGetFunctionPointerDiscardMethod(cls, pointerField, deferredCompilationField, burstCompileMethod);
  430. var getFunctionPointerMethod = CreateGetFunctionPointerMethod(cls, getFunctionPointerDiscardMethod);
  431. var asmAttribute = new CustomAttribute(_burstCompilerCompileUnsafeStaticMethodReinitialiseAttributeCtor);
  432. asmAttribute.ConstructorArguments.Add(new CustomAttributeArgument(_systemType, cls));
  433. _assemblyDefinition.CustomAttributes.Add(asmAttribute);
  434. // Create the static Constructor Method (called via .cctor and via reflection on burst compilation enable)
  435. // private static void Constructor() {
  436. // deferredCompilation = CompileILPPMethod(burstCompileMethod, managedFallbackMethod, DelegateType);
  437. // }
  438. var constructor = new MethodDefinition("Constructor", MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Static, _typeSystem.Void)
  439. {
  440. ImplAttributes = MethodImplAttributes.IL | MethodImplAttributes.Managed,
  441. DeclaringType = cls
  442. };
  443. var processor = constructor.Body.GetILProcessor();
  444. processor.Emit(OpCodes.Ldtoken, burstCompileMethod);
  445. processor.Emit(OpCodes.Ldtoken, managedFallbackMethod);
  446. processor.Emit(OpCodes.Ldtoken, injectedDelegate);
  447. processor.Emit(OpCodes.Call, _burstCompilerCompileILPPMethod);
  448. processor.Emit(OpCodes.Stsfld, deferredCompilationField);
  449. processor.Emit(OpCodes.Ret);
  450. cls.Methods.Add(FixDebugInformation(constructor));
  451. // Create an Initialize method
  452. // This will be called from the single [RuntimeInitializeOnLoadMethod]
  453. // method that we'll generate for this assembly.
  454. // Its only job is to cause the .cctor to run.
  455. //
  456. // public static void Initialize() { }
  457. var initializeMethod = new MethodDefinition("Initialize", MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Static, _typeSystem.Void)
  458. {
  459. ImplAttributes = MethodImplAttributes.IL | MethodImplAttributes.Managed,
  460. DeclaringType = cls
  461. };
  462. processor = initializeMethod.Body.GetILProcessor();
  463. processor.Emit(OpCodes.Ret);
  464. cls.Methods.Add(FixDebugInformation(initializeMethod));
  465. var currentInitializer = initializeMethod;
  466. var currentDeclaringType = declaringType;
  467. // If our target method is hidden behind one or more nested private classes, then
  468. // create a method on the parent type that calls said method (for each private nested class)
  469. while (currentDeclaringType.DeclaringType != null)
  470. {
  471. var parentType = currentDeclaringType.DeclaringType;
  472. if (((currentDeclaringType.Attributes & TypeAttributes.NestedPrivate) == TypeAttributes.NestedPrivate))
  473. {
  474. var redirectingInitializer = new MethodDefinition($"Initialize${declaringType.Name}_{cls.Name}",
  475. MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Static,
  476. _typeSystem.Void)
  477. {
  478. ImplAttributes = MethodImplAttributes.IL | MethodImplAttributes.Managed,
  479. DeclaringType = parentType
  480. };
  481. processor = redirectingInitializer.Body.GetILProcessor();
  482. processor.Emit(OpCodes.Call, currentInitializer);
  483. processor.Emit(OpCodes.Ret);
  484. parentType.Methods.Add(redirectingInitializer);
  485. currentInitializer = redirectingInitializer;
  486. }
  487. currentDeclaringType = parentType;
  488. }
  489. _directCallInitializeMethods.Add(currentInitializer);
  490. // Create the static constructor
  491. //
  492. // public static .cctor() {
  493. // Constructor();
  494. // }
  495. var cctor = new MethodDefinition(".cctor", MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName | MethodAttributes.Static, _typeSystem.Void)
  496. {
  497. ImplAttributes = MethodImplAttributes.IL | MethodImplAttributes.Managed,
  498. DeclaringType = cls,
  499. };
  500. processor = cctor.Body.GetILProcessor();
  501. processor.Emit(OpCodes.Call, constructor);
  502. processor.Emit(OpCodes.Ret);
  503. cls.Methods.Add(FixDebugInformation(cctor));
  504. // Create the Invoke method based on the original method (same signature)
  505. //
  506. // public static XXX Invoke(...args) {
  507. // if (BurstCompiler.IsEnabled)
  508. // {
  509. // var funcPtr = GetFunctionPointer();
  510. // if (funcPtr != null) return funcPtr(...args);
  511. // }
  512. // return OriginalMethod(...args);
  513. // }
  514. var invokeAttributes = managedFallbackMethod.Attributes;
  515. invokeAttributes &= ~MethodAttributes.Private;
  516. invokeAttributes |= MethodAttributes.Public;
  517. var invoke = new MethodDefinition(InvokeName, invokeAttributes, burstCompileMethod.ReturnType)
  518. {
  519. ImplAttributes = MethodImplAttributes.IL | MethodImplAttributes.Managed,
  520. DeclaringType = cls
  521. };
  522. var signature = new CallSite(burstCompileMethod.ReturnType)
  523. {
  524. CallingConvention = MethodCallingConvention.C
  525. };
  526. foreach (var parameter in burstCompileMethod.Parameters)
  527. {
  528. invoke.Parameters.Add(parameter);
  529. signature.Parameters.Add(parameter);
  530. }
  531. invoke.Body.Variables.Add(new VariableDefinition(_typeSystem.IntPtr));
  532. invoke.Body.InitLocals = true;
  533. processor = invoke.Body.GetILProcessor();
  534. processor.Emit(OpCodes.Call, _burstCompilerIsEnabledMethodDefinition);
  535. var branchPosition0 = processor.Body.Instructions[processor.Body.Instructions.Count - 1];
  536. processor.Emit(OpCodes.Call, getFunctionPointerMethod);
  537. processor.Emit(OpCodes.Stloc_0);
  538. processor.Emit(OpCodes.Ldloc_0);
  539. var branchPosition1 = processor.Body.Instructions[processor.Body.Instructions.Count - 1];
  540. EmitArguments(processor, invoke);
  541. processor.Emit(OpCodes.Ldloc_0);
  542. processor.Emit(OpCodes.Calli, signature);
  543. processor.Emit(OpCodes.Ret);
  544. var previousRet = processor.Body.Instructions[processor.Body.Instructions.Count - 1];
  545. EmitArguments(processor, invoke);
  546. processor.Emit(OpCodes.Call, managedFallbackMethod);
  547. processor.Emit(OpCodes.Ret);
  548. // Insert the branch once we have emitted the instructions
  549. processor.InsertAfter(branchPosition0, Instruction.Create(OpCodes.Brfalse, previousRet.Next));
  550. processor.InsertAfter(branchPosition1, Instruction.Create(OpCodes.Brfalse, previousRet.Next));
  551. cls.Methods.Add(FixDebugInformation(invoke));
  552. // Final patching of the original method
  553. // public static XXX OriginalMethod(...args) {
  554. // Name_RID.$Postfix.Invoke(...args);
  555. // ret;
  556. // }
  557. burstCompileMethod.Body = new MethodBody(burstCompileMethod);
  558. processor = burstCompileMethod.Body.GetILProcessor();
  559. EmitArguments(processor, burstCompileMethod);
  560. processor.Emit(OpCodes.Call, invoke);
  561. processor.Emit(OpCodes.Ret);
  562. FixDebugInformation(burstCompileMethod);
  563. }
  564. private static MethodDefinition FixDebugInformation(MethodDefinition method)
  565. {
  566. method.DebugInformation.Scope = new ScopeDebugInformation(method.Body.Instructions.First(), method.Body.Instructions.Last());
  567. return method;
  568. }
  569. private AssemblyDefinition GetAsmDefinitionFromFile(AssemblyLoader loader, string filename)
  570. {
  571. foreach (var folder in loader.GetSearchDirectories())
  572. {
  573. var path = Path.Combine(folder, filename);
  574. if (File.Exists(path))
  575. return loader.LoadFromFile(path);
  576. }
  577. return null;
  578. }
  579. private MethodReference _unityEngineInitializeOnLoadAttributeCtor;
  580. private TypeReference _unityEngineRuntimeInitializeLoadType;
  581. private FieldDefinition _unityEngineRuntimeInitializeLoadAfterAssemblies;
  582. private MethodReference _unityEditorInitilizeOnLoadAttributeCtor;
  583. private void InitializeBurstAssembly(AssemblyDefinition burstAssembly)
  584. {
  585. _burstAssembly = burstAssembly;
  586. _burstCompilerTypeDefinition = burstAssembly.MainModule.GetType("Unity.Burst", "BurstCompiler");
  587. _burstCompilerIsEnabledMethodDefinition = _assemblyDefinition.MainModule.ImportReference(_burstCompilerTypeDefinition.Methods.FirstOrDefault(x => x.Name == "get_IsEnabled"));
  588. _burstCompilerCompileILPPMethod = _assemblyDefinition.MainModule.ImportReference(_burstCompilerTypeDefinition.Methods.FirstOrDefault(x => x.Name == "CompileILPPMethod"));
  589. _burstCompilerGetILPPMethodFunctionPointer = _assemblyDefinition.MainModule.ImportReference(_burstCompilerTypeDefinition.Methods.FirstOrDefault(x => x.Name == "GetILPPMethodFunctionPointer"));
  590. var reinitializeAttribute = _burstCompilerTypeDefinition.NestedTypes.FirstOrDefault(x => x.Name == "StaticTypeReinitAttribute");
  591. _burstCompilerCompileUnsafeStaticMethodReinitialiseAttributeCtor = _assemblyDefinition.MainModule.ImportReference(reinitializeAttribute.Methods.FirstOrDefault(x=>x.Name == ".ctor" && x.HasParameters));
  592. var corLibrary = Loader.Resolve((AssemblyNameReference)_typeSystem.CoreLibrary);
  593. _systemType = _assemblyDefinition.MainModule.ImportReference(corLibrary.MainModule.GetType("System.Type"));
  594. _systemDelegateType = _assemblyDefinition.MainModule.ImportReference(corLibrary.MainModule.GetType("System.MulticastDelegate"));
  595. _systemASyncCallbackType = _assemblyDefinition.MainModule.ImportReference(corLibrary.MainModule.GetType("System.AsyncCallback"));
  596. _systemIASyncResultType = _assemblyDefinition.MainModule.ImportReference(corLibrary.MainModule.GetType("System.IAsyncResult"));
  597. var asmDef = GetAsmDefinitionFromFile(Loader, "UnityEngine.CoreModule.dll");
  598. var runtimeInitializeOnLoadMethodAttribute = asmDef.MainModule.GetType("UnityEngine", "RuntimeInitializeOnLoadMethodAttribute");
  599. var runtimeInitializeLoadType = asmDef.MainModule.GetType("UnityEngine", "RuntimeInitializeLoadType");
  600. var burstDiscardType = asmDef.MainModule.GetType("Unity.Burst", "BurstDiscardAttribute");
  601. _burstDiscardAttributeConstructor = _assemblyDefinition.MainModule.ImportReference(burstDiscardType.Methods.First(method => method.Name == ".ctor"));
  602. _unityEngineInitializeOnLoadAttributeCtor = _assemblyDefinition.MainModule.ImportReference(runtimeInitializeOnLoadMethodAttribute.Methods.FirstOrDefault(x => x.Name == ".ctor" && x.HasParameters));
  603. _unityEngineRuntimeInitializeLoadType = _assemblyDefinition.MainModule.ImportReference(runtimeInitializeLoadType);
  604. _unityEngineRuntimeInitializeLoadAfterAssemblies = runtimeInitializeLoadType.Fields.FirstOrDefault(x => x.Name=="AfterAssembliesLoaded");
  605. if (IsForEditor)
  606. {
  607. asmDef = GetAsmDefinitionFromFile(Loader, "UnityEditor.CoreModule.dll");
  608. if (asmDef == null)
  609. asmDef = GetAsmDefinitionFromFile(Loader, "UnityEditor.dll");
  610. var initializeOnLoadMethodAttribute = asmDef.MainModule.GetType("UnityEditor", "InitializeOnLoadMethodAttribute");
  611. _unityEditorInitilizeOnLoadAttributeCtor = _assemblyDefinition.MainModule.ImportReference(initializeOnLoadMethodAttribute.Methods.FirstOrDefault(x => x.Name == ".ctor" && !x.HasParameters));
  612. }
  613. }
  614. private static void EmitArguments(ILProcessor processor, MethodDefinition method)
  615. {
  616. for (var i = 0; i < method.Parameters.Count; i++)
  617. {
  618. switch (i)
  619. {
  620. case 0:
  621. processor.Emit(OpCodes.Ldarg_0);
  622. break;
  623. case 1:
  624. processor.Emit(OpCodes.Ldarg_1);
  625. break;
  626. case 2:
  627. processor.Emit(OpCodes.Ldarg_2);
  628. break;
  629. case 3:
  630. processor.Emit(OpCodes.Ldarg_3);
  631. break;
  632. default:
  633. if (i <= 255)
  634. {
  635. processor.Emit(OpCodes.Ldarg_S, (byte)i);
  636. }
  637. else
  638. {
  639. processor.Emit(OpCodes.Ldarg, i);
  640. }
  641. break;
  642. }
  643. }
  644. }
  645. private static bool TryGetBurstCompilerAttribute(ICustomAttributeProvider provider, out CustomAttribute customAttribute)
  646. {
  647. if (provider.HasCustomAttributes)
  648. {
  649. foreach (var customAttr in provider.CustomAttributes)
  650. {
  651. if (customAttr.Constructor.DeclaringType.Name == "BurstCompileAttribute")
  652. {
  653. customAttribute = customAttr;
  654. return true;
  655. }
  656. }
  657. }
  658. customAttribute = null;
  659. return false;
  660. }
  661. }
  662. }