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.

299 lines
12 KiB

  1. // Copyright (c) 2015 Siegfried Pammer
  2. //
  3. // Permission is hereby granted, free of charge, to any person obtaining a copy of this
  4. // software and associated documentation files (the "Software"), to deal in the Software
  5. // without restriction, including without limitation the rights to use, copy, modify, merge,
  6. // publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
  7. // to whom the Software is furnished to do so, subject to the following conditions:
  8. //
  9. // The above copyright notice and this permission notice shall be included in all copies or
  10. // substantial portions of the Software.
  11. //
  12. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
  13. // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  14. // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
  15. // FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
  16. // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  17. // DEALINGS IN THE SOFTWARE.
  18. using System;
  19. using System.Collections.Generic;
  20. using System.Linq;
  21. using ICSharpCode.NRefactory.TypeSystem;
  22. using ICSharpCode.NRefactory.TypeSystem.Implementation;
  23. namespace ICSharpCode.Decompiler.IL.Transforms
  24. {
  25. /// <summary>
  26. /// Description of TransformAssignment.
  27. /// </summary>
  28. public class TransformAssignment : IILTransform
  29. {
  30. ILTransformContext context;
  31. void IILTransform.Run(ILFunction function, ILTransformContext context)
  32. {
  33. this.context = context;
  34. foreach (var block in function.Descendants.OfType<Block>()) {
  35. for (int i = block.Instructions.Count - 1; i >= 0; i--) {
  36. if (TransformPostIncDecOperatorOnAddress(block, i) || TransformPostIncDecOnStaticField(block, i) || TransformCSharp4PostIncDecOperatorOnAddress(block, i)) {
  37. block.Instructions.RemoveAt(i);
  38. continue;
  39. }
  40. if (InlineLdAddressUsages(block, i)) {
  41. block.Instructions.RemoveAt(i);
  42. continue;
  43. }
  44. if (TransformPostIncDecOperator(block, i, function)) {
  45. block.Instructions.RemoveAt(i);
  46. continue;
  47. }
  48. TransformInlineAssignmentStObj(block, i);
  49. }
  50. }
  51. }
  52. /// <code>
  53. /// stloc s(value)
  54. /// stloc l(ldloc s)
  55. /// stobj(..., ldloc s)
  56. /// -->
  57. /// stloc l(stobj (..., value))
  58. /// </code>
  59. /// -or-
  60. /// <code>
  61. /// stloc s(value)
  62. /// stobj (..., ldloc s)
  63. /// -->
  64. /// stloc s(stobj (..., value))
  65. /// </code>
  66. static void TransformInlineAssignmentStObj(Block block, int i)
  67. {
  68. var inst = block.Instructions[i] as StLoc;
  69. if (inst == null || inst.Variable.Kind != VariableKind.StackSlot)
  70. return;
  71. var nextInst = block.Instructions.ElementAtOrDefault(i + 1);
  72. ILInstruction value;
  73. StObj fieldStore;
  74. ILVariable local;
  75. if (nextInst is StLoc) { // instance fields
  76. var localStore = (StLoc)nextInst;
  77. fieldStore = block.Instructions.ElementAtOrDefault(i + 2) as StObj;
  78. if (fieldStore == null) { // otherwise it must local
  79. TransformInlineAssignmentLocal(block, i);
  80. return;
  81. }
  82. if (localStore.Variable.Kind == VariableKind.StackSlot || !localStore.Value.MatchLdLoc(inst.Variable) || !fieldStore.Value.MatchLdLoc(inst.Variable))
  83. return;
  84. value = inst.Value;
  85. local = localStore.Variable;
  86. block.Instructions.RemoveAt(i + 1);
  87. } else if (nextInst is StObj) { // static fields
  88. fieldStore = (StObj)nextInst;
  89. if (!fieldStore.Value.MatchLdLoc(inst.Variable))
  90. return;
  91. value = inst.Value;
  92. local = inst.Variable;
  93. } else return;
  94. block.Instructions.RemoveAt(i + 1);
  95. inst.ReplaceWith(new StLoc(local, new StObj(fieldStore.Target, value, fieldStore.Type)));
  96. }
  97. /// <code>
  98. /// stloc s(value)
  99. /// stloc l(ldloc s)
  100. /// -->
  101. /// stloc s(stloc l(value))
  102. /// </code>
  103. static void TransformInlineAssignmentLocal(Block block, int i)
  104. {
  105. var inst = block.Instructions[i] as StLoc;
  106. var nextInst = block.Instructions.ElementAtOrDefault(i + 1) as StLoc;
  107. if (inst == null || nextInst == null)
  108. return;
  109. if (nextInst.Variable.Kind == VariableKind.StackSlot || !nextInst.Value.MatchLdLoc(inst.Variable))
  110. return;
  111. var value = inst.Value;
  112. var var = nextInst.Variable;
  113. var stackVar = inst.Variable;
  114. block.Instructions.RemoveAt(i);
  115. nextInst.ReplaceWith(new StLoc(stackVar, new StLoc(var, value)));
  116. }
  117. /// ldaddress ::= ldelema | ldflda | ldsflda;
  118. /// <code>
  119. /// stloc s(ldaddress)
  120. /// usages of ldobj(ldloc s) or stobj(ldloc s, ...) in next instruction
  121. /// -->
  122. /// use ldaddress instead of ldloc s
  123. /// </code>
  124. static bool InlineLdAddressUsages(Block block, int i)
  125. {
  126. var inst = block.Instructions[i] as StLoc;
  127. if (inst == null || inst.Variable.Kind != VariableKind.StackSlot || !(inst.Value is LdElema || inst.Value is LdFlda || inst.Value is LdsFlda))
  128. return false;
  129. var valueToCopy = inst.Value;
  130. var nextInstruction = inst.Parent.Children.ElementAtOrDefault(inst.ChildIndex + 1);
  131. if (nextInstruction == null)
  132. return false;
  133. var affectedUsages = block.Descendants
  134. .OfType<IInstructionWithVariableOperand>()
  135. .Where(ins => ins != inst)
  136. .Where(ins => ins.Variable == inst.Variable)
  137. .Cast<ILInstruction>().ToArray();
  138. if (affectedUsages.Length == 0 || affectedUsages.Any(ins => !(ins.Parent is StObj || ins.Parent is LdObj)))
  139. return false;
  140. foreach (var usage in affectedUsages) {
  141. usage.ReplaceWith(valueToCopy.Clone());
  142. }
  143. return true;
  144. }
  145. /// <code>
  146. /// stloc s(ldloc l)
  147. /// stloc l(binary.op(ldloc s, ldc.i4 1))
  148. /// -->
  149. /// stloc s(block {
  150. /// stloc s2(ldloc l)
  151. /// stloc l(binary.op(ldloc s2, ldc.i4 1))
  152. /// final: ldloc s2
  153. /// })
  154. /// </code>
  155. static bool TransformPostIncDecOperator(Block block, int i, ILFunction function)
  156. {
  157. var inst = block.Instructions[i] as StLoc;
  158. var nextInst = block.Instructions.ElementAtOrDefault(i + 1) as StLoc;
  159. if (inst == null || nextInst == null)
  160. return false;
  161. var binary = nextInst.Value as BinaryNumericInstruction;
  162. if (inst.Variable.Kind != VariableKind.StackSlot || nextInst.Variable.Kind == VariableKind.StackSlot || binary == null)
  163. return false;
  164. if ((binary.Operator != BinaryNumericOperator.Add && binary.Operator != BinaryNumericOperator.Sub) || !binary.Left.MatchLdLoc(inst.Variable) || !binary.Right.MatchLdcI4(1))
  165. return false;
  166. var tempStore = function.RegisterVariable(VariableKind.StackSlot, inst.Variable.Type);
  167. var assignment = new Block(BlockType.CompoundOperator);
  168. assignment.Instructions.Add(new StLoc(tempStore, new LdLoc(nextInst.Variable)));
  169. assignment.Instructions.Add(new StLoc(nextInst.Variable, new BinaryNumericInstruction(binary.Operator, new LdLoc(tempStore), new LdcI4(1), binary.CheckForOverflow, binary.Sign)));
  170. assignment.FinalInstruction = new LdLoc(tempStore);
  171. nextInst.ReplaceWith(new StLoc(inst.Variable, assignment));
  172. return true;
  173. }
  174. /// ldaddress ::= ldelema | ldflda | ldsflda;
  175. /// <code>
  176. /// stloc s(ldaddress)
  177. /// stloc l(ldobj(ldloc s))
  178. /// stobj(ldloc s, binary.op(ldloc l, ldc.i4 1))
  179. /// -->
  180. /// stloc l(compound.op.old(ldobj(ldaddress), ldc.i4 1))
  181. /// </code>
  182. static bool TransformPostIncDecOperatorOnAddress(Block block, int i)
  183. {
  184. var inst = block.Instructions[i] as StLoc;
  185. var nextInst = block.Instructions.ElementAtOrDefault(i + 1) as StLoc;
  186. var stobj = block.Instructions.ElementAtOrDefault(i + 2) as StObj;
  187. if (inst == null || nextInst == null || stobj == null)
  188. return false;
  189. if (!(inst.Value is LdElema || inst.Value is LdFlda || inst.Value is LdsFlda))
  190. return false;
  191. ILInstruction target;
  192. IType targetType;
  193. if (nextInst.Variable.Kind == VariableKind.StackSlot || !nextInst.Value.MatchLdObj(out target, out targetType) || !target.MatchLdLoc(inst.Variable))
  194. return false;
  195. if (!stobj.Target.MatchLdLoc(inst.Variable))
  196. return false;
  197. var binary = stobj.Value as BinaryNumericInstruction;
  198. if (binary == null || !binary.Left.MatchLdLoc(nextInst.Variable) || !binary.Right.MatchLdcI4(1)
  199. || (binary.Operator != BinaryNumericOperator.Add && binary.Operator != BinaryNumericOperator.Sub))
  200. return false;
  201. var assignment = new CompoundAssignmentInstruction(binary.Operator, new LdObj(inst.Value, targetType), binary.Right, targetType, binary.CheckForOverflow, binary.Sign, CompoundAssignmentType.EvaluatesToOldValue);
  202. stobj.ReplaceWith(new StLoc(nextInst.Variable, assignment));
  203. block.Instructions.RemoveAt(i + 1);
  204. return true;
  205. }
  206. /// <code>
  207. /// stloc s(ldflda)
  208. /// stloc s2(ldobj(ldflda(ldloc s)))
  209. /// stloc l(ldloc s2)
  210. /// stobj (ldflda(ldloc s), binary.add(ldloc s2, ldc.i4 1))
  211. /// -->
  212. /// stloc l(compound.op.old(ldobj(ldflda(ldflda)), ldc.i4 1))
  213. /// </code>
  214. static bool TransformCSharp4PostIncDecOperatorOnAddress(Block block, int i)
  215. {
  216. var baseFieldAddress = block.Instructions[i] as StLoc;
  217. var fieldValue = block.Instructions.ElementAtOrDefault(i + 1) as StLoc;
  218. var fieldValueCopyToLocal = block.Instructions.ElementAtOrDefault(i + 2) as StLoc;
  219. var stobj = block.Instructions.ElementAtOrDefault(i + 3) as StObj;
  220. if (baseFieldAddress == null || fieldValue == null || fieldValueCopyToLocal == null || stobj == null)
  221. return false;
  222. if (baseFieldAddress.Variable.Kind != VariableKind.StackSlot || fieldValue.Variable.Kind != VariableKind.StackSlot || fieldValueCopyToLocal.Variable.Kind != VariableKind.Local)
  223. return false;
  224. IType t;
  225. IField targetField;
  226. ILInstruction targetFieldLoad, baseFieldAddressLoad2;
  227. if (!fieldValue.Value.MatchLdObj(out targetFieldLoad, out t))
  228. return false;
  229. ILInstruction baseAddress;
  230. if (baseFieldAddress.Value is LdFlda) {
  231. IField targetField2;
  232. ILInstruction baseFieldAddressLoad3;
  233. if (!targetFieldLoad.MatchLdFlda(out baseFieldAddressLoad2, out targetField) || !baseFieldAddressLoad2.MatchLdLoc(baseFieldAddress.Variable))
  234. return false;
  235. if (!stobj.Target.MatchLdFlda(out baseFieldAddressLoad3, out targetField2) || !baseFieldAddressLoad3.MatchLdLoc(baseFieldAddress.Variable) || !SameField(targetField, targetField2))
  236. return false;
  237. baseAddress = new LdFlda(baseFieldAddress.Value, targetField);
  238. } else if (baseFieldAddress.Value is LdElema) {
  239. if (!targetFieldLoad.MatchLdLoc(baseFieldAddress.Variable) || !stobj.Target.MatchLdLoc(baseFieldAddress.Variable))
  240. return false;
  241. baseAddress = baseFieldAddress.Value;
  242. } else {
  243. return false;
  244. }
  245. BinaryNumericInstruction binary = stobj.Value as BinaryNumericInstruction;
  246. if (binary == null || !binary.Left.MatchLdLoc(fieldValue.Variable) || !binary.Right.MatchLdcI4(1)
  247. || (binary.Operator != BinaryNumericOperator.Add && binary.Operator != BinaryNumericOperator.Sub))
  248. return false;
  249. var assignment = new CompoundAssignmentInstruction(binary.Operator, new LdObj(baseAddress, t), binary.Right, t, binary.CheckForOverflow, binary.Sign, CompoundAssignmentType.EvaluatesToOldValue);
  250. stobj.ReplaceWith(new StLoc(fieldValueCopyToLocal.Variable, assignment));
  251. block.Instructions.RemoveAt(i + 2);
  252. block.Instructions.RemoveAt(i + 1);
  253. return true;
  254. }
  255. /// <code>
  256. /// stloc s(ldobj(ldsflda))
  257. /// stobj (ldsflda, binary.op(ldloc s, ldc.i4 1))
  258. /// -->
  259. /// stloc s(compound.op.old(ldobj(ldsflda), ldc.i4 1))
  260. /// </code>
  261. static bool TransformPostIncDecOnStaticField(Block block, int i)
  262. {
  263. var inst = block.Instructions[i] as StLoc;
  264. var stobj = block.Instructions.ElementAtOrDefault(i + 1) as StObj;
  265. if (inst == null || stobj == null)
  266. return false;
  267. ILInstruction target;
  268. IType type;
  269. IField field, field2;
  270. if (inst.Variable.Kind != VariableKind.StackSlot || !inst.Value.MatchLdObj(out target, out type) || !target.MatchLdsFlda(out field))
  271. return false;
  272. if (!stobj.Target.MatchLdsFlda(out field2) || !SameField(field, field2))
  273. return false;
  274. var binary = stobj.Value as BinaryNumericInstruction;
  275. if (binary == null || !binary.Left.MatchLdLoc(inst.Variable) || !binary.Right.MatchLdcI4(1))
  276. return false;
  277. var assignment = new CompoundAssignmentInstruction(binary.Operator, inst.Value, binary.Right, type, binary.CheckForOverflow, binary.Sign, CompoundAssignmentType.EvaluatesToOldValue);
  278. stobj.ReplaceWith(new StLoc(inst.Variable, assignment));
  279. return true;
  280. }
  281. static bool SameField(IField a, IField b)
  282. {
  283. a = (IField)a.MemberDefinition;
  284. b = (IField)b.MemberDefinition;
  285. return a.Equals(b);
  286. }
  287. }
  288. }