internal static bool IsProtectedIfInst(IfInstruction ifInst) { // We exclude logic.and to avoid turning // "logic.and(comp(interfaces != ldnull), call get_Count(interfaces))" // into "if ((interfaces?.Count ?? 0) != 0)". return((ifInst.MatchLogicAnd(out _, out _) || ifInst.MatchLogicOr(out _, out _)) && IfInstruction.IsInConditionSlot(ifInst)); }
protected internal override void VisitComp(Comp inst) { // "logic.not(arg)" is sugar for "comp(arg != ldc.i4 0)" if (inst.MatchLogicNot(out var arg)) { VisitLogicNot(inst, arg); return; } else if (inst.Kind == ComparisonKind.Inequality && inst.LiftingKind == ComparisonLiftingKind.None && inst.Right.MatchLdcI4(0) && (IfInstruction.IsInConditionSlot(inst) || inst.Left is Comp) ) { // if (comp(x != 0)) ==> if (x) // comp(comp(...) != 0) => comp(...) context.Step("Remove redundant comp(... != 0)", inst); inst.Left.AddILRange(inst); inst.ReplaceWith(inst.Left); inst.Left.AcceptVisitor(this); return; } base.VisitComp(inst); if (inst.IsLifted) { return; } if (inst.Right.MatchLdNull()) { if (inst.Kind == ComparisonKind.GreaterThan) { context.Step("comp(left > ldnull) => comp(left != ldnull)", inst); inst.Kind = ComparisonKind.Inequality; } else if (inst.Kind == ComparisonKind.LessThanOrEqual) { context.Step("comp(left <= ldnull) => comp(left == ldnull)", inst); inst.Kind = ComparisonKind.Equality; } } else if (inst.Left.MatchLdNull()) { if (inst.Kind == ComparisonKind.LessThan) { context.Step("comp(ldnull < right) => comp(ldnull != right)", inst); inst.Kind = ComparisonKind.Inequality; } else if (inst.Kind == ComparisonKind.GreaterThanOrEqual) { context.Step("comp(ldnull >= right) => comp(ldnull == right)", inst); inst.Kind = ComparisonKind.Equality; } } var rightWithoutConv = inst.Right.UnwrapConv(ConversionKind.SignExtend).UnwrapConv(ConversionKind.ZeroExtend); if (rightWithoutConv.MatchLdcI4(0) && inst.Sign == Sign.Unsigned && (inst.Kind == ComparisonKind.GreaterThan || inst.Kind == ComparisonKind.LessThanOrEqual)) { if (inst.Kind == ComparisonKind.GreaterThan) { context.Step("comp.unsigned(left > ldc.i4 0) => comp(left != ldc.i4 0)", inst); inst.Kind = ComparisonKind.Inequality; VisitComp(inst); return; } else if (inst.Kind == ComparisonKind.LessThanOrEqual) { context.Step("comp.unsigned(left <= ldc.i4 0) => comp(left == ldc.i4 0)", inst); inst.Kind = ComparisonKind.Equality; VisitComp(inst); return; } } else if (rightWithoutConv.MatchLdcI4(0) && inst.Kind.IsEqualityOrInequality()) { if (inst.Left.MatchLdLen(StackType.I, out ILInstruction array)) { // comp.unsigned(ldlen array == conv i4->i(ldc.i4 0)) // => comp(ldlen.i4 array == ldc.i4 0) // This is a special case where the C# compiler doesn't generate conv.i4 after ldlen. context.Step("comp(ldlen.i4 array == ldc.i4 0)", inst); inst.InputType = StackType.I4; inst.Left.ReplaceWith(new LdLen(StackType.I4, array).WithILRange(inst.Left)); inst.Right = rightWithoutConv; } else if (inst.Left is Conv conv && conv.TargetType == PrimitiveType.I && conv.Argument.ResultType == StackType.O) { // C++/CLI sometimes uses this weird comparison with null: context.Step("comp(conv o->i (ldloc obj) == conv i4->i <sign extend>(ldc.i4 0))", inst); // -> comp(ldloc obj == ldnull) inst.InputType = StackType.O; inst.Left = conv.Argument; inst.Right = new LdNull().WithILRange(inst.Right); inst.Right.AddILRange(rightWithoutConv); } } if (inst.Right.MatchLdNull() && inst.Left.MatchBox(out arg, out var type) && type.Kind == TypeKind.TypeParameter) { if (inst.Kind == ComparisonKind.Equality) { context.Step("comp(box T(..) == ldnull) -> comp(.. == ldnull)", inst); inst.Left = arg; } if (inst.Kind == ComparisonKind.Inequality) { context.Step("comp(box T(..) != ldnull) -> comp(.. != ldnull)", inst); inst.Left = arg; } } }