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.

226 lines
7.1 KiB

  1. // Copyright (c) 2017 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.Linq;
  19. using ICSharpCode.Decompiler.TypeSystem;
  20. namespace ICSharpCode.Decompiler.IL.Transforms
  21. {
  22. public class EarlyExpressionTransforms : ILVisitor, IILTransform
  23. {
  24. ILTransformContext context;
  25. public void Run(ILFunction function, ILTransformContext context)
  26. {
  27. this.context = context;
  28. Default(function);
  29. }
  30. protected override void Default(ILInstruction inst)
  31. {
  32. foreach (var child in inst.Children)
  33. {
  34. child.AcceptVisitor(this);
  35. }
  36. }
  37. protected internal override void VisitComp(Comp inst)
  38. {
  39. base.VisitComp(inst);
  40. FixComparisonKindLdNull(inst, context);
  41. }
  42. internal static void FixComparisonKindLdNull(Comp inst, ILTransformContext context)
  43. {
  44. if (inst.IsLifted)
  45. {
  46. return;
  47. }
  48. if (inst.Right.MatchLdNull())
  49. {
  50. if (inst.Kind == ComparisonKind.GreaterThan)
  51. {
  52. context.Step("comp(left > ldnull) => comp(left != ldnull)", inst);
  53. inst.Kind = ComparisonKind.Inequality;
  54. }
  55. else if (inst.Kind == ComparisonKind.LessThanOrEqual)
  56. {
  57. context.Step("comp(left <= ldnull) => comp(left == ldnull)", inst);
  58. inst.Kind = ComparisonKind.Equality;
  59. }
  60. }
  61. else if (inst.Left.MatchLdNull())
  62. {
  63. if (inst.Kind == ComparisonKind.LessThan)
  64. {
  65. context.Step("comp(ldnull < right) => comp(ldnull != right)", inst);
  66. inst.Kind = ComparisonKind.Inequality;
  67. }
  68. else if (inst.Kind == ComparisonKind.GreaterThanOrEqual)
  69. {
  70. context.Step("comp(ldnull >= right) => comp(ldnull == right)", inst);
  71. inst.Kind = ComparisonKind.Equality;
  72. }
  73. }
  74. if (inst.Right.MatchLdNull() && inst.Left.MatchBox(out var arg, out var type) && type.Kind == TypeKind.TypeParameter)
  75. {
  76. if (inst.Kind == ComparisonKind.Equality)
  77. {
  78. context.Step("comp(box T(..) == ldnull) -> comp(.. == ldnull)", inst);
  79. inst.Left = arg;
  80. }
  81. if (inst.Kind == ComparisonKind.Inequality)
  82. {
  83. context.Step("comp(box T(..) != ldnull) -> comp(.. != ldnull)", inst);
  84. inst.Left = arg;
  85. }
  86. }
  87. }
  88. protected internal override void VisitStObj(StObj inst)
  89. {
  90. base.VisitStObj(inst);
  91. StObjToStLoc(inst, context);
  92. }
  93. // This transform is required because ILInlining only works with stloc/ldloc
  94. internal static bool StObjToStLoc(StObj inst, ILTransformContext context)
  95. {
  96. if (inst.Target.MatchLdLoca(out ILVariable v)
  97. && TypeUtils.IsCompatibleTypeForMemoryAccess(v.Type, inst.Type)
  98. && inst.UnalignedPrefix == 0
  99. && !inst.IsVolatile)
  100. {
  101. context.Step($"stobj(ldloca {v.Name}, ...) => stloc {v.Name}(...)", inst);
  102. ILInstruction replacement = new StLoc(v, inst.Value).WithILRange(inst);
  103. if (v.StackType == StackType.Unknown && inst.Type.Kind != TypeKind.Unknown
  104. && inst.SlotInfo != Block.InstructionSlot)
  105. {
  106. replacement = new Conv(replacement, inst.Type.ToPrimitiveType(),
  107. checkForOverflow: false, Sign.None);
  108. }
  109. inst.ReplaceWith(replacement);
  110. return true;
  111. }
  112. return false;
  113. }
  114. protected internal override void VisitLdObj(LdObj inst)
  115. {
  116. base.VisitLdObj(inst);
  117. AddressOfLdLocToLdLoca(inst, context);
  118. LdObjToLdLoc(inst, context);
  119. }
  120. internal static bool LdObjToLdLoc(LdObj inst, ILTransformContext context)
  121. {
  122. if (inst.Target.MatchLdLoca(out ILVariable v)
  123. && TypeUtils.IsCompatibleTypeForMemoryAccess(v.Type, inst.Type)
  124. && inst.UnalignedPrefix == 0
  125. && !inst.IsVolatile)
  126. {
  127. context.Step($"ldobj(ldloca {v.Name}) => ldloc {v.Name}", inst);
  128. ILInstruction replacement = new LdLoc(v).WithILRange(inst);
  129. if (v.StackType == StackType.Unknown && inst.Type.Kind != TypeKind.Unknown)
  130. {
  131. replacement = new Conv(replacement, inst.Type.ToPrimitiveType(),
  132. checkForOverflow: false, Sign.None);
  133. }
  134. inst.ReplaceWith(replacement);
  135. return true;
  136. }
  137. return false;
  138. }
  139. internal static void AddressOfLdLocToLdLoca(LdObj inst, ILTransformContext context)
  140. {
  141. // ldobj(...(addressof(ldloc V))) where ... can be zero or more ldflda instructions
  142. // =>
  143. // ldobj(...(ldloca V))
  144. var temp = inst.Target;
  145. var range = temp.ILRanges;
  146. while (temp.MatchLdFlda(out var ldfldaTarget, out _))
  147. {
  148. temp = ldfldaTarget;
  149. range = range.Concat(temp.ILRanges);
  150. }
  151. if (temp.MatchAddressOf(out var addressOfTarget, out _) && addressOfTarget.MatchLdLoc(out var v))
  152. {
  153. context.Step($"ldobj(...(addressof(ldloca {v.Name}))) => ldobj(...(ldloca {v.Name}))", inst);
  154. var replacement = new LdLoca(v).WithILRange(addressOfTarget);
  155. foreach (var r in range)
  156. {
  157. replacement = replacement.WithILRange(r);
  158. }
  159. temp.ReplaceWith(replacement);
  160. }
  161. }
  162. protected internal override void VisitNewObj(NewObj inst)
  163. {
  164. if (TransformDecimalCtorToConstant(inst, out LdcDecimal decimalConstant))
  165. {
  166. context.Step("TransformDecimalCtorToConstant", inst);
  167. inst.ReplaceWith(decimalConstant);
  168. return;
  169. }
  170. base.VisitNewObj(inst);
  171. }
  172. bool TransformDecimalCtorToConstant(NewObj inst, out LdcDecimal result)
  173. {
  174. IType t = inst.Method.DeclaringType;
  175. result = null;
  176. if (!t.IsKnownType(KnownTypeCode.Decimal))
  177. return false;
  178. var args = inst.Arguments;
  179. if (args.Count == 1)
  180. {
  181. long val;
  182. if (args[0].MatchLdcI(out val))
  183. {
  184. var paramType = inst.Method.Parameters[0].Type.GetDefinition()?.KnownTypeCode;
  185. result = paramType switch {
  186. KnownTypeCode.Int32 => new LdcDecimal(new decimal(unchecked((int)val))),
  187. KnownTypeCode.UInt32 => new LdcDecimal(new decimal(unchecked((uint)val))),
  188. KnownTypeCode.Int64 => new LdcDecimal(new decimal(val)),
  189. KnownTypeCode.UInt64 => new LdcDecimal(new decimal(unchecked((ulong)val))),
  190. _ => null
  191. };
  192. return result is not null;
  193. }
  194. }
  195. else if (args.Count == 5)
  196. {
  197. int lo, mid, hi, isNegative, scale;
  198. if (args[0].MatchLdcI4(out lo) && args[1].MatchLdcI4(out mid) &&
  199. args[2].MatchLdcI4(out hi) && args[3].MatchLdcI4(out isNegative) &&
  200. args[4].MatchLdcI4(out scale) && unchecked((byte)scale) <= 28)
  201. {
  202. result = new LdcDecimal(new decimal(lo, mid, hi, isNegative != 0, unchecked((byte)scale)));
  203. return true;
  204. }
  205. }
  206. return false;
  207. }
  208. }
  209. }