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.

121 lines
4.5 KiB

  1. // Copyright (c) 2016 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.Collections.Generic;
  19. using System.Linq;
  20. using ICSharpCode.Decompiler.FlowAnalysis;
  21. using ICSharpCode.Decompiler.TypeSystem;
  22. namespace ICSharpCode.Decompiler.IL.Transforms
  23. {
  24. /// <summary>
  25. /// Remove <c>HasInitialValue</c> from locals that are definitely assigned before every use
  26. /// (=the initial value is a dead store).
  27. ///
  28. /// In yield return generators, additionally removes dead 'V = null;' assignments.
  29. ///
  30. /// Additionally infers IType of stack slots that have StackType.Ref
  31. /// </summary>
  32. public class RemoveDeadVariableInit : IILTransform
  33. {
  34. public void Run(ILFunction function, ILTransformContext context)
  35. {
  36. ResetUsesInitialValueFlag(function, context);
  37. // Remove dead stores to variables that are never read from.
  38. // If the stored value has some side-effect, the value is unwrapped.
  39. // This is necessary to remove useless stores generated by some compilers, e.g., the F# compiler.
  40. // In yield return + async, the C# compiler tends to store null/default(T) to variables
  41. // when the variable goes out of scope.
  42. bool removeDeadStores = function.IsAsync || function.IsIterator || context.Settings.RemoveDeadStores;
  43. var variableQueue = new Queue<ILVariable>(function.Variables);
  44. while (variableQueue.Count > 0)
  45. {
  46. var v = variableQueue.Dequeue();
  47. if (v.Kind != VariableKind.Local && v.Kind != VariableKind.StackSlot)
  48. continue;
  49. if (!(v.RemoveIfRedundant || removeDeadStores))
  50. continue;
  51. // Skip variables that are captured in a mcs yield state-machine
  52. // loads of these will only be visible after DelegateConstruction step.
  53. if (function.StateMachineCompiledWithMono && v.StateMachineField != null)
  54. continue;
  55. if (v.LoadCount != 0 || v.AddressCount != 0)
  56. continue;
  57. foreach (var stloc in v.StoreInstructions.OfType<StLoc>().ToArray())
  58. {
  59. if (stloc.Parent is Block block)
  60. {
  61. context.Step($"Dead store to {v.Name}", stloc);
  62. if (SemanticHelper.IsPure(stloc.Value.Flags))
  63. {
  64. block.Instructions.Remove(stloc);
  65. }
  66. else
  67. {
  68. stloc.ReplaceWith(stloc.Value);
  69. }
  70. if (stloc.Value is LdLoc ldloc)
  71. {
  72. variableQueue.Enqueue(ldloc.Variable);
  73. }
  74. }
  75. }
  76. }
  77. // Try to infer IType of stack slots that are of StackType.Ref:
  78. foreach (var v in function.Variables)
  79. {
  80. if (v.Kind == VariableKind.StackSlot && v.StackType == StackType.Ref && v.AddressCount == 0)
  81. {
  82. IType newType = null;
  83. // Multiple store are possible in case of (c ? ref a : ref b) += 1, for example.
  84. foreach (var stloc in v.StoreInstructions.OfType<StLoc>())
  85. {
  86. var inferredType = stloc.Value.InferType(context.TypeSystem);
  87. // cancel, if types of values do not match exactly
  88. if (newType != null && !newType.Equals(inferredType))
  89. {
  90. newType = SpecialType.UnknownType;
  91. break;
  92. }
  93. newType = inferredType;
  94. }
  95. // Only overwrite existing type, if a "better" type was found.
  96. if (newType != null && newType != SpecialType.UnknownType)
  97. v.Type = newType;
  98. }
  99. }
  100. }
  101. internal static void ResetUsesInitialValueFlag(ILFunction function, ILTransformContext context)
  102. {
  103. var visitor = new DefiniteAssignmentVisitor(function, context.CancellationToken);
  104. function.AcceptVisitor(visitor);
  105. foreach (var v in function.Variables)
  106. {
  107. if (v.Kind != VariableKind.Parameter && !visitor.IsPotentiallyUsedUninitialized(v))
  108. {
  109. v.UsesInitialValue = false;
  110. }
  111. }
  112. }
  113. }
  114. }