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.

184 lines
6.6 KiB

  1. // Copyright (c) 2019 Daniel Grunwald
  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.Diagnostics;
  21. using System.Linq;
  22. using ICSharpCode.Decompiler.TypeSystem;
  23. namespace ICSharpCode.Decompiler.IL.Transforms
  24. {
  25. /// <summary>
  26. /// Context object for the ILInstruction.Extract() operation.
  27. /// </summary>
  28. class ExtractionContext
  29. {
  30. /// <summary>
  31. /// Nearest function, used for registering the new locals that are created by extraction.
  32. /// </summary>
  33. readonly ILFunction Function;
  34. readonly ILTransformContext context;
  35. /// <summary>
  36. /// Combined flags of all instructions being moved.
  37. /// </summary>
  38. internal InstructionFlags FlagsBeingMoved;
  39. /// <summary>
  40. /// List of actions to be executed when performing the extraction.
  41. ///
  42. /// Each function in this list has the side-effect of replacing the instruction-to-be-moved
  43. /// with a load of a fresh temporary variable; and returns the the store to the temporary variable,
  44. /// which will be inserted at block-level.
  45. /// </summary>
  46. readonly List<Func<ILInstruction>> MoveActions = new List<Func<ILInstruction>>();
  47. ExtractionContext(ILFunction function, ILTransformContext context)
  48. {
  49. Debug.Assert(function != null);
  50. this.Function = function;
  51. this.context = context;
  52. }
  53. internal void RegisterMove(ILInstruction predecessor)
  54. {
  55. FlagsBeingMoved |= predecessor.Flags;
  56. MoveActions.Add(delegate {
  57. var type = context.TypeSystem.FindType(predecessor.ResultType);
  58. var v = Function.RegisterVariable(VariableKind.StackSlot, type);
  59. predecessor.ReplaceWith(new LdLoc(v));
  60. return new StLoc(v, predecessor);
  61. });
  62. }
  63. internal void RegisterMoveIfNecessary(ILInstruction predecessor)
  64. {
  65. if (!CanReorderWithInstructionsBeingMoved(predecessor))
  66. {
  67. RegisterMove(predecessor);
  68. }
  69. }
  70. /// <summary>
  71. /// Currently, <c>predecessor</c> is evaluated before the instructions being moved.
  72. /// If this function returns true, <c>predecessor</c> can stay as-is, despite the move changing the evaluation order.
  73. /// If this function returns false, <c>predecessor</c> will need to also move, to ensure the evaluation order stays unchanged.
  74. /// </summary>
  75. public bool CanReorderWithInstructionsBeingMoved(ILInstruction predecessor)
  76. {
  77. // We could track the instructions being moved and be smarter about unnecessary moves,
  78. // but given the limited scenarios where extraction is used so far,
  79. // this seems unnecessary.
  80. return predecessor.Flags == InstructionFlags.None;
  81. }
  82. /// <summary>
  83. /// Extracts the specified instruction:
  84. /// The instruction is replaced with a load of a new temporary variable;
  85. /// and the instruction is moved to a store to said variable at block-level.
  86. ///
  87. /// May return null if extraction is not possible.
  88. /// </summary>
  89. public static ILVariable Extract(ILInstruction instToExtract, ILTransformContext context)
  90. {
  91. var function = instToExtract.Ancestors.OfType<ILFunction>().First();
  92. ExtractionContext ctx = new ExtractionContext(function, context);
  93. ctx.FlagsBeingMoved = instToExtract.Flags;
  94. ILInstruction inst = instToExtract;
  95. while (inst != null)
  96. {
  97. if (inst.Parent is IfInstruction ifInst && inst.SlotInfo != IfInstruction.ConditionSlot)
  98. {
  99. // this context doesn't support extraction, but maybe we can create a block here?
  100. if (ifInst.ResultType == StackType.Void)
  101. {
  102. Block newBlock = new Block();
  103. inst.ReplaceWith(newBlock);
  104. newBlock.Instructions.Add(inst);
  105. }
  106. }
  107. if (inst.Parent is Block { Kind: BlockKind.ControlFlow } block)
  108. {
  109. // We've reached a target block, and extraction is possible all the way.
  110. // Check if the parent BlockContainer allows extraction:
  111. if (block.Parent is BlockContainer container)
  112. {
  113. switch (container.Kind)
  114. {
  115. case ContainerKind.Normal:
  116. case ContainerKind.Loop:
  117. // extraction is always possible
  118. break;
  119. case ContainerKind.Switch:
  120. // extraction is possible, unless in the entry-point (i.e., the switch head)
  121. if (block == container.EntryPoint && inst.ChildIndex == 0)
  122. {
  123. // try to extract to the container's parent block, if it's a valid location
  124. inst = container;
  125. continue;
  126. }
  127. break;
  128. case ContainerKind.While:
  129. // extraction is possible, unless in the entry-point (i.e., the condition block)
  130. if (block == container.EntryPoint)
  131. {
  132. return null;
  133. }
  134. break;
  135. case ContainerKind.DoWhile:
  136. // extraction is possible, unless in the last block (i.e., the condition block)
  137. if (block == container.Blocks.Last())
  138. {
  139. return null;
  140. }
  141. break;
  142. case ContainerKind.For:
  143. // extraction is possible, unless in the first or last block
  144. // (i.e., the condition block or increment block)
  145. if (block == container.EntryPoint
  146. || block == container.Blocks.Last())
  147. {
  148. return null;
  149. }
  150. break;
  151. }
  152. }
  153. int insertIndex = inst.ChildIndex;
  154. var type = context.TypeSystem.FindType(instToExtract.ResultType);
  155. // Move instToExtract itself:
  156. var v = function.RegisterVariable(VariableKind.StackSlot, type);
  157. instToExtract.ReplaceWith(new LdLoc(v));
  158. block.Instructions.Insert(insertIndex, new StLoc(v, instToExtract));
  159. // Apply the other move actions:
  160. foreach (var moveAction in ctx.MoveActions)
  161. {
  162. block.Instructions.Insert(insertIndex, moveAction());
  163. }
  164. return v;
  165. }
  166. if (!inst.Parent.PrepareExtract(inst.ChildIndex, ctx))
  167. return null;
  168. inst = inst.Parent;
  169. }
  170. return null;
  171. }
  172. }
  173. }