/LSOOptimizer.cs

http://lsotool.googlecode.com/ · C# · 387 lines · 293 code · 26 blank · 68 comment · 49 complexity · 421a84a29590252adf86181f186857be MD5 · raw file

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. namespace LSO
  5. {
  6. static class LSOOptimizer
  7. {
  8. /// <summary>
  9. /// Mini-assembler just for sequences of instructions
  10. /// </summary>
  11. /// <param name="instructions"></param>
  12. /// <returns></returns>
  13. public static byte[] AssembleInstructions(List<LSO.Instruction> instructions)
  14. {
  15. int length = 0;
  16. foreach (LSO.Instruction instruction in instructions)
  17. length += instruction.Size;
  18. byte[] b = new byte[length];
  19. int offset = 0;
  20. byte[] buffer;
  21. foreach (LSO.Instruction instruction in instructions)
  22. {
  23. buffer = instruction.ToBytes();
  24. Buffer.BlockCopy(buffer, 0, b, offset, buffer.Length);
  25. offset += buffer.Length;
  26. }
  27. return b;
  28. }
  29. /// <summary>
  30. /// Compares two instructions including their arguments.
  31. /// Not sure why this is necessary
  32. /// </summary>
  33. public static bool SameInstruction(LSO.Instruction first, LSO.Instruction second)
  34. {
  35. if (first.Info.Opcode != second.Info.Opcode)
  36. return false;
  37. if (first.Info.NumberOfArgs != second.Info.NumberOfArgs)
  38. return false;
  39. int argcount = first.Info.NumberOfArgs;
  40. for (int i = 0; i < argcount; i++)
  41. {
  42. if ((first.Arguments[i].Equals(second.Arguments[i])) == false)
  43. return false;
  44. }
  45. return true;
  46. }
  47. /// <summary>
  48. /// Returns true if no jumps land within the defined region of instructions
  49. /// (Greater than offset, less than offset + length)
  50. /// </summary>
  51. public static bool OkayToReplace(List<LSO.Instruction> haystack, int offset, int length)
  52. {
  53. List<int> Offsets = new List<int>();
  54. int byteoffset = 0;
  55. for (int i = 0; i < haystack.Count; i++)
  56. {
  57. Offsets.Add(byteoffset);
  58. byteoffset += haystack[i].Size;
  59. }
  60. int bytestart = Offsets[offset];
  61. int byteend = Offsets[offset + length];
  62. LSO.Instruction instruction;
  63. int jump;
  64. for (int i = 0; i < haystack.Count; i++)
  65. {
  66. instruction = haystack[i];
  67. jump = 0;
  68. if (instruction.Info.Official == "JUMP")
  69. jump = Convert.ToInt32(instruction.Arguments[0]) + instruction.Size;
  70. else if (instruction.Info.Official == "JUMPIF" || instruction.Info.Official == "JUMPNIF")
  71. jump = Convert.ToInt32(instruction.Arguments[1]) + instruction.Size;
  72. if (jump != 0)
  73. {
  74. jump = Offsets[i] + jump;
  75. if (jump > bytestart && jump < byteend)
  76. return false;
  77. }
  78. }
  79. return true;
  80. }
  81. public static int SizeDifference(List<LSO.Instruction> from, List<LSO.Instruction> to)
  82. {
  83. int fromsize = 0;
  84. foreach (LSO.Instruction inst in from)
  85. fromsize += inst.Size;
  86. int tosize = 0;
  87. foreach (LSO.Instruction inst in to)
  88. tosize += inst.Size;
  89. return fromsize - tosize;
  90. }
  91. private static List<LSO.Instruction> RecalculateJumps(List<LSO.Instruction> instructions, int median, int bytedifference)
  92. {
  93. List<int> Offsets = new List<int>();
  94. int byteoffset = 0;
  95. for (int i = 0; i < instructions.Count; i++)
  96. {
  97. Offsets.Add(byteoffset);
  98. byteoffset += instructions[i].Size;
  99. }
  100. int bytemedian = Offsets[median];
  101. LSO.Instruction instruction;
  102. int jump;
  103. for (int i = 0; i < instructions.Count; i++)
  104. {
  105. jump = 0;
  106. instruction = instructions[i];
  107. if (instruction.Info.Official == "JUMP")
  108. jump = Convert.ToInt32(instruction.Arguments[0]) + instruction.Size;
  109. else if (instruction.Info.Official == "JUMPIF" || instruction.Info.Official == "JUMPNIF")
  110. jump = Convert.ToInt32(instruction.Arguments[1]) + instruction.Size;
  111. if (jump != 0)
  112. {
  113. jump = Offsets[i] + jump;
  114. if (Offsets[i] < bytemedian)
  115. {
  116. if (jump >= bytemedian)
  117. jump -= bytedifference;
  118. }
  119. else
  120. {
  121. if (jump < bytemedian)
  122. jump += bytedifference;
  123. }
  124. jump = jump - Offsets[i] - instruction.Size;
  125. if (instruction.Info.Official == "JUMP")
  126. instruction.Arguments[0] = jump;
  127. else
  128. instruction.Arguments[1] = jump;
  129. }
  130. }
  131. return instructions;
  132. }
  133. public static class FindAndReplaceOptimizer
  134. {
  135. private static Dictionary<List<LSO.Instruction>, List<LSO.Instruction>> LookupTable = new Dictionary<List<LSO.Instruction>, List<LSO.Instruction>>();
  136. private static int ListFind(List<LSO.Instruction> haystack, List<LSO.Instruction> needle, int offset)
  137. {
  138. int si;
  139. int next = offset;
  140. for (int di = offset; di < haystack.Count; di++)
  141. {
  142. si = 0;
  143. di = next;
  144. next = di + 1;
  145. while (SameInstruction(haystack[di], needle[si]))
  146. {
  147. si++;
  148. di++;
  149. if (si == needle.Count)
  150. return next - 1;
  151. if (di == haystack.Count)
  152. break;
  153. }
  154. }
  155. return -1;
  156. }
  157. private static List<LSO.Instruction> ListReplace(List<LSO.Instruction> haystack, int replacedcount, List<LSO.Instruction> needle, int offset)
  158. {
  159. List<LSO.Instruction> output = new List<LSO.Instruction>();
  160. for (int i = 0; i < offset; i++)
  161. output.Add(haystack[i]);
  162. foreach(LSO.Instruction inst in needle)
  163. output.Add(inst);
  164. for(int i = offset + replacedcount; i < haystack.Count; i++)
  165. {
  166. output.Add(haystack[i]);
  167. }
  168. return output;
  169. }
  170. private static void LUTAdd(string find, string replace)
  171. {
  172. List<LSO.Instruction> findinst = LSODisassembler.Disassemble(Helpers.Hex2Bytes(find));
  173. List<LSO.Instruction> replaceinst = LSODisassembler.Disassemble(Helpers.Hex2Bytes(replace));
  174. LookupTable.Add(findinst, replaceinst);
  175. }
  176. static FindAndReplaceOptimizer()
  177. {
  178. #region new
  179. // pushargi 1 add integer, integer -> bitnot neg integer
  180. // thx Strife
  181. LUTAdd("5e000000017011", "818001");
  182. // pushargi 1 sub integer, integer -> neg integer bitnot
  183. // thx Strife
  184. LUTAdd("5e000000017111", "800181");
  185. #endregion
  186. #region POPARG *
  187. // poparg 0 -> nothing
  188. LUTAdd("0600000000", "");
  189. // poparg 4 -> pop
  190. LUTAdd("0600000004", "01");
  191. // poparg 8 -> pop pop
  192. LUTAdd("0600000008", "0101");
  193. // poparg 12 -> popv
  194. LUTAdd("060000000c", "04");
  195. // poparg 16 -> popq
  196. LUTAdd("0600000010", "05");
  197. // poparg 20 -> popq pop
  198. LUTAdd("0600000014", "0501");
  199. // poparg 24 -> popv popv
  200. LUTAdd("0600000018", "0404");
  201. // poparg 28 -> popq popv
  202. LUTAdd("060000001c", "0504");
  203. // poparg 32 -> popq popq
  204. LUTAdd("0600000020", "0505");
  205. // poparg 36 -> popv popv popv
  206. LUTAdd("0600000024", "040404");
  207. // poparg 40 -> popq popv popv
  208. LUTAdd("0600000028", "050404");
  209. // poparg 44 -> popq popq popv
  210. LUTAdd("060000002c", "050504");
  211. // poparg 48 -> popq popq popq
  212. LUTAdd("0600000030", "050505");
  213. // poparg 52 -> popq popq popq pop
  214. LUTAdd("0600000034", "05050501");
  215. // poparg 56 -> popq popq popv popv
  216. LUTAdd("0600000038", "05050404");
  217. // poparg 60 -> popq popq popq popv
  218. LUTAdd("060000003c", "05050504");
  219. // poparg 64 -> popq popq popq popq
  220. LUTAdd("0600000040", "05050505");
  221. #endregion
  222. #region POP POP POP ...
  223. // pop pop pop pop -> popq
  224. LUTAdd("01010101", "05");
  225. // pop pop pop -> popv
  226. LUTAdd("010101", "04");
  227. #endregion
  228. #region PUSHARGE *
  229. // pusharge 0 -> nothing
  230. LUTAdd("6600000000", "");
  231. // pusharge 4 -> pushe
  232. LUTAdd("6600000004", "63");
  233. // pusharge 8 -> pushe pushe
  234. LUTAdd("6600000008", "6363");
  235. // pusharge 12 -> pushev
  236. LUTAdd("660000000c", "64");
  237. // pusharge 16 -> pusheq
  238. LUTAdd("6600000010", "65");
  239. // pusharge 20 -> pusheq pushe
  240. LUTAdd("6600000014", "6563");
  241. // pusharge 24 -> pushev pushev
  242. LUTAdd("6600000018", "6464");
  243. // pusharge 28 -> pusheq pushev
  244. LUTAdd("660000001c", "6564");
  245. // pusharge 32 -> pusheq pusheq
  246. LUTAdd("6600000020", "6565");
  247. // pusharge 36 -> pushev pushev pushev
  248. LUTAdd("6600000024", "646464");
  249. // pusharge 40 -> pusheq pushev pushev
  250. LUTAdd("6600000028", "656464");
  251. // pusharge 44 -> pusheq pusheq pushev
  252. LUTAdd("660000002c", "656564");
  253. // pusharge 48 -> pusheq pusheq pusheq
  254. LUTAdd("6600000030", "656565");
  255. // pusharge 52 -> pusheq pushev pushev pushev
  256. LUTAdd("6600000034", "65646464");
  257. // pusharge 56 -> pusheq pusheq pushev pushev
  258. LUTAdd("6600000038", "65656464");
  259. // pusharge 60 -> pusheq pusheq pusheq pushev
  260. LUTAdd("660000003c", "65656564");
  261. // pusharge 64 -> pusheq pusheq pusheq pusheq
  262. LUTAdd("6600000040", "65656565");
  263. #endregion
  264. #region PUSHARG* 0
  265. // pushargi 0 -> pushe
  266. LUTAdd("5e00000000", "63");
  267. // pushargf 0
  268. LUTAdd("5f00000000", "63");
  269. // pushargv <0, 0, 0> -> pushev
  270. LUTAdd("61000000000000000000000000", "64");
  271. // pushargq <0, 0, 0, 0> -> pusheq
  272. LUTAdd("6200000000000000000000000000000000", "65");
  273. #endregion
  274. #region ADD/SUB 0
  275. // pushe add integer, integer -> nothing
  276. LUTAdd("637011", "");
  277. // pushe sub integer, integer -> nothing
  278. LUTAdd("637111", "");
  279. #endregion
  280. #region These are smaller, but there's a chance they may be slower
  281. // pushargi 1 -> pushe boolnot
  282. LUTAdd("5E00000001", "6382");
  283. // pushargi -1 -> pushe bitnot
  284. LUTAdd("5EFFFFFFFF", "6381");
  285. #endregion
  286. #region PUSHE PUSHE PUSHE ...
  287. // pushe pushe pushe pushe -> pusheq
  288. LUTAdd("63636363", "65");
  289. // pushe pushe pushe -> pushev
  290. LUTAdd("636363", "64");
  291. #endregion
  292. #region ideas
  293. // push 0 push 4 push 8 -> pushv 0 ?
  294. // pushargi 2 -> pushe boolnot dup shl ?
  295. // callib_two_byte 0x00** -> calllib 0x** ?
  296. // push(g) x push(g) x pushargi 1 add/sub int, int store(g) x pop pop
  297. // -> push(g) x pushargi 1 add/sub int, int store(g) x pop (Strife)
  298. #endregion
  299. }
  300. public static List<LSO.Instruction> Optimize(List<LSO.Instruction> instructions)
  301. {
  302. int offset;
  303. foreach (KeyValuePair<List<LSO.Instruction>, List<LSO.Instruction>> kvp in LookupTable)
  304. {
  305. offset = 0;
  306. do
  307. {
  308. offset = ListFind(instructions, kvp.Key, offset);
  309. if (offset != -1)
  310. {
  311. if (OkayToReplace(instructions, offset, kvp.Key.Count))
  312. {
  313. instructions = ListReplace(instructions, kvp.Key.Count, kvp.Value, offset);
  314. instructions = RecalculateJumps(instructions, offset + kvp.Value.Count, SizeDifference(kvp.Key, kvp.Value));
  315. }
  316. else
  317. offset++;
  318. }
  319. } while (offset != -1);
  320. }
  321. return instructions;
  322. }
  323. public static LSOAssembly PerformOptimizations(LSOAssembly assembly)
  324. {
  325. List<LSO.Instruction> instructions;
  326. foreach (LSOAssembly.FunctionCodeChunk chunk in assembly.FunctionCodeChunks)
  327. {
  328. instructions = LSODisassembler.Disassemble(chunk.Code);
  329. instructions = Optimize(instructions);
  330. chunk.Code = AssembleInstructions(instructions);
  331. }
  332. foreach (LSOAssembly.EventHandlerCodeChunk chunk in assembly.EventHandlerCodeChunks)
  333. {
  334. instructions = LSODisassembler.Disassemble(chunk.Code);
  335. instructions = Optimize(instructions);
  336. chunk.Code = AssembleInstructions(instructions);
  337. }
  338. return assembly;
  339. }
  340. }
  341. public static class HeaderStringOptimizer
  342. {
  343. public static LSOAssembly PerformOptimizations(LSOAssembly assembly)
  344. {
  345. foreach (LSOAssembly.StaticBlock block in assembly.StaticBlocks)
  346. block.Header.StringData = "";
  347. foreach (LSOAssembly.StateBlock block in assembly.StateBlocks)
  348. block.Header.StringData = "";
  349. foreach (LSOAssembly.EventHandlerCodeChunk block in assembly.EventHandlerCodeChunks)
  350. block.Header.StringData = "";
  351. return assembly;
  352. }
  353. }
  354. public static LSOAssembly PerformAllOptimizations(LSOAssembly assembly)
  355. {
  356. assembly = FindAndReplaceOptimizer.PerformOptimizations(assembly);
  357. assembly = HeaderStringOptimizer.PerformOptimizations(assembly);
  358. return assembly;
  359. }
  360. }
  361. }