protected internal override void VisitBinaryNumericInstruction(BinaryNumericInstruction inst) { base.VisitBinaryNumericInstruction(inst); switch (inst.Operator) { case BinaryNumericOperator.ShiftLeft: case BinaryNumericOperator.ShiftRight: if (inst.Right.MatchBinaryNumericInstruction(BinaryNumericOperator.BitAnd, out var lhs, out var rhs) && rhs.MatchLdcI4(inst.ResultType == StackType.I8 ? 63 : 31)) { // a << (b & 31) => a << b context.Step("Combine bit.and into shift", inst); inst.Right = lhs; } break; case BinaryNumericOperator.BitAnd: if (inst.Left.InferType(context.TypeSystem).IsKnownType(KnownTypeCode.Boolean) && inst.Right.InferType(context.TypeSystem).IsKnownType(KnownTypeCode.Boolean)) { if (new NullableLiftingTransform(context).Run(inst)) { // e.g. "(a.GetValueOrDefault() == b.GetValueOrDefault()) & (a.HasValue & b.HasValue)" } else if (SemanticHelper.IsPure(inst.Right.Flags)) { context.Step("Replace bit.and with logic.and", inst); var expr = IfInstruction.LogicAnd(inst.Left, inst.Right); inst.ReplaceWith(expr); expr.AcceptVisitor(this); } } break; } }
protected internal override void VisitBinaryNumericInstruction(BinaryNumericInstruction inst) { base.VisitBinaryNumericInstruction(inst); switch (inst.Operator) { case BinaryNumericOperator.ShiftLeft: case BinaryNumericOperator.ShiftRight: if (inst.Right.MatchBinaryNumericInstruction(BinaryNumericOperator.BitAnd, out var lhs, out var rhs) && rhs.MatchLdcI4(inst.ResultType == StackType.I8 ? 63 : 31)) { // a << (b & 31) => a << b context.Step("Combine bit.and into shift", inst); inst.Right = lhs; } break; case BinaryNumericOperator.BitAnd: if (IsBoolean(inst.Left) && IsBoolean(inst.Right) && SemanticHelper.IsPure(inst.Right.Flags)) { context.Step("Replace bit.and with logic.and", inst); var expr = IfInstruction.LogicAnd(inst.Left, inst.Right); inst.ReplaceWith(expr); expr.AcceptVisitor(this); } break; } }
/// <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); }
/// <summary> /// VS2017.8 / Roslyn 2.9 started optimizing some cases of /// "a.GetValueOrDefault() == b.GetValueOrDefault() && (a.HasValue & b.HasValue)" /// to /// "(a.GetValueOrDefault() == b.GetValueOrDefault()) & (a.HasValue & b.HasValue)" /// so this secondary entry point analyses logic.and as-if it was a short-circuiting &&. /// </summary> public bool Run(BinaryNumericInstruction bni) { Debug.Assert(!bni.IsLifted && bni.Operator == BinaryNumericOperator.BitAnd); // caller ensures that bni.Left/bni.Right are booleans var lifted = Lift(bni, bni.Left, bni.Right, new LdcI4(0)); if (lifted != null) { bni.ReplaceWith(lifted); return(true); } return(false); }