暂无描述
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

BurstDisassemblerTests.cs 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Text;
  7. using System.Reflection;
  8. using NUnit.Framework;
  9. using UnityEngine;
  10. using UnityEngine.TestTools;
  11. using Unity.Burst;
  12. using Unity.Burst.Editor;
  13. using UnityEditorInternal;
  14. using System.Runtime.CompilerServices;
  15. public class BurstDisassemblerTests
  16. {
  17. private BurstDisassembler _disassembler;
  18. [OneTimeSetUp]
  19. public void SetUp()
  20. {
  21. _disassembler = new BurstDisassembler();
  22. }
  23. private static string GetThisFilePath([CallerFilePath] string path = null) => path;
  24. // A Test behaves as an ordinary method
  25. [Test]
  26. public void GetBlockIdxFromTextIdxTest()
  27. {
  28. var thisPath = Path.GetDirectoryName(GetThisFilePath());
  29. Assert.IsTrue(_disassembler.Initialize(
  30. File.ReadAllText(Path.Combine(thisPath, "burstTestTarget.txt")),
  31. BurstDisassembler.AsmKind.Intel,
  32. false,
  33. false));
  34. for (int blockIdx = 0; blockIdx < _disassembler.Blocks.Count; blockIdx++)
  35. {
  36. int blockStart = 0;
  37. for (int i = 0; i < blockIdx; i++)
  38. {
  39. blockStart += _disassembler.GetOrRenderBlockToText(i).Length;
  40. }
  41. var blockStr = _disassembler.GetOrRenderBlockToText(blockIdx);
  42. Assert.AreEqual((blockIdx, blockStart),
  43. _disassembler.GetBlockIdxFromTextIdx(blockStart + 1),
  44. $"Block index was wrong for block with label {blockStr.Substring(0, blockStr.IndexOf('\n'))}");
  45. }
  46. }
  47. [Test]
  48. public void InstantiateRegistersUsedTest()
  49. {
  50. Assert.IsTrue(_disassembler.Initialize(simpleAssembly, BurstDisassembler.AsmKind.Intel));
  51. var regsUsed = _disassembler._registersUsedAtLine;
  52. // Match against expected:
  53. var expectedLines = from l in expected select l.lineNr;
  54. var failed = expectedLines.Except(regsUsed._linesRegisters.Keys);
  55. failed = failed.Concat(regsUsed._linesRegisters.Keys.Except(expectedLines)).Distinct();
  56. if (failed.Any())
  57. {
  58. // Not exact match
  59. foreach (var f in failed)
  60. {
  61. Debug.Log($"lineNumber {f} failed");
  62. }
  63. Assert.Fail();
  64. }
  65. }
  66. [Test]
  67. public void CleanRegisterListTest()
  68. {
  69. Assert.IsTrue(_disassembler.Initialize(simpleAssembly, BurstDisassembler.AsmKind.Intel));
  70. var regs = new List<string> { "rcx", "ecx", "rax" };
  71. var output = _disassembler._registersUsedAtLine.CleanRegs(regs);
  72. var expected = new List<string> { "rcx", "rax" };
  73. Assert.AreEqual(output, expected);
  74. }
  75. [Test]
  76. public void IndexOfRegisterTest()
  77. {
  78. var assembly =
  79. "\n" +
  80. " nop\n" +
  81. " movsxd rcx, cx\n" +
  82. " mov rax, qword ptr [rbp - 16]";
  83. Assert.IsTrue(_disassembler.Initialize(assembly, BurstDisassembler.AsmKind.Intel));
  84. string[,] regs =
  85. {
  86. { "rcx", "cx" },
  87. { "rax", "rbp" }
  88. };
  89. string[] lines =
  90. {
  91. " movsxd rcx, cx\n",
  92. " mov rax, qword ptr [rbp - 16]"
  93. };
  94. for (var i = 0; i < 2; i++)
  95. {
  96. var line = lines[i];
  97. var reg = regs[i, 0];
  98. var asmLine = _disassembler.Lines[i+1];
  99. var output = _disassembler.GetRegisterTokenIndex(asmLine, reg);
  100. var regIdx = _disassembler.Tokens[output].AlignedPosition - _disassembler.Tokens[asmLine.TokenIndex].AlignedPosition;
  101. var expected = line.IndexOf(reg) + 1;
  102. Assert.AreEqual(expected, regIdx, $"Failed for line \"{line}\"");
  103. reg = regs[i, 1];
  104. output = _disassembler.GetRegisterTokenIndex(asmLine, reg, output + 1);
  105. regIdx = _disassembler.Tokens[output].AlignedPosition - _disassembler.Tokens[asmLine.TokenIndex].AlignedPosition;
  106. expected = line.IndexOf(reg, expected + 1) + 1;
  107. Assert.AreEqual(expected, regIdx, $"Failed for line \"{line}\"");
  108. }
  109. }
  110. [Test]
  111. [TestCase("x86", new [] {"rdx","edx","dx","dl"}, "dl")]
  112. [TestCase("arm", new [] {"wsp", "sp"},"sp")]
  113. [TestCase("arm", new [] {"v0.2d", "s0", "q0", "h0", "d0", "b0"}, "b0")]
  114. [TestCase("arm", new [] {"w0","x0"}, "x0")]
  115. public void RegisterEqualityTest(string assemblyName, string[] assemblyLine, string register)
  116. {
  117. BurstDisassembler.AsmTokenKindProvider tokenProvider = BurstDisassembler.ARM64AsmTokenKindProvider.Instance;
  118. if (assemblyName == "x86")
  119. {
  120. tokenProvider = BurstDisassembler.X86AsmTokenKindProvider.Instance;
  121. }
  122. foreach (var reg in assemblyLine)
  123. {
  124. Assert.IsTrue(tokenProvider.RegisterEqual(reg, register), $"{reg} == {register}");
  125. }
  126. // Some special cases:
  127. tokenProvider = BurstDisassembler.ARM64AsmTokenKindProvider.Instance;
  128. Assert.IsFalse(tokenProvider.RegisterEqual("w8", "x0"), $"w8 != x0");
  129. Assert.IsFalse(tokenProvider.RegisterEqual("w0", "q0"), "w0 != q0");
  130. Assert.IsFalse(tokenProvider.RegisterEqual("x0", "q0"), "x0 != q0");
  131. }
  132. [Test]
  133. public void RegisterEqualTest()
  134. {
  135. // Only tests for x86, as the others are trivial.
  136. Assert.IsTrue(_disassembler.Initialize(simpleAssembly, BurstDisassembler.AsmKind.Intel));
  137. // Get all register strings:
  138. var tokenProvider = BurstDisassembler.X86AsmTokenKindProvider.Instance;
  139. var tokenProviderT = typeof(BurstDisassembler.AsmTokenKindProvider);
  140. var field = tokenProviderT.GetField("_tokenKinds",
  141. BindingFlags.Instance | BindingFlags.NonPublic);
  142. Assert.NotNull(field, "Could not find _tokenKinds field in AsmTokenKindProvider");
  143. var allTokens = (Dictionary<StringSlice, BurstDisassembler.AsmTokenKind>)field.GetValue(tokenProvider);
  144. var tokensToTest =
  145. from tok in allTokens.Keys
  146. where allTokens.TryGetValue(tok, out var kind)
  147. && kind == BurstDisassembler.AsmTokenKind.Register
  148. select tok.ToString();
  149. // Test that equality works for all registers:
  150. try
  151. {
  152. foreach (var reg in tokensToTest)
  153. {
  154. // Simply check whether all registers are processable:
  155. tokenProvider.RegisterEqual(reg, "rax");
  156. }
  157. }
  158. catch (Exception e)
  159. {
  160. Assert.Fail($"Not all registers works for register equality (x86). {e}");
  161. }
  162. }
  163. [Test]
  164. public void InstructionAlignmentTest()
  165. {
  166. var assembly =
  167. "\n" + // newline as BurstDisassembler ignores first line
  168. " push rbp\n" +
  169. " .seh_pushreg rbp\n" +
  170. " sub rsp, 32\n";
  171. (int, char)[] expectedPositions =
  172. {
  173. (1,' '), (10, 'p'), (14, ' '), (24, 'r'), (27, '\n'),
  174. (28, ' '), (37, '.'), (49, ' '), (50, 'r'), (53, '\n'),
  175. (54, ' '), (63, 's'), (66, ' '), (77, 'r'), (80, ','), (82, '3'), (84, '\n')
  176. };
  177. Assert.IsTrue(_disassembler.Initialize(assembly, BurstDisassembler.AsmKind.Intel));
  178. var builder = new StringBuilder();
  179. for (int i = 0; i < _disassembler.Blocks.Count; i++)
  180. {
  181. var text = _disassembler.GetOrRenderBlockToTextUncached(i, false);
  182. builder.Append(text);
  183. }
  184. var output = builder.ToString();
  185. for (var i = 0; i < expectedPositions.Length; i++)
  186. {
  187. Assert.AreEqual(expectedPositions[i].Item1, _disassembler.Tokens[i].AlignedPosition);
  188. }
  189. foreach (var (idx, c) in expectedPositions)
  190. {
  191. // -1 as token index for some reason aren't zero indexed.
  192. Assert.AreEqual(c, output[idx-1], $"Token position for index {idx} was wrong.");
  193. }
  194. }
  195. [Test]
  196. public void X86AsmTokenProviderSimdKindTest()
  197. {
  198. var tp = BurstDisassembler.X86AsmTokenKindProvider.Instance;
  199. BurstDisassembler.SIMDkind actual = tp.SimdKind(new StringSlice("vsqrtsd"));
  200. var expected = BurstDisassembler.SIMDkind.Scalar;
  201. Assert.AreEqual(expected, actual);
  202. actual = tp.SimdKind(new StringSlice("vroundpd"));
  203. expected = BurstDisassembler.SIMDkind.Packed;
  204. Assert.AreEqual(expected, actual);
  205. actual = tp.SimdKind(new StringSlice("xsaves"));
  206. expected = BurstDisassembler.SIMDkind.Infrastructure;
  207. Assert.AreEqual(expected,actual);
  208. }
  209. [Test]
  210. public void ARMAsmTokenProviderSimdKindTest()
  211. {
  212. var tp = BurstDisassembler.ARM64AsmTokenKindProvider.Instance;
  213. BurstDisassembler.SIMDkind actual = tp.SimdKind(new StringSlice("vaddw"));
  214. var expected = BurstDisassembler.SIMDkind.Scalar;
  215. Assert.AreEqual(expected, actual);
  216. actual = tp.SimdKind(new StringSlice("vadd.i8"));
  217. expected = BurstDisassembler.SIMDkind.Packed;
  218. Assert.AreEqual(expected, actual);
  219. }
  220. private string GetFirstColorTag(string line)
  221. {
  222. const string colorTag = "#XXXXXX";
  223. const string tag = "<color=";
  224. int idx = line.IndexOf('<');
  225. return line.Substring(idx + tag.Length, colorTag.Length);
  226. }
  227. private const string ARMsimdAssembly =
  228. "\n" +
  229. " ldr r0, [sp, #12]\n" +
  230. " vldr s0, [sp, #20]\n" +
  231. " vstr s0, [sp, #4]\n" +
  232. " ldr r1, [sp, #24]\n" +
  233. " vldr s0, [sp, #4]\n" +
  234. " vmov s2, r0\n" +
  235. " vadd.f32 s0, s0, s2\n" +
  236. " vstr s0, [sp, #20]";
  237. private const string X86SimdAssembly =
  238. "\n" +
  239. " mov rcx, qword ptr [rbp - 32]\n" +
  240. " vmovss xmm0, dword ptr [rbp - 12]\n" +
  241. " vmovss dword ptr [rbp - 40], xmm0\n" +
  242. " mov edx, dword ptr [rbp - 8]\n" +
  243. " call \"Unity.Collections.NativeArray`1<float>.get_Item(Unity.Collections.NativeArray`1<float>* this, int index) -> float_c303f72c9cc472e2ef84a442ead69ef2 from Unity.Burst.Editor.Tests, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null\"\n" +
  244. " vmovaps xmm1, xmm0\n" +
  245. " vmovss xmm0, dword ptr [rbp - 40]\n" +
  246. " vaddss xmm0, xmm0, xmm1\n" +
  247. " vmovss dword ptr [rbp - 12], xmm0\n" +
  248. " vzeroall";
  249. [Test]
  250. [TestCase(X86SimdAssembly, 0, BurstDisassembler.DarkColorInstructionSIMDScalar, 1)]
  251. [TestCase(X86SimdAssembly, 0, BurstDisassembler.DarkColorInstructionSIMDPacked, 5)]
  252. [TestCase(X86SimdAssembly, 0, BurstDisassembler.DarkColorInstructionSIMD, 9)]
  253. [TestCase(ARMsimdAssembly, 1, BurstDisassembler.DarkColorInstructionSIMDScalar, 1)]
  254. [TestCase(ARMsimdAssembly, 1, BurstDisassembler.DarkColorInstructionSIMDPacked, 6)]
  255. public void AssemblyColouringSmellTest(string asm, int asmkind, string colorTag, int lineIdx)
  256. {
  257. _disassembler.Initialize(asm, (BurstDisassembler.AsmKind)asmkind, true, true, true);
  258. var line = _disassembler.Lines[lineIdx];
  259. _disassembler._output.Clear();
  260. _disassembler.RenderLine(ref line, true);
  261. var lineString = _disassembler._output.ToString();
  262. _disassembler._output.Clear();
  263. Assert.AreEqual(colorTag, GetFirstColorTag(lineString));
  264. }
  265. private List<(int lineNr, List<string>)> expected = new List<(int lineNr, List<string>)>
  266. {
  267. (2, new List<string> { "rbp" }),
  268. (3, new List<string> { "rbp" }),
  269. (4, new List<string> { "rsp" }),
  270. (6, new List<string> { "rbp", "rsp" }),
  271. (7, new List<string> { "rbp" }),
  272. (11, new List<string> { "rsp" }),
  273. (12, new List<string> { "rbp" }),
  274. (26, new List<string> { "rbp" }),
  275. (27, new List<string> { "rbp" }),
  276. (28, new List<string> { "rsp" }),
  277. (30, new List<string> { "rbp", "rsp" }),
  278. (31, new List<string> { "rbp" }),
  279. (36, new List<string> { "rsp" }),
  280. (37, new List<string> { "rbp" }),
  281. };
  282. private string simpleAssembly =
  283. "\n" + // newline as BurstDisassembler ignores first line
  284. ".Lfunc_begin0:\n" +
  285. ".seh_proc 589a9d678dbb1201e550a054238fad11\n" +
  286. " push rbp\n" +
  287. " .seh_pushreg rbp\n" +
  288. " sub rsp, 32\n" +
  289. " .seh_stackalloc 32\n" +
  290. " lea rbp, [rsp + 32]\n" +
  291. " .seh_setframe rbp, 32\n" +
  292. " .seh_endprologue\n" +
  293. " call A.B.DoIt\n" +
  294. " nop\n" +
  295. " add rsp, 32\n" +
  296. " pop rbp\n" +
  297. " ret\n" +
  298. " .Lfunc_end0:\n" +
  299. " .seh_endproc\n" +
  300. " \n" +
  301. " .def burst.initialize;\n" +
  302. " .scl 2;\n" +
  303. " .type 32;\n" +
  304. " .endef\n" +
  305. " .globl burst.initialize\n" +
  306. " .p2align 4, 0x90\n" +
  307. " burst.initialize:\n" +
  308. " .Lfunc_begin1:\n" +
  309. " .seh_proc burst.initialize\n" +
  310. " push rbp\n" +
  311. " .seh_pushreg rbp\n" +
  312. " sub rsp, 32\n" +
  313. " .seh_stackalloc 32\n" +
  314. " lea rbp, [rsp + 32]\n" +
  315. " .seh_setframe rbp, 32\n" +
  316. " .seh_endprologue\n" +
  317. " call burst.initialize.externals\n" +
  318. " call burst.initialize.statics\n" +
  319. " nop\n" +
  320. " add rsp, 32\n" +
  321. " pop rbp\n" +
  322. " ret\n" +
  323. " .Lfunc_end1:\n" +
  324. " .seh_endproc\n" +
  325. " \n" +
  326. " .def burst.initialize.externals;\n" +
  327. " .scl 2;\n" +
  328. " .type 32;\n" +
  329. " .endef\n" +
  330. " .globl burst.initialize.externals\n" +
  331. " .p2align 4, 0x90";
  332. }