diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs index b133ac0787..34f940d861 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs @@ -4946,5 +4946,21 @@ public void Issue1779(int value) CustomStruct2 customStruct = GetStruct(); customStruct.IntProp += value; } + + public static string PreincrementWithMethodCall(int value) + { + return (++value).ToString(); + } + +#if CS72 + public static string PreincrementWithInParameter(int value) + { + PreincrementWithInParameter_Helper(++value); + return value.ToString(); + } + public static void PreincrementWithInParameter_Helper(in int value) + { + } +#endif } } diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs index b5530fb70d..1b6b1b44f0 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs @@ -54,7 +54,8 @@ void IStatementTransform.Run(Block block, int pos, StatementTransformContext con if (context.Settings.IntroduceIncrementAndDecrement) { if (TransformPostIncDecOperatorWithInlineStore(block, pos) - || TransformPostIncDecOperator(block, pos)) + || TransformPostIncDecOperator(block, pos) + || TransformPreIncDecOperatorWithInlineStore(block, pos)) { // again, new top-level stloc might need inlining: context.RequestRerun(); @@ -844,6 +845,83 @@ bool IsDuplicatedAddressComputation(ILInstruction storeTarget, ILInstruction loa } } + /// + /// stloc l(stloc target(binary.add(ldloc target, ldc.i4 1))) + /// + bool TransformPreIncDecOperatorWithInlineStore(Block block, int pos) + { + var store = block.Instructions[pos]; + if (!IsCompoundStore(store, out var targetType1, out var value1, context.TypeSystem)) + { + return false; + } + if (!IsCompoundStore(value1, out var targetType2, out var value2, context.TypeSystem)) + { + return false; + } + if (targetType1 != targetType2) + return false; + var targetType = targetType1; + var stloc_outer = store as StLoc; + var stloc_inner = value1 as StLoc; + LdLoc ldloc; + var binary = UnwrapSmallIntegerConv(value2, out var conv) as BinaryNumericInstruction; + if (binary != null && (binary.Right.MatchLdcI(1) || binary.Right.MatchLdcF4(1) || binary.Right.MatchLdcF8(1))) + { + if (!(binary.Operator == BinaryNumericOperator.Add || binary.Operator == BinaryNumericOperator.Sub)) + return false; + + if (conv is not null) + { + var primitiveType = targetType.ToPrimitiveType(); + if (primitiveType.GetSize() == conv.TargetType.GetSize() && primitiveType.GetSign() != conv.TargetType.GetSign()) + targetType = SwapSign(targetType, context.TypeSystem); + } + + if (!ValidateCompoundAssign(binary, conv, targetType, context.Settings)) + return false; + ldloc = binary.Left as LdLoc; + } + else if (value2 is Call operatorCall && operatorCall.Method.IsOperator && operatorCall.Arguments.Count == 1) + { + if (!(operatorCall.Method.Name == "op_Increment" || operatorCall.Method.Name == "op_Decrement")) + return false; + if (operatorCall.IsLifted) + return false; // TODO: add tests and think about whether nullables need special considerations + ldloc = operatorCall.Arguments[0] as LdLoc; + } + else + { + return false; + } + if (stloc_outer == null) + return false; + if (stloc_inner == null) + return false; + if (ldloc == null) + return false; + if (!(stloc_outer.Variable.Kind == VariableKind.Local || stloc_outer.Variable.Kind == VariableKind.StackSlot)) + return false; + if (!IsMatchingCompoundLoad(ldloc, stloc_inner, out var target, out var targetKind, out var finalizeMatch)) + return false; + if (IsImplicitTruncation(stloc_outer.Value, stloc_outer.Variable.Type, context.TypeSystem)) + return false; + context.Step(nameof(TransformPreIncDecOperatorWithInlineStore), store); + finalizeMatch?.Invoke(context); + if (binary != null) + { + block.Instructions[pos] = new StLoc(stloc_outer.Variable, new NumericCompoundAssign( + binary, target, targetKind, binary.Right, targetType, CompoundEvalMode.EvaluatesToNewValue)); + } + else + { + Call operatorCall = (Call)value2; + block.Instructions[pos] = new StLoc(stloc_outer.Variable, new UserDefinedCompoundAssign( + operatorCall.Method, CompoundEvalMode.EvaluatesToNewValue, target, targetKind, new LdcI4(1))); + } + return true; + } + /// /// stobj(target, binary.add(stloc l(ldobj(target)), ldc.i4 1)) /// where target is pure and does not use 'l', and the 'stloc l' does not truncate