コード例 #1
0
 /// <summary>
 /// Creates a new expression visitor to rewrite expressions for JIT compilation
 /// support.
 /// </summary>
 /// <param name="analysis">Scope analysis of nodes in the tree.</param>
 /// <param name="methodTable">Method table parameter passed to the top-level lambda.</param>
 /// <param name="thunkFactory">Factory for thunk types.</param>
 public Impl(Dictionary <object, Scope> analysis, ParameterExpression methodTable, IThunkFactory thunkFactory)
 {
     _analysis     = analysis;
     _methodTable  = methodTable;
     _thunkFactory = thunkFactory;
     _scope        = new CompilerScope(_methodTable);
 }
コード例 #2
0
        private static void AssertThunkType(IThunkFactory factory, Type delegateType, Dictionary <Type, object[]> values)
        {
            //
            // Get the delegate's Invoke method and infer parameter and return types.
            //
            var invokeMethod = delegateType.GetMethod("Invoke");

            var retType = invokeMethod.ReturnType;
            var ret     = retType != typeof(void) ? values[retType][0] : null;

            var parameterTypes = invokeMethod.GetParameters().Select(p => p.ParameterType).ToArray();
            var args           = parameterTypes.Select(p => values[p][0]).ToArray();

            //
            // Use a special closure type to assert behavior.
            //
            var closureType = typeof(AssertiveClosure);
            var closureAdd  = closureType.GetMethod("Add");
            var closureFlag = closureType.GetMethod("Flag");

            //
            // This is the functionality we're testing.
            //
            var t = factory.GetThunkType(delegateType, closureType);

            //
            // Assert we got only one constructor.
            //
            var ctors = t.GetConstructors();

            Assert.AreEqual(1, ctors.Length);

            var ctor       = ctors[0];
            var ctorParams = ctor.GetParameters();

            Assert.AreEqual(1, ctorParams.Length);

            //
            // Obtain the expression tree type passed to the constructor.
            //
            var exprType = ctorParams[0].ParameterType;

            Assert.IsTrue(typeof(LambdaExpression).IsAssignableFrom(exprType));
            Assert.IsTrue(exprType.IsGenericType);
            Assert.AreEqual(typeof(Expression <>), exprType.GetGenericTypeDefinition());
            var innerDelegateType = exprType.GetGenericArguments()[0];

            //
            // Build an expression tree of the required type, using the "assertive closure"
            // to record all the parameters passed in.
            //
            var closureParam = Expression.Parameter(closureType);
            var valueParams = parameterTypes.Select(p => Expression.Parameter(p)).ToArray();
            var lambdaParams = new[] { closureParam }.Concat(valueParams).ToArray();

            var exprs = new List <Expression>();

            foreach (var valueParam in valueParams)
            {
                exprs.Add(Expression.Call(closureParam, closureAdd, Expression.Convert(valueParam, typeof(object))));
            }

            if (retType != typeof(void))
            {
                exprs.Add(Expression.Constant(ret, retType));
            }
            else
            {
                exprs.Add(Expression.Constant(value: null));
            }

            var e =
                Expression.Lambda(
                    innerDelegateType,
                    Expression.Block(
                        retType,
                        exprs
                        ),
                    lambdaParams
                    );

            //
            // Instantiate the thunk type.
            //
            var o     = Activator.CreateInstance(t, e);
            var thunk = (Thunk)o;

            //
            // Ensure we can obtain the original expression at runtime.
            //
            Assert.AreSame(e, thunk.Lambda);

            //
            // Check the Target field and get its original value.
            //
            var target = t.GetField("Target");

            Assert.IsNotNull(target);
            var jitCompiler = (Delegate)target.GetValue(o);

            //
            // Assert the shape of the CreateDelegate method.
            //
            var createDelegate = t.GetMethod("CreateDelegate");

            Assert.IsNotNull(createDelegate);
            Assert.IsFalse(createDelegate.IsStatic);
            Assert.IsTrue(createDelegate.IsPublic);
            Assert.AreEqual(delegateType, createDelegate.ReturnType);
            var createDelegateParams = createDelegate.GetParameters();

            Assert.AreEqual(1, createDelegateParams.Length);
            Assert.AreEqual(closureType, createDelegateParams[0].ParameterType);

            //
            // Invoke CreateDelegate with an "assertive closure" instance.
            //
            var closure = new AssertiveClosure();
            var del     = (Delegate)createDelegate.Invoke(o, new object[] { closure });

            //
            // Prior to invoking the delegate, check we haven't triggered JIT compilation yet.
            //
            var stillJit = (Delegate)target.GetValue(o);

            Assert.AreSame(jitCompiler, stillJit);

            //
            // Invoke the delegate and assert the result through the "assertive closure".
            //
            var res = del.DynamicInvoke(args);

            Assert.AreEqual(ret, res);
            closure.Check(flag: false, args);

            //
            // Check we triggered a JIT compilation and the Target has been replaced.
            //
            var compiled = (Delegate)target.GetValue(o);

            Assert.AreNotSame(jitCompiler, compiled);

            //
            // Invoke the delegate again to assert we don't trigger recompilation.
            //
            closure.Clear();
            res = del.DynamicInvoke(args);
            Assert.AreEqual(ret, res);
            closure.Check(flag: false, args);

            //
            // Invoke the delegate a couple more times to ensure it keeps working.
            //
            // NB: This also tests the tiered compilation logic, which is count-based at the moment.
            //
            for (var i = 0; i < 8; i++)
            {
                res = del.DynamicInvoke(args);
                Assert.AreEqual(ret, res);
            }

            //
            // Substitute the expression to assert reinstallation of the JIT.
            //
            exprs.Insert(0, Expression.Call(closureParam, closureFlag));
            var newExpression =
                Expression.Lambda(
                    innerDelegateType,
                    Expression.Block(
                        retType,
                        exprs
                        ),
                    lambdaParams
                    );

            thunk.Lambda = newExpression;

            //
            // Assert the JIT gets re-installed.
            //
            // NB: Note we assert the target method and not the delegate instance. This is
            //     because the instance can differ; we don't store the compiler delegate
            //     to save space but implement a virtual Compiler property instead, whose
            //     getter returns an instance of the JIT delegate.
            //
            var reinstalledJit = (Delegate)target.GetValue(o);

            Assert.AreSame(jitCompiler.Method, reinstalledJit.Method);

            //
            // Invoke the lambda and assert that the new lambda expression is used.
            //
            closure.Clear();
            res = del.DynamicInvoke(args);
            Assert.AreEqual(ret, res);
            closure.Check(flag: true, args); // NB: Note the check for `flag` being set to `true`.

            //
            // Assert the target is substituted for the newly compiled delegate.
            //
            var recompiled = (Delegate)target.GetValue(o);

            Assert.AreNotSame(compiled, recompiled);
            Assert.AreNotSame(reinstalledJit, recompiled);
        }
コード例 #3
0
        /// <summary>
        /// Prepares the specified <paramref name="expression"/> for JIT compilation support.
        /// </summary>
        /// <param name="methodTable">The parameter containing a reference to the method table.</param>
        /// <param name="analysis">The scope analysis for the expression.</param>
        /// <param name="expression">The expression to prepare for JIT compilation.</param>
        /// <param name="thunkFactory">Factory for thunk types.</param>
        /// <returns>Information used to compile the expression.</returns>
        public static JitCompilationInfo Prepare(ParameterExpression methodTable, Dictionary <object, Scope> analysis, LambdaExpression expression, IThunkFactory thunkFactory)
        {
            //
            // Rewrite the expression tree to prepare it for JIT compilation.
            // This will replace all inner lambdas with explicit calls to thunk
            // methods to create a delegate using explicit closures.
            //
            var impl = new Impl(analysis, methodTable, thunkFactory);
            var expr = impl.Visit(expression);

            //
            // Get the method table whose entries contain the thunks for the
            // inner lambdas. The rewritten expression will use array indexing
            // operations into the `mtParam` parameter to obtain the thunks.
            // The instance of the method table has to be passed to the top-
            // level compiled delegate, which is done by the caller.
            //
            var mt = impl.GetMethodTable();

            //
            // Return all the objects we've built as part of JIT compilation.
            // This enables the caller to intercept the method table we built
            // in order to perform instrumentation or to trigger compilation
            // of inner lambdas in a non-deferred manner.
            //
            return(new JitCompilationInfo
            {
                MethodTableParameter = methodTable,
                Expression = expr,
                MethodTable = mt,
            });
        }