暫無描述
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

BurstILPostProcessor.cs 6.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. #if UNITY_2019_3_OR_NEWER
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Diagnostics;
  5. using System.IO;
  6. using System.Linq;
  7. using Mono.Cecil;
  8. using Mono.Cecil.Cil;
  9. using Unity.CompilationPipeline.Common.Diagnostics;
  10. using Unity.CompilationPipeline.Common.ILPostProcessing;
  11. /// Deliberately named zzzUnity.Burst.CodeGen, as we need to ensure its last in the chain
  12. namespace zzzUnity.Burst.CodeGen
  13. {
  14. /// <summary>
  15. /// Postprocessor used to replace calls from C# to [BurstCompile] functions to direct calls to
  16. /// Burst native code functions without having to go through a C# delegate.
  17. /// </summary>
  18. internal class BurstILPostProcessor : ILPostProcessor
  19. {
  20. public bool IsDebugging;
  21. public int DebuggingLevel;
  22. private void SetupDebugging()
  23. {
  24. // This can be setup to get more diagnostics
  25. var debuggingStr = Environment.GetEnvironmentVariable("UNITY_BURST_DEBUG");
  26. IsDebugging = debuggingStr != null;
  27. if (IsDebugging)
  28. {
  29. Log("[com.unity.burst] Extra debugging is turned on.");
  30. int debuggingLevel;
  31. int.TryParse(debuggingStr, out debuggingLevel);
  32. if (debuggingLevel <= 0) debuggingLevel = 1;
  33. DebuggingLevel = debuggingLevel;
  34. }
  35. }
  36. private static SequencePoint FindBestSequencePointFor(MethodDefinition method, Instruction instruction)
  37. {
  38. var sequencePoints = method.DebugInformation?.GetSequencePointMapping().Values.OrderBy(s => s.Offset).ToList();
  39. if (sequencePoints == null || !sequencePoints.Any())
  40. return null;
  41. for (int i = 0; i != sequencePoints.Count-1; i++)
  42. {
  43. if (sequencePoints[i].Offset < instruction.Offset &&
  44. sequencePoints[i + 1].Offset > instruction.Offset)
  45. return sequencePoints[i];
  46. }
  47. return sequencePoints.FirstOrDefault();
  48. }
  49. private static DiagnosticMessage MakeDiagnosticError(MethodDefinition method, Instruction errorLocation, string message)
  50. {
  51. var m = new DiagnosticMessage { DiagnosticType = DiagnosticType.Error };
  52. var sPoint = errorLocation != null ? FindBestSequencePointFor(method, errorLocation) : null;
  53. if (sPoint!=null)
  54. {
  55. m.Column = sPoint.StartColumn;
  56. m.Line = sPoint.StartLine;
  57. m.File = sPoint.Document.Url;
  58. }
  59. m.MessageData = message;
  60. return m;
  61. }
  62. public override unsafe ILPostProcessResult Process(ICompiledAssembly compiledAssembly)
  63. {
  64. var diagnostics = new List<DiagnosticMessage>();
  65. if (!WillProcess(compiledAssembly))
  66. return new ILPostProcessResult(null, diagnostics);
  67. bool wasModified = false;
  68. SetupDebugging();
  69. bool debugging = IsDebugging && DebuggingLevel >= 2;
  70. var inMemoryAssembly = compiledAssembly.InMemoryAssembly;
  71. var peData = inMemoryAssembly.PeData;
  72. var pdbData = inMemoryAssembly.PdbData;
  73. var loader = new AssemblyLoader();
  74. var folders = new HashSet<string>();
  75. var isForEditor = compiledAssembly.Defines?.Contains("UNITY_EDITOR") ?? false;
  76. foreach (var reference in compiledAssembly.References)
  77. {
  78. folders.Add(Path.Combine(Environment.CurrentDirectory, Path.GetDirectoryName(reference)));
  79. }
  80. var folderList = folders.OrderBy(x => x).ToList();
  81. foreach (var folder in folderList)
  82. {
  83. loader.AddSearchDirectory(folder);
  84. }
  85. var clock = Stopwatch.StartNew();
  86. if (debugging)
  87. {
  88. Log($"Start processing assembly {compiledAssembly.Name}, IsForEditor: {isForEditor}, Folders: {string.Join("\n", folderList)}");
  89. }
  90. var ilPostProcessing = new ILPostProcessing(loader, isForEditor,
  91. (m,i,s) => { diagnostics.Add(MakeDiagnosticError(m, i, s)); },
  92. IsDebugging ? Log : (LogDelegate)null, DebuggingLevel);
  93. var functionPointerProcessing = new FunctionPointerInvokeTransform(loader,
  94. (m,i,s) => { diagnostics.Add(MakeDiagnosticError(m, i, s)); },
  95. IsDebugging ? Log : (LogDelegate)null, DebuggingLevel);
  96. try
  97. {
  98. // For IL Post Processing, use the builtin symbol reader provider
  99. var assemblyDefinition = loader.LoadFromStream(new MemoryStream(peData), new MemoryStream(pdbData), new PortablePdbReaderProvider() );
  100. wasModified |= ilPostProcessing.Run(assemblyDefinition);
  101. wasModified |= functionPointerProcessing.Run(assemblyDefinition);
  102. if (wasModified)
  103. {
  104. var peStream = new MemoryStream();
  105. var pdbStream = new MemoryStream();
  106. var writeParameters = new WriterParameters
  107. {
  108. SymbolWriterProvider = new PortablePdbWriterProvider(),
  109. WriteSymbols = true,
  110. SymbolStream = pdbStream
  111. };
  112. assemblyDefinition.Write(peStream, writeParameters);
  113. peStream.Flush();
  114. pdbStream.Flush();
  115. peData = peStream.ToArray();
  116. pdbData = pdbStream.ToArray();
  117. }
  118. }
  119. catch (Exception ex)
  120. {
  121. throw new InvalidOperationException($"Internal compiler error for Burst ILPostProcessor on {compiledAssembly.Name}. Exception: {ex}");
  122. }
  123. if (debugging)
  124. {
  125. Log($"End processing assembly {compiledAssembly.Name} in {clock.Elapsed.TotalMilliseconds}ms.");
  126. }
  127. if (wasModified && !diagnostics.Any(d => d.DiagnosticType == DiagnosticType.Error))
  128. {
  129. return new ILPostProcessResult(new InMemoryAssembly(peData, pdbData), diagnostics);
  130. }
  131. return new ILPostProcessResult(null, diagnostics);
  132. }
  133. private static void Log(string message)
  134. {
  135. #if !UNITY_2020_2_OR_NEWER && !UNITY_DOTSPLAYER
  136. UnityEngine.Debug.Log($"{nameof(BurstILPostProcessor)}: {message}");
  137. #else
  138. Console.WriteLine($"{nameof(BurstILPostProcessor)}: {message}");
  139. #endif
  140. }
  141. public override ILPostProcessor GetInstance()
  142. {
  143. return this;
  144. }
  145. public override bool WillProcess(ICompiledAssembly compiledAssembly)
  146. {
  147. return compiledAssembly.References.Any(f => Path.GetFileName(f) == "Unity.Burst.dll");
  148. }
  149. }
  150. }
  151. #endif