@@ -54,7 +54,8 @@ void IStatementTransform.Run(Block block, int pos, StatementTransformContext con
5454 if ( context . Settings . IntroduceIncrementAndDecrement )
5555 {
5656 if ( TransformPostIncDecOperatorWithInlineStore ( block , pos )
57- || TransformPostIncDecOperator ( block , pos ) )
57+ || TransformPostIncDecOperator ( block , pos )
58+ || TransformPreIncDecOperatorWithInlineStore ( block , pos ) )
5859 {
5960 // again, new top-level stloc might need inlining:
6061 context . RequestRerun ( ) ;
@@ -844,6 +845,83 @@ bool IsDuplicatedAddressComputation(ILInstruction storeTarget, ILInstruction loa
844845 }
845846 }
846847
848+ /// <code>
849+ /// stloc l(stloc target(binary.add(ldloc target, ldc.i4 1)))
850+ /// </code>
851+ bool TransformPreIncDecOperatorWithInlineStore ( Block block , int pos )
852+ {
853+ var store = block . Instructions [ pos ] ;
854+ if ( ! IsCompoundStore ( store , out var targetType1 , out var value1 , context . TypeSystem ) )
855+ {
856+ return false ;
857+ }
858+ if ( ! IsCompoundStore ( value1 , out var targetType2 , out var value2 , context . TypeSystem ) )
859+ {
860+ return false ;
861+ }
862+ if ( targetType1 != targetType2 )
863+ return false ;
864+ var targetType = targetType1 ;
865+ var stloc_outer = store as StLoc ;
866+ var stloc_inner = value1 as StLoc ;
867+ LdLoc ldloc ;
868+ var binary = UnwrapSmallIntegerConv ( value2 , out var conv ) as BinaryNumericInstruction ;
869+ if ( binary != null && ( binary . Right . MatchLdcI ( 1 ) || binary . Right . MatchLdcF4 ( 1 ) || binary . Right . MatchLdcF8 ( 1 ) ) )
870+ {
871+ if ( ! ( binary . Operator == BinaryNumericOperator . Add || binary . Operator == BinaryNumericOperator . Sub ) )
872+ return false ;
873+
874+ if ( conv is not null )
875+ {
876+ var primitiveType = targetType . ToPrimitiveType ( ) ;
877+ if ( primitiveType . GetSize ( ) == conv . TargetType . GetSize ( ) && primitiveType . GetSign ( ) != conv . TargetType . GetSign ( ) )
878+ targetType = SwapSign ( targetType , context . TypeSystem ) ;
879+ }
880+
881+ if ( ! ValidateCompoundAssign ( binary , conv , targetType , context . Settings ) )
882+ return false ;
883+ ldloc = binary . Left as LdLoc ;
884+ }
885+ else if ( value2 is Call operatorCall && operatorCall . Method . IsOperator && operatorCall . Arguments . Count == 1 )
886+ {
887+ if ( ! ( operatorCall . Method . Name == "op_Increment" || operatorCall . Method . Name == "op_Decrement" ) )
888+ return false ;
889+ if ( operatorCall . IsLifted )
890+ return false ; // TODO: add tests and think about whether nullables need special considerations
891+ ldloc = operatorCall . Arguments [ 0 ] as LdLoc ;
892+ }
893+ else
894+ {
895+ return false ;
896+ }
897+ if ( stloc_outer == null )
898+ return false ;
899+ if ( stloc_inner == null )
900+ return false ;
901+ if ( ldloc == null )
902+ return false ;
903+ if ( ! ( stloc_outer . Variable . Kind == VariableKind . Local || stloc_outer . Variable . Kind == VariableKind . StackSlot ) )
904+ return false ;
905+ if ( ! IsMatchingCompoundLoad ( ldloc , stloc_inner , out var target , out var targetKind , out var finalizeMatch ) )
906+ return false ;
907+ if ( IsImplicitTruncation ( stloc_outer . Value , stloc_outer . Variable . Type , context . TypeSystem ) )
908+ return false ;
909+ context . Step ( nameof ( TransformPreIncDecOperatorWithInlineStore ) , store ) ;
910+ finalizeMatch ? . Invoke ( context ) ;
911+ if ( binary != null )
912+ {
913+ block . Instructions [ pos ] = new StLoc ( stloc_outer . Variable , new NumericCompoundAssign (
914+ binary , target , targetKind , binary . Right , targetType , CompoundEvalMode . EvaluatesToNewValue ) ) ;
915+ }
916+ else
917+ {
918+ Call operatorCall = ( Call ) value2 ;
919+ block . Instructions [ pos ] = new StLoc ( stloc_outer . Variable , new UserDefinedCompoundAssign (
920+ operatorCall . Method , CompoundEvalMode . EvaluatesToNewValue , target , targetKind , new LdcI4 ( 1 ) ) ) ;
921+ }
922+ return true ;
923+ }
924+
847925 /// <code>
848926 /// stobj(target, binary.add(stloc l(ldobj(target)), ldc.i4 1))
849927 /// where target is pure and does not use 'l', and the 'stloc l' does not truncate
0 commit comments