Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

System.InvalidProgramException : The JIT compiler encountered invalid IL code or an internal limitation. #419

Closed
michaelgrosner opened this issue Jul 13, 2024 · 3 comments
Assignees
Labels
Milestone

Comments

@michaelgrosner
Copy link

michaelgrosner commented Jul 13, 2024

Hello,

I've been trying out FastExpressionCompiler in order to replace my usages of Expression.Compile. I am running into a situation that I can't figure out why FEC breaks while Expression.Compile handles the expression.

In pseudo-code, I am trying to get a RefFunc<T, bool> which is "Obj.X > 2 * Obj?.Nested?.Y". "X" and "Y" are both nullable doubles, and "Obj" and "Nested" are classes with other fields.

This is what ToExpressionString returns for me:

var p = new ParameterExpression[1]; // the parameter expressions
var e = new Expression[18]; // the unique expressions
var expr = MakeBinary(ExpressionType.GreaterThan,
  e[0]=Condition(
    e[1]=MakeBinary(ExpressionType.Equal,
      p[0]=Parameter(typeof(Test.Obj).MakeByRefType(), "Obj"),
      e[2]=Constant(null)),
    e[3]=Constant(null, typeof(double?)),
    e[4]=Property(
      p[0 // (Test.Obj Obj)
        ],
      typeof(Test.Obj).GetTypeInfo().GetDeclaredProperty("X")),
    typeof(double?)),
  e[5]=MakeBinary(ExpressionType.Multiply,
    e[6]=Convert(
      e[7]=Constant(2),
      typeof(double?)),
    e[8]=Condition(
      e[9]=MakeBinary(ExpressionType.Equal,
        e[10]=Condition(
          e[11]=MakeBinary(ExpressionType.Equal,
            p[0 // (Test.Obj Obj)
              ],
            e[12]=Constant(null)),
          e[13]=Constant(null, typeof(Test.Nested)),
          e[14]=Property(
            p[0 // (Test.Obj Obj)
              ],
            typeof(Test.Obj).GetTypeInfo().GetDeclaredProperty("Nested")),
          typeof(Test.Nested)),
        e[15]=Constant(null)),
      e[16]=Constant(null, typeof(double?)),
      e[17]=Property(
        e[10 // Conditional of Test.Nested
          ],
        typeof(Test.Nested).GetTypeInfo().GetDeclaredProperty("Y")),
      typeof(double?)),
    liftToNull: true,
    null));

I think it's probably due to the nested null checks in "Obj?.Nested?.Y". But the standard Expression.Compile handles this just fine. Do you have any insight into how to fix this, either in this library or in my code? I have seen this issue when running on 4.2.1 and 4.0.0 on .NET 6.

@dadhi
Copy link
Owner

dadhi commented Jul 14, 2024

@michaelgrosner Could you provide the missing Obj and Nested types so that I can compile and test this expression?
You may look into the last tests in the IssueTests project to see what I need.

@michaelgrosner
Copy link
Author

michaelgrosner commented Jul 15, 2024

Sure, here's something that reproduces the issue:

using System.Linq.Expressions;
using System.Reflection;
using FastExpressionCompiler;
using static System.Linq.Expressions.Expression;

public class Obj(double? x, Nested? nested)
{
    public double? X { get; } = x;
    public Nested? Nested { get; } = nested;
}

public class Nested(double? y)
{
    public double? Y { get; } = y;
}

public delegate TOut RefFunc<TIn, TOut>(in TIn t);

public class Program
{
    public static void Main()
    {
        var p = new ParameterExpression[1]; // the parameter expressions
        var e = new Expression[19]; // the unique expressions
        var expr = Lambda<RefFunc<Obj, bool>>(
          e[0] = MakeBinary(ExpressionType.GreaterThan,
            e[1] = Condition(
              e[2] = MakeBinary(ExpressionType.Equal,
                p[0] = Parameter(typeof(Obj).MakeByRefType(), "p"),
                e[3] = Constant(null)),
              e[4] = Constant(null, typeof(double?)),
              e[5] = Property(
                p[0 // (Obj p)
                  ],
                typeof(Obj).GetTypeInfo().GetDeclaredProperty("X")),
              typeof(double?)),
            e[6] = MakeBinary(ExpressionType.Multiply,
              e[7] = Convert(
                e[8] = Constant(2),
                typeof(double?)),
              e[9] = Condition(
                e[10] = MakeBinary(ExpressionType.Equal,
                  e[11] = Condition(
                    e[12] = MakeBinary(ExpressionType.Equal,
                      p[0 // (Obj p)
                        ],
                      e[13] = Constant(null)),
                    e[14] = Constant(null, typeof(Nested)),
                    e[15] = Property(
                      p[0 // (Obj p)
                        ],
                      typeof(Obj).GetTypeInfo().GetDeclaredProperty("Nested")),
                    typeof(Nested)),
                  e[16] = Constant(null)),
                e[17] = Constant(null, typeof(double?)),
                e[18] = Property(
                  e[11 // Conditional of Nested
                    ],
                  typeof(Nested).GetTypeInfo().GetDeclaredProperty("Y")),
                typeof(double?)),
              liftToNull: true,
              null)),
          p[0 // (Obj p)
            ]);
        var compiled = expr.CompileFast();

        // Unhandled exception. System.InvalidProgramException: The JIT compiler encountered invalid IL code or an internal limitation.
        compiled(new Obj(5, new Nested(6)));
    }
}

I was also able to reproduce it this way, too:

    public class Obj(double? a, Nested nested)
    {
        public double? A { get; } = a;
        public Nested? Nested { get; } = nested;
    }

    public class Nested(double? b)
    {
        public double? B { get; } = b;
    }

    [Test]
    public void Issue419()
    {
        Expression<Func<Obj, bool>> expression = d => 
            (d == null ? null : d.A) > (double?)3 * ((d == null ? null : d.Nested) == null ? null : d.Nested.B);
        Console.WriteLine(expression.Body.ToExpressionString());
        var regular = expression.Compile();
        var fast = expression.CompileFast();
        var data = new Obj(10, new Nested(5));
        Console.WriteLine(regular(data));
        Console.WriteLine(fast(data)); // also throws System.InvalidProgramException
    }

@dadhi dadhi self-assigned this Jul 18, 2024
@dadhi dadhi added the bug label Jul 18, 2024
@dadhi dadhi added this to the v4.2.2 milestone Jul 18, 2024
@dadhi
Copy link
Owner

dadhi commented Oct 12, 2024

@michaelgrosner Thank you for the refined test, adding to the queue to fix.

dadhi added a commit that referenced this issue Oct 12, 2024
@dadhi dadhi closed this as completed in 706de20 Oct 13, 2024
dadhi added a commit that referenced this issue Oct 13, 2024
dadhi added a commit that referenced this issue Oct 13, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants