/// <code> /// stloc s(ldobj(ldsflda)) /// stobj (ldsflda, binary.op(ldloc s, ldc.i4 1)) /// --> /// stloc s(compound.op.old(ldobj(ldsflda), ldc.i4 1)) /// </code> bool TransformPostIncDecOnStaticField(Block block, int i) { var inst = block.Instructions[i] as StLoc; var stobj = block.Instructions.ElementAtOrDefault(i + 1) as StObj; if (inst == null || stobj == null) { return(false); } ILInstruction target; IType type; IField field, field2; if (inst.Variable.Kind != VariableKind.StackSlot || !inst.Value.MatchLdObj(out target, out type) || !target.MatchLdsFlda(out field)) { return(false); } if (!stobj.Target.MatchLdsFlda(out field2) || !IsSameMember(field, field2)) { return(false); } var binary = stobj.Value as BinaryNumericInstruction; if (binary == null || !binary.Left.MatchLdLoc(inst.Variable) || !binary.Right.MatchLdcI4(1)) { return(false); } context.Step($"TransformPostIncDecOnStaticField", inst); var assignment = new CompoundAssignmentInstruction(binary, inst.Value, binary.Right, type, CompoundAssignmentType.EvaluatesToOldValue); stobj.ReplaceWith(new StLoc(inst.Variable, assignment)); return(true); }
/// <summary> /// Transform compound assignments where the return value is not being used, /// or where there's an inlined assignment within the setter call. /// </summary> /// <remarks> /// Called by ExpressionTransforms. /// </remarks> internal static bool HandleCallCompoundAssign(CallInstruction setterCall, StatementTransformContext context) { // callvirt set_Property(ldloc S_1, binary.op(callvirt get_Property(ldloc S_1), value)) // ==> compound.op.new(callvirt get_Property(ldloc S_1), value) var setterValue = setterCall.Arguments.LastOrDefault(); var storeInSetter = setterValue as StLoc; if (storeInSetter != null) { // callvirt set_Property(ldloc S_1, stloc v(binary.op(callvirt get_Property(ldloc S_1), value))) // ==> stloc v(compound.op.new(callvirt get_Property(ldloc S_1), value)) setterValue = storeInSetter.Value; } setterValue = UnwrapSmallIntegerConv(setterValue, out var conv); if (!(setterValue is BinaryNumericInstruction binary)) { return(false); } var getterCall = binary.Left as CallInstruction; if (!MatchingGetterAndSetterCalls(getterCall, setterCall)) { return(false); } IType targetType = getterCall.Method.ReturnType; if (!ValidateCompoundAssign(binary, conv, targetType)) { return(false); } if (storeInSetter != null && storeInSetter.Variable.Type.IsSmallIntegerType()) { // 'stloc v' implicitly truncates. // Ensure that type of 'v' must match type of the property: if (storeInSetter.Variable.Type.GetSize() != targetType.GetSize()) { return(false); } if (storeInSetter.Variable.Type.GetSign() != targetType.GetSign()) { return(false); } } context.Step($"Compound assignment to '{getterCall.Method.AccessorOwner.Name}'", setterCall); ILInstruction newInst = new CompoundAssignmentInstruction( binary, getterCall, binary.Right, getterCall.Method.ReturnType, CompoundAssignmentType.EvaluatesToNewValue); if (storeInSetter != null) { storeInSetter.Value = newInst; newInst = storeInSetter; context.RequestRerun(); // moving stloc to top-level might trigger inlining } setterCall.ReplaceWith(newInst); return(true); }
static bool ValidateCompoundAssign(BinaryNumericInstruction binary, Conv conv, IType targetType) { if (!CompoundAssignmentInstruction.IsBinaryCompatibleWithType(binary, targetType)) { return(false); } if (conv != null && !(conv.TargetType == targetType.ToPrimitiveType() && conv.CheckForOverflow == binary.CheckForOverflow)) { return(false); // conv does not match binary operation } return(true); }
/// <code> /// stloc s(binary(callvirt(getter), value)) /// callvirt (setter, ldloc s) /// (followed by single usage of s in next instruction) /// --> /// stloc s(compound.op.new(callvirt(getter), value)) /// </code> bool TransformInlineCompoundAssignmentCall(Block block, int i) { var mainStLoc = block.Instructions[i] as StLoc; // in some cases it can be a compiler-generated local if (mainStLoc == null || (mainStLoc.Variable.Kind != VariableKind.StackSlot && mainStLoc.Variable.Kind != VariableKind.Local)) { return(false); } BinaryNumericInstruction binary = mainStLoc.Value as BinaryNumericInstruction; ILVariable localVariable = mainStLoc.Variable; if (!localVariable.IsSingleDefinition) { return(false); } if (localVariable.LoadCount != 2) { return(false); } var getterCall = binary?.Left as CallInstruction; var setterCall = block.Instructions.ElementAtOrDefault(i + 1) as CallInstruction; if (!MatchingGetterAndSetterCalls(getterCall, setterCall)) { return(false); } if (!setterCall.Arguments.Last().MatchLdLoc(localVariable)) { return(false); } var next = block.Instructions.ElementAtOrDefault(i + 2); if (next == null) { return(false); } if (next.Descendants.Where(d => d.MatchLdLoc(localVariable)).Count() != 1) { return(false); } if (!CompoundAssignmentInstruction.IsBinaryCompatibleWithType(binary, getterCall.Method.ReturnType)) { return(false); } context.Step($"Inline compound assignment to '{getterCall.Method.AccessorOwner.Name}'", setterCall); block.Instructions.RemoveAt(i + 1); // remove setter call binary.ReplaceWith(new CompoundAssignmentInstruction( binary, getterCall, binary.Right, getterCall.Method.ReturnType, CompoundAssignmentType.EvaluatesToNewValue)); return(true); }
/// ldaddress ::= ldelema | ldflda | ldsflda; /// <code> /// stloc s(ldaddress) /// stloc l(ldobj(ldloc s)) /// stobj(ldloc s, binary.op(ldloc l, ldc.i4 1)) /// --> /// stloc l(compound.op.old(ldobj(ldaddress), ldc.i4 1)) /// </code> bool TransformPostIncDecOperatorOnAddress(Block block, int i) { var inst = block.Instructions[i] as StLoc; var nextInst = block.Instructions.ElementAtOrDefault(i + 1) as StLoc; var stobj = block.Instructions.ElementAtOrDefault(i + 2) as StObj; if (inst == null || nextInst == null || stobj == null) { return(false); } if (!(inst.Value is LdElema || inst.Value is LdFlda || inst.Value is LdsFlda)) { return(false); } ILInstruction target; IType targetType; if (nextInst.Variable.Kind == VariableKind.StackSlot || !nextInst.Value.MatchLdObj(out target, out targetType) || !target.MatchLdLoc(inst.Variable)) { return(false); } if (!stobj.Target.MatchLdLoc(inst.Variable)) { return(false); } var binary = stobj.Value as BinaryNumericInstruction; if (binary == null || !binary.Left.MatchLdLoc(nextInst.Variable) || !binary.Right.MatchLdcI4(1) || (binary.Operator != BinaryNumericOperator.Add && binary.Operator != BinaryNumericOperator.Sub)) { return(false); } if (binary.IsLifted) { return(false); } context.Step($"TransformPostIncDecOperator", inst); var assignment = new CompoundAssignmentInstruction(binary.Operator, new LdObj(inst.Value, targetType), binary.Right, targetType, binary.CheckForOverflow, binary.Sign, CompoundAssignmentType.EvaluatesToOldValue); stobj.ReplaceWith(new StLoc(nextInst.Variable, assignment)); block.Instructions.RemoveAt(i + 1); return(true); }
/// <summary> /// Transform compound assignments where the return value is not being used, /// or where there's an inlined assignment within the setter call. /// </summary> /// <remarks> /// Called by ExpressionTransforms. /// </remarks> internal static bool HandleCallCompoundAssign(CallInstruction setterCall, StatementTransformContext context) { // callvirt set_Property(ldloc S_1, binary.op(callvirt get_Property(ldloc S_1), value)) // ==> compound.op.new(callvirt(callvirt get_Property(ldloc S_1)), value) var setterValue = setterCall.Arguments.LastOrDefault(); var storeInSetter = setterValue as StLoc; if (storeInSetter != null) { // callvirt set_Property(ldloc S_1, stloc v(binary.op(callvirt get_Property(ldloc S_1), value))) // ==> stloc v(compound.op.new(callvirt(callvirt get_Property(ldloc S_1)), value)) setterValue = storeInSetter.Value; } if (!(setterValue is BinaryNumericInstruction binary)) { return(false); } var getterCall = binary.Left as CallInstruction; if (!MatchingGetterAndSetterCalls(getterCall, setterCall)) { return(false); } if (!CompoundAssignmentInstruction.IsBinaryCompatibleWithType(binary, getterCall.Method.ReturnType)) { return(false); } context.Step($"Compound assignment to '{getterCall.Method.AccessorOwner.Name}'", setterCall); ILInstruction newInst = new CompoundAssignmentInstruction( binary, getterCall, binary.Right, getterCall.Method.ReturnType, CompoundAssignmentType.EvaluatesToNewValue); if (storeInSetter != null) { storeInSetter.Value = newInst; newInst = storeInSetter; context.RequestRerun(); // moving stloc to top-level might trigger inlining } setterCall.ReplaceWith(newInst); return(true); }
protected internal override void VisitStObj(StObj inst) { base.VisitStObj(inst); if (EarlyExpressionTransforms.StObjToStLoc(inst, context)) { context.RequestRerun(); return; } if (inst.Value is BinaryNumericInstruction binary && binary.Left.MatchLdObj(out ILInstruction target, out IType t) && inst.Target.Match(target).Success && SemanticHelper.IsPure(target.Flags) && CompoundAssignmentInstruction.IsBinaryCompatibleWithType(binary, t)) { context.Step("compound assignment", inst); // stobj(target, binary.op(ldobj(target), ...)) // => compound.op(target, ...) inst.ReplaceWith(new CompoundAssignmentInstruction( binary, binary.Left, binary.Right, t, CompoundAssignmentType.EvaluatesToNewValue)); } }
/// <summary> /// Roslyn compound assignment that's not inline within another instruction. /// </summary> bool TransformRoslynCompoundAssignmentCall(Block block, int i) { // stloc variable(callvirt get_Property(ldloc obj)) // callvirt set_Property(ldloc obj, binary.op(ldloc variable, ldc.i4 1)) // => compound.op.new(callvirt get_Property(ldloc obj), ldc.i4 1) if (!(block.Instructions[i] is StLoc stloc)) { return(false); } if (!(stloc.Variable.IsSingleDefinition && stloc.Variable.LoadCount == 1)) { return(false); } var getterCall = stloc.Value as CallInstruction; var setterCall = block.Instructions[i + 1] as CallInstruction; if (!(MatchingGetterAndSetterCalls(getterCall, setterCall))) { return(false); } var binary = setterCall.Arguments.Last() as BinaryNumericInstruction; if (binary == null || !binary.Left.MatchLdLoc(stloc.Variable)) { return(false); } if (!CompoundAssignmentInstruction.IsBinaryCompatibleWithType(binary, getterCall.Method.ReturnType)) { return(false); } context.Step($"Compound assignment to '{getterCall.Method.AccessorOwner.Name}'", setterCall); block.Instructions.RemoveAt(i + 1); // remove setter call stloc.ReplaceWith(new CompoundAssignmentInstruction( binary, getterCall, binary.Right, getterCall.Method.ReturnType, CompoundAssignmentType.EvaluatesToNewValue)); return(true); }
/// <code> /// stloc s(ldflda) /// stloc s2(ldobj(ldflda(ldloc s))) /// stloc l(ldloc s2) /// stobj (ldflda(ldloc s), binary.add(ldloc s2, ldc.i4 1)) /// --> /// stloc l(compound.op.old(ldobj(ldflda(ldflda)), ldc.i4 1)) /// </code> bool TransformCSharp4PostIncDecOperatorOnAddress(Block block, int i) { var baseFieldAddress = block.Instructions[i] as StLoc; var fieldValue = block.Instructions.ElementAtOrDefault(i + 1) as StLoc; var fieldValueCopyToLocal = block.Instructions.ElementAtOrDefault(i + 2) as StLoc; var stobj = block.Instructions.ElementAtOrDefault(i + 3) as StObj; if (baseFieldAddress == null || fieldValue == null || fieldValueCopyToLocal == null || stobj == null) { return(false); } if (baseFieldAddress.Variable.Kind != VariableKind.StackSlot || fieldValue.Variable.Kind != VariableKind.StackSlot || fieldValueCopyToLocal.Variable.Kind != VariableKind.Local) { return(false); } IType t; IField targetField; ILInstruction targetFieldLoad, baseFieldAddressLoad2; if (!fieldValue.Value.MatchLdObj(out targetFieldLoad, out t)) { return(false); } ILInstruction baseAddress; if (baseFieldAddress.Value is LdFlda) { IField targetField2; ILInstruction baseFieldAddressLoad3; if (!targetFieldLoad.MatchLdFlda(out baseFieldAddressLoad2, out targetField) || !baseFieldAddressLoad2.MatchLdLoc(baseFieldAddress.Variable)) { return(false); } if (!stobj.Target.MatchLdFlda(out baseFieldAddressLoad3, out targetField2) || !baseFieldAddressLoad3.MatchLdLoc(baseFieldAddress.Variable) || !IsSameMember(targetField, targetField2)) { return(false); } baseAddress = new LdFlda(baseFieldAddress.Value, targetField); } else if (baseFieldAddress.Value is LdElema) { if (!targetFieldLoad.MatchLdLoc(baseFieldAddress.Variable) || !stobj.Target.MatchLdLoc(baseFieldAddress.Variable)) { return(false); } baseAddress = baseFieldAddress.Value; } else { return(false); } BinaryNumericInstruction binary = stobj.Value as BinaryNumericInstruction; if (binary == null || !binary.Left.MatchLdLoc(fieldValue.Variable) || !binary.Right.MatchLdcI4(1) || (binary.Operator != BinaryNumericOperator.Add && binary.Operator != BinaryNumericOperator.Sub)) { return(false); } context.Step($"TransformCSharp4PostIncDecOperatorOnAddress", baseFieldAddress); var assignment = new CompoundAssignmentInstruction(binary, new LdObj(baseAddress, t), binary.Right, t, CompoundAssignmentType.EvaluatesToOldValue); stobj.ReplaceWith(new StLoc(fieldValueCopyToLocal.Variable, assignment)); block.Instructions.RemoveAt(i + 2); block.Instructions.RemoveAt(i + 1); return(true); }