/// <summary> /// Emits load of optional parameters array on the evaluation stack. /// </summary> /// <param name="builder">An overloads builder.</param> /// <param name="start">An index of the first optional parameter to be loaded into the array (indices start from 0).</param> /// <param name="param"> /// A <see cref="ParameterInfo"/> of the formal parameter of the target method where the array will be passed. /// This information influences conversions all optional parameters. /// </param> /// <param name="optArgCount">Optional argument count (unused).</param> internal void EmitLibraryLoadOptArguments(OverloadsBuilder /*!*/ builder, int start, ParameterInfo /*!*/ param, IPlace optArgCount) { Debug.Assert(start >= 0 && builder != null && param != null && builder.Aux is CodeGenerator); ILEmitter il = builder.IL; Type elem_type = param.ParameterType.GetElementType(); Type array_type = elem_type.MakeArrayType(); LocalBuilder loc_array = il.DeclareLocal(array_type); LocalBuilder loc_item = il.DeclareLocal(elem_type); // NEW <alem_type>[<parameters count - start>] il.LdcI4(parameters.Count - start); il.Emit(OpCodes.Newarr, elem_type); il.Stloc(loc_array); // loads each optional parameter into the appropriate bucket of the array: for (int i = start; i < parameters.Count; i++) { // item = <parameter value>; object type_or_value = EmitLibraryLoadArgument(il, i, builder.Aux); builder.EmitArgumentConversion(elem_type, type_or_value, false, param); il.Stloc(loc_item); // array[<i-start>] = item; il.Ldloc(loc_array); il.LdcI4(i - start); il.Ldloc(loc_item); il.Stelem(elem_type); } // loads the array: il.Ldloc(loc_array); }
/// <summary cref="KernelAccelerator{TCompiledKernel, TKernel} /// .GenerateKernelLauncherMethod(TCompiledKernel, int)"/> protected override MethodInfo GenerateKernelLauncherMethod( CLCompiledKernel kernel, int customGroupSize) { var entryPoint = kernel.EntryPoint; AdjustAndVerifyKernelGroupSize(ref customGroupSize, entryPoint); // Add support for by ref parameters if (entryPoint.HasByRefParameters) { throw new NotSupportedException( ErrorMessages.NotSupportedByRefKernelParameters); } var launcher = entryPoint.CreateLauncherMethod(Context); var emitter = new ILEmitter(launcher.ILGenerator); // Load kernel instance var kernelLocal = emitter.DeclareLocal(typeof(CLKernel)); KernelLauncherBuilder.EmitLoadKernelArgument <CLKernel, ILEmitter>( Kernel.KernelInstanceParamIdx, emitter); emitter.Emit(LocalOperation.Store, kernelLocal); // Map all kernel arguments var argumentMapper = Backend.ArgumentMapper; argumentMapper.Map(emitter, kernelLocal, entryPoint); // Load stream KernelLauncherBuilder.EmitLoadAcceleratorStream <CLStream, ILEmitter>( Kernel.KernelStreamParamIdx, emitter); // Load kernel emitter.Emit(LocalOperation.Load, kernelLocal); // Load dimensions KernelLauncherBuilder.EmitLoadRuntimeKernelConfig( entryPoint, emitter, Kernel.KernelParamDimensionIdx, customGroupSize); // Dispatch kernel emitter.EmitCall(LaunchKernelMethod); // Emit ThrowIfFailed emitter.EmitCall(ThrowIfFailedMethod); emitter.Emit(OpCodes.Ret); emitter.Finish(); return(launcher.Finish()); }
/// <summary> /// Emits code that loads current <see cref="PHP.Core.ScriptContext"/> by calling /// <see cref="PHP.Core.ScriptContext.CurrentContext"/> and remembers it in a local. /// </summary> /// <param name="il"></param> public void EmitLoad(ILEmitter il) { if (localBuilder == null) { localBuilder = il.DeclareLocal(typeof(ScriptContext)); il.EmitCall(OpCodes.Call, Methods.ScriptContext.GetCurrentContext, null); il.Stloc(localBuilder); } il.Ldloc(localBuilder); }
/// <summary> /// Emits code that loads a specified parameter on the evaluation stack. /// </summary> /// <param name="paramInfo">The parameter to load.</param> /// <param name="requiredTypeCode">Specifies whether <see cref="PhpReference"/> /// (<see cref="PhpTypeCode.PhpReference"/>), <see cref="object"/> (<see cref="PhpTypeCode.Object"/>), /// or the most fitting of these two should be loaded.</param> public void EmitLoadClrParameter(ParameterInfo /*!*/ paramInfo, PhpTypeCode requiredTypeCode) { if (paramInfo.IsOut) { il.Emit(OpCodes.Ldnull); } else { il.Ldarg(paramInfo.Position + paramOffset); // dereference ref param Type param_type = paramInfo.ParameterType; if (param_type.IsByRef) { param_type = param_type.GetElementType(); il.Ldind(param_type); } // convert the parameter to PHP type PhpTypeCode type_code = ClrOverloadBuilder.EmitConvertToPhp( il, param_type /*, * scriptContextPlace*/); il.EmitBoxing(type_code); } // check whether we have to create a PhpReference if (requiredTypeCode == PhpTypeCode.Object || (requiredTypeCode == PhpTypeCode.Unknown && !paramInfo.ParameterType.IsByRef)) { return; } if (paramInfo.ParameterType.IsByRef) { LocalBuilder ref_local = il.DeclareLocal(Types.PhpReference[0]); // remember the PhpReference in a local il.Emit(OpCodes.Newobj, Constructors.PhpReference_Object); il.Emit(OpCodes.Dup); il.Stloc(ref_local); referenceLocals[paramInfo.Position] = ref_local; } else { // no reference store-back is necessary il.Emit(OpCodes.Newobj, Constructors.PhpReference_Object); } }
public void EmitNewLinqContext() { ILEmitter il = cg.IL; linqContextLocal = il.DeclareLocal(linqContextBuilder); // linq_context = NEW <linq context>(<this>, <variables>, <script context>, <type desc>) cg.SelfPlace.EmitLoad(il); cg.RTVariablesTablePlace.EmitLoad(il); cg.ScriptContextPlace.EmitLoad(il); cg.TypeContextPlace.EmitLoad(il); il.Emit(OpCodes.Newobj, linqContextCtor); il.Stloc(linqContextLocal); }
private static void EmitExportedConstructor(PhpType /*!*/ phpType, ConstructorBuilder /*!*/ ctorStubBuilder, PhpMethod phpCtor, ParameterInfo[] /*!*/ parameters) { // set parameter names and attributes if (phpCtor != null) { ClrStubBuilder.DefineStubParameters( ctorStubBuilder, phpCtor.Builder.Signature.FormalParams, parameters); } // emit ctor body ILEmitter cil = new ILEmitter(ctorStubBuilder); // [ this(ScriptContext.CurrentContext ] cil.Ldarg(FunctionBuilder.ArgThis); cil.EmitCall(OpCodes.Call, Methods.ScriptContext.GetCurrentContext, null); LocalBuilder sc_local = cil.DeclareLocal(Types.ScriptContext[0]); cil.Stloc(sc_local); cil.Ldloc(sc_local); cil.LdcI4(1); cil.Emit(OpCodes.Call, phpType.ShortConstructorInfo); if (phpCtor != null) { // invoke the PHP ctor method ClrStubBuilder.EmitMethodStubBody( cil, new Place(sc_local), parameters, new GenericTypeParameterBuilder[0], Types.Void, phpCtor, phpCtor.DeclaringType); } else { cil.Emit(OpCodes.Ret); } }
public static void EmitArgFullPreCall(ILEmitter /*!*/ il, IPlace /*!*/ stack, bool argsAware, int formalParamCount, int formalTypeParamCount, out LocalBuilder locArgsCount) { if (argsAware) { locArgsCount = il.DeclareLocal(typeof(int)); // locArgsCount = stack.MakeArgsAware(<formal tpye param count | formal param count>); stack.EmitLoad(il); il.LdcI4((formalTypeParamCount << 16) | formalParamCount); il.Emit(OpCodes.Call, Methods.PhpStack.MakeArgsAware); il.Stloc(locArgsCount); } else { locArgsCount = null; // CALL stack.RemoveFrame(); stack.EmitLoad(il); il.Emit(OpCodes.Call, Methods.PhpStack.RemoveFrame); } }
/// <summary> /// Emits IL instructions that transfer the control to the target label for <B>break</B> statement /// having parameter that cannot be evaluated at compile time. /// </summary> /// <remarks>This function is used to generate code for <B>break v;</B> where <i>v</i> is a variable.</remarks> public void EmitBreakRuntime() { int i; ILEmitter il = codeGenerator.IL; Label[] jumpTable = new Label[stack.Count + 1]; Label exitLabel = il.DefineLabel(); Debug.Assert(stack.Count != 0); for (i = 0; i <= stack.Count; i++) { jumpTable[i] = il.DefineLabel(); } // The value according to we switch is already present on the evaluation stack LocalBuilder break_level_count = il.DeclareLocal(typeof(Int32)); il.Emit(OpCodes.Dup); il.Stloc(break_level_count); il.Emit(OpCodes.Switch, jumpTable); // Default case il.Ldloc(break_level_count); codeGenerator.EmitPhpException(Methods.PhpException.InvalidBreakLevelCount); il.Emit(OpCodes.Br, exitLabel); il.MarkLabel(jumpTable[0]); EmitBranchToExit((StackItem)stack[stack.Count - 1]); for (i = 1; i <= stack.Count; i++) { il.MarkLabel(jumpTable[i]); EmitBranchToExit((StackItem)stack[stack.Count - i]); } il.MarkLabel(exitLabel); }
/// <summary> /// Emits load of an array where all optional arguments are stored. /// Each optional argument is peeked from the PHP stack and converted before stored to the array. /// The resulting array is pushed on evaluation stack so it can be later passed as an argument to a method. /// </summary> /// <param name="builder">The builder.</param> /// <param name="start">The index of the first argument to be loaded.</param> /// <param name="param">The last parameter of the overload (should be an array).</param> /// <param name="optArgCount">The place where the number of optional arguments is stored.</param> /// <remarks>Assumes that the non-negative number of optional arguments has been stored to /// <paramref name="optArgCount"/> place.</remarks> public static void EmitPeekAllArguments(OverloadsBuilder /*!*/ builder, int start, ParameterInfo param, IPlace optArgCount) { Debug.Assert(start >= 0 && optArgCount != null && param != null); ILEmitter il = builder.IL; Type elem_type = param.ParameterType.GetElementType(); Type array_type = Type.GetType(elem_type.FullName + "[]", true); Type actual_type; // declares aux. variables: LocalBuilder loc_array = il.DeclareLocal(array_type); LocalBuilder loc_i = il.DeclareLocal(typeof(int)); LocalBuilder loc_elem = il.DeclareLocal(elem_type); // creates an array for the arguments // array = new <elem_type>[opt_arg_count]: optArgCount.EmitLoad(il); il.Emit(OpCodes.Newarr, elem_type); il.Stloc(loc_array); Label for_end_label = il.DefineLabel(); Label condition_label = il.DefineLabel(); // i = 0; il.Emit(OpCodes.Ldc_I4_0); il.Stloc(loc_i); // FOR (i = 0; i < opt_arg_count; i++) if (true) { il.MarkLabel(condition_label); // condition (i < opt_arg_count): il.Ldloc(loc_i); optArgCount.EmitLoad(il); il.Emit(OpCodes.Bge, for_end_label); // LOAD stack, i + start+1>: builder.Stack.EmitLoad(il); il.Ldloc(loc_i); il.LdcI4(start + 1); il.Emit(OpCodes.Add); if (elem_type == typeof(PhpReference)) { // CALL stack.PeekReferenceUnchecked(STACK); il.Emit(OpCodes.Call, Methods.PhpStack.PeekReferenceUnchecked); actual_type = typeof(PhpReference); } else { // CALL stack.PeekValueUnchecked(STACK); il.Emit(OpCodes.Call, Methods.PhpStack.PeekValueUnchecked); actual_type = typeof(object); } // emits a conversion stuff (loads result into "elem" local variable): builder.EmitArgumentConversion(elem_type, actual_type, false, param); il.Stloc(loc_elem); // array[i] = elem; il.Ldloc(loc_array); il.Ldloc(loc_i); il.Ldloc(loc_elem); il.Stelem(elem_type); // i = i + 1; il.Ldloc(loc_i); il.Emit(OpCodes.Ldc_I4_1); il.Emit(OpCodes.Add); il.Stloc(loc_i); // GOTO condition; il.Emit(OpCodes.Br, condition_label); } // END FOR il.MarkLabel(for_end_label); // loads array to stack - consumed by the method call: il.Ldloc(loc_array); }
/// <summary cref="KernelAccelerator{TCompiledKernel, TKernel} /// .GenerateKernelLauncherMethod(TCompiledKernel, int)"/> protected override MethodInfo GenerateKernelLauncherMethod( CLCompiledKernel kernel, int customGroupSize) { var entryPoint = kernel.EntryPoint; AdjustAndVerifyKernelGroupSize(ref customGroupSize, entryPoint); // Add support for by ref parameters if (entryPoint.HasByRefParameters) { throw new NotSupportedException( ErrorMessages.NotSupportedByRefKernelParameters); } using var scopedLock = entryPoint.CreateLauncherMethod( Context.RuntimeSystem, out var launcher); var emitter = new ILEmitter(launcher.ILGenerator); // Load kernel instance var kernelLocal = emitter.DeclareLocal(typeof(CLKernel)); KernelLauncherBuilder.EmitLoadKernelArgument <CLKernel, ILEmitter>( Kernel.KernelInstanceParamIdx, emitter); emitter.Emit(LocalOperation.Store, kernelLocal); // Map all kernel arguments var argumentMapper = Backend.ArgumentMapper; argumentMapper.Map( emitter, kernelLocal, Context.TypeContext, entryPoint); // Load current driver API emitter.EmitCall(GetCLAPIMethod); // Load stream KernelLauncherBuilder.EmitLoadAcceleratorStream <CLStream, ILEmitter>( Kernel.KernelStreamParamIdx, emitter); // Load kernel emitter.Emit(LocalOperation.Load, kernelLocal); // Load dimensions KernelLauncherBuilder.EmitLoadRuntimeKernelConfig( entryPoint, emitter, Kernel.KernelParamDimensionIdx, MaxGridSize, MaxGroupSize, customGroupSize); // Dispatch kernel var launchMethod = GenericLaunchKernelMethod.MakeGenericMethod( entryPoint.SharedMemory.HasDynamicMemory ? typeof(DynamicSharedMemoryHandler) : typeof(DefaultLaunchHandler)); emitter.EmitCall(launchMethod); // Emit ThrowIfFailed emitter.EmitCall(ThrowIfFailedMethod); emitter.Emit(OpCodes.Ret); emitter.Finish(); return(launcher.Finish()); }
private ArrayList refReferences = new ArrayList(3); // GENERICS: <LocalBuilder> #endregion #region Call Switch Emitter /// <summary> /// Emits calls to specified overloads and a switch statement which calls appropriate overload /// according to the current value of <see cref="PhpStack.ArgCount"/> field of the current stack. /// </summary> /// <param name="thisRef">Reference to self.</param> /// <param name="script_context">Current script context.</param> /// <param name="rtVariables"> /// Place where a run-time variables table can be loaded from. /// </param> /// <param name="namingContext">Naming context load-from place.</param> /// <param name="classContext">Class context load.</param> /// <param name="overloads">The overload list.</param> /// <remarks> /// Example: given overloads (2,5,7,9+), i.e. there are four overloads having 2, 5, 7 and 9 PHP parameters, /// respectively, and the last overload is marked as vararg, /// the method emits the following code: /// <code> /// switch(ArgCount - 2) // 2 = minimum { arg count of overload } /// { /// case 0: return call #2; // call to the 2nd overload with appropriate arg. and return value handling /// case 1: goto case error; /// case 2: goto case error; /// case 3: return call #5; /// case 4: goto case error; /// case 5: return call #7; /// case 6: goto case error; /// /// #if vararg /// case 7: goto default; /// default: return call #vararg (9 mandatory args,optional args);break; /// #elif /// case 7: return call #9; /// default: goto case error; /// #endif /// /// case error: PhpException.InvalidArgumentCount(null, functionName); break; /// } /// </code> /// </remarks> public void EmitCallSwitch(IPlace /*!*/ thisRef, IPlace /*!*/ script_context, IPlace /*!*/ rtVariables, IPlace /*!*/ namingContext, IPlace /*!*/ classContext, List <PhpLibraryFunction.Overload> /*!!*/ overloads) { Debug.AssertAllNonNull(overloads); int last = overloads.Count - 1; int min = overloads[0].ParamCount; int max = overloads[last].ParamCount; var flags = overloads[last].Flags; // if function is not supported, just throw the warning: if ((flags & PhpLibraryFunction.OverloadFlags.NotSupported) != 0) { // stack.RemoveFrame(); if (stack != null) { stack.EmitLoad(il); il.Emit(OpCodes.Call, Methods.PhpStack.RemoveFrame); } // PhpException.FunctionNotSupported( <FullName> ); il.Emit(OpCodes.Ldstr, FunctionName.Value); il.Emit(OpCodes.Call, Methods.PhpException.FunctionNotSupported_String); if (debug) { il.Emit(OpCodes.Nop); } // load methods default value il.EmitBoxing(OverloadsBuilder.EmitLoadDefault(il, overloads[last].Method)); return; } bool is_vararg = (flags & PhpLibraryFunction.OverloadFlags.IsVararg) != 0; if ((flags & PhpLibraryFunction.OverloadFlags.NeedsScriptContext) == 0) { script_context = null; } if ((flags & PhpLibraryFunction.OverloadFlags.NeedsThisReference) == 0) { thisRef = null; } if ((flags & PhpLibraryFunction.OverloadFlags.NeedsVariables) == 0) { rtVariables = null; } if ((flags & PhpLibraryFunction.OverloadFlags.NeedsNamingContext) == 0) { namingContext = null; } if ((flags & (PhpLibraryFunction.OverloadFlags.NeedsClassContext | PhpLibraryFunction.OverloadFlags.NeedsLateStaticBind)) == 0) { classContext = null; } Label end_label = il.DefineLabel(); Label error_label = il.DefineLabel(); Label[] cases = new Label[max - min + 1]; MethodInfo method; // fills cases with "goto case error": for (int i = 0; i < cases.Length; i++) { cases[i] = error_label; } // define labels for valid cases: for (int i = 0; i < overloads.Count; i++) { int count = overloads[i].ParamCount; cases[count - min] = il.DefineLabel(); } // LOAD(stack.ArgCount - min); stack.EmitLoad(il); il.Emit(OpCodes.Ldfld, Fields.PhpStack_ArgCount); il.LdcI4(min); il.Emit(OpCodes.Sub); // SWITCH(tmp) il.Emit(OpCodes.Switch, cases); // CASE >=N or <0 (underflows); // if the last overload is vararg: if (is_vararg) { LocalBuilder opt_arg_count_local = il.DeclareLocal(typeof(int)); // CASE N: il.MarkLabel(cases[cases.Length - 1]); // opt_arg_count = stack.ArgCount - max; stack.EmitLoad(il); il.Emit(OpCodes.Ldfld, Fields.PhpStack_ArgCount); il.LdcI4(max); il.Emit(OpCodes.Sub); il.Stloc(opt_arg_count_local); // IF(tmp<0) GOTO CASE error; il.Ldloc(opt_arg_count_local); il.Emit(OpCodes.Ldc_I4_0); il.Emit(OpCodes.Blt, error_label); // emits argument loading, stack frame removal, method call, return value conversion: method = overloads[last].Method; Type return_type = EmitOverloadCall(method, overloads[last].RealParameters, max, script_context, rtVariables, namingContext, classContext, new Place(opt_arg_count_local), thisRef, false); // loads boxed return value: if (return_type != Types.Void) { //il.LoadBoxed(return_value); if (return_type.IsValueType) { il.Emit(OpCodes.Box, return_type); } } else { il.Emit(OpCodes.Ldnull); } // RETURN; il.Emit(OpCodes.Ret); //bug in Reflector: il.Emit(OpCodes.Br,end_label); } else { // GOTO CASE error; il.Emit(OpCodes.Br, error_label); } // emits all valid cases which are not vararg: int j = 0; for (int i = min; i <= max - (is_vararg ? 1 : 0); i++) { if (overloads[j].ParamCount == i) { // CASE <i>; il.MarkLabel(cases[i - min]); // emits argument loading, stack frame removal, method call, return value conversion: method = overloads[j].Method; Type return_type = EmitOverloadCall(method, overloads[j].RealParameters, i, script_context, rtVariables, namingContext, classContext, null, thisRef, false); // loads boxed return value: if (return_type != Types.Void) { //il.LoadBoxed(return_value); if (return_type.IsValueType) { il.Emit(OpCodes.Box, return_type); } } else { il.Emit(OpCodes.Ldnull); } // RETURN; il.Emit(OpCodes.Ret); //bug in Reflector: il.Emit(OpCodes.Br,end_label); j++; } } Debug.Assert(j + (is_vararg ? 1 : 0) == overloads.Count); // ERROR: il.MarkLabel(error_label); il.Emit(OpCodes.Ldnull); il.Emit(OpCodes.Ldstr, this.functionName.ToString()); il.Emit(OpCodes.Call, Methods.PhpException.InvalidArgumentCount); if (debug) { il.Emit(OpCodes.Nop); } // RETURN null: il.Emit(OpCodes.Ldnull); il.MarkLabel(end_label); }
/// <summary> Emit PHP to CLR conversion </summary> /// <remarks>If the return type is interface marked using <seealso cref="DuckTypeAttribute"/> /// it is wrapped again. /// <code> /// // type is IDuckEnumerable<T> /// return new DuckEnumerableWrapper<T>(obj.GetForeachEnumerator(false, false, null)) /// /// // type is IDuckKeyedEnumerable<T> /// return new DuckKeyedEnumerableWrapper<T>(obj.GetForeachEnumerator(true, false, null)) /// /// // type is marked using [DuckType] /// return DuckTyping.Instance.ImplementDuckType<T>(obj); /// /// // otherwise uses standard ConvertToClr conversion method /// </code> /// </remarks> private static void EmitReturn(ILEmitter il, Type returnedType, bool isPhpRef) { Type[] gargs = returnedType.GetGenericArguments(); object[] attrs = returnedType.GetCustomAttributes(typeof(DuckTypeAttribute), false); bool isDuckEnumerable = (gargs.Length == 1 && returnedType.Equals(typeof(IDuckEnumerable <>).MakeGenericType(gargs))); bool isDuckKeyedEnumerable = (gargs.Length == 2 && returnedType.Equals(typeof(IDuckKeyedEnumerable <,>).MakeGenericType(gargs))); bool isDuckType = attrs != null && attrs.Length > 0; if (returnedType.Equals(typeof(void))) { il.Emit(OpCodes.Pop); il.Emit(OpCodes.Ret); } else if (isDuckType || isDuckEnumerable || isDuckKeyedEnumerable) { LocalBuilder tmp = il.DeclareLocal(typeof(object)); //store the value local var (after unwrapping it from the reference) if (isPhpRef) { il.Emit(OpCodes.Ldfld, Fields.PhpReference_Value); } il.Stloc(tmp); Label lblTestMinusOne = il.DefineLabel(); Label lblWrap = il.DefineLabel(); Label lblInvalidInt = il.DefineLabel(); // test whether the value is null il.Ldloc(tmp); il.Emit(OpCodes.Ldnull); il.Emit(OpCodes.Ceq); il.Emit(OpCodes.Brfalse, lblTestMinusOne); il.Emit(OpCodes.Ldnull); il.Emit(OpCodes.Ret); il.MarkLabel(lblTestMinusOne); // test whether value is -1 il.Ldloc(tmp); il.Emit(OpCodes.Isinst, typeof(int)); il.Emit(OpCodes.Brfalse, lblWrap); // value is not int, so we can wrap the value il.Ldloc(tmp); il.Emit(OpCodes.Unbox_Any, typeof(int)); il.Emit(OpCodes.Ldc_I4, -1); il.Emit(OpCodes.Ceq); il.Emit(OpCodes.Brfalse, lblWrap); // value is int but not -1 il.Emit(OpCodes.Ldnull); il.Emit(OpCodes.Ret); il.MarkLabel(lblWrap); // specific duck type wrapping if (isDuckEnumerable || isDuckKeyedEnumerable) { il.Ldloc(tmp); il.Emit(OpCodes.Dup); // Standard: new DuckEnumerableWrapper<T>(obj.GetForeachEnumerator(false, false, null)) // Keyed: new DuckKeyedEnumerableWrapper<T>(obj.GetForeachEnumerator(false, false, null)) il.LoadLiteral(gargs.Length == 2); // keyed? il.LoadLiteral(false); il.LoadLiteral(null); il.Emit(OpCodes.Callvirt, Methods.IPhpEnumerable_GetForeachEnumerator); if (isDuckEnumerable) { il.Emit(OpCodes.Newobj, typeof(DuckEnumerableWrapper <>). MakeGenericType(gargs).GetConstructors()[0]); } else { il.Emit(OpCodes.Newobj, typeof(DuckKeyedEnumerableWrapper <,>). MakeGenericType(gargs).GetConstructors()[0]); } } else { il.Emit(OpCodes.Call, typeof(DuckTyping).GetProperty("Instance", BindingFlags.Public | BindingFlags.Static).GetGetMethod()); il.Ldloc(tmp); il.Emit(OpCodes.Call, typeof(DuckTyping).GetMethod("ImplementDuckType", BindingFlags.Public | BindingFlags.Instance).MakeGenericMethod(returnedType)); } il.Emit(OpCodes.Ret); } else { if (returnedType == typeof(object)) { Label lbl = il.DefineLabel(); if (isPhpRef) { il.Emit(OpCodes.Ldfld, Fields.PhpReference_Value); } il.Emit(OpCodes.Dup); il.Emit(OpCodes.Isinst, typeof(PhpBytes)); il.Emit(OpCodes.Brfalse, lbl); il.EmitCall(OpCodes.Call, typeof(IPhpConvertible).GetMethod("ToString", Type.EmptyTypes), Type.EmptyTypes); il.Emit(OpCodes.Ret); il.MarkLabel(lbl); ClrOverloadBuilder.EmitConvertToClr(il, PhpTypeCode.Object, returnedType); il.Emit(OpCodes.Ret); } else { ClrOverloadBuilder.EmitConvertToClr(il, isPhpRef ? PhpTypeCode.PhpReference : PhpTypeCode.Object, returnedType); il.Emit(OpCodes.Ret); } } }
/// <summary> Implements method </summary> /// <remarks><code> /// class A : IDuck { /// /*type*/ Func(/*arguments*/) { /// sc = ScriptContext.Current; /// // temporary array is created only when arguments.Length > 8 (otherwise AddFrame overload exists) /// object[] tmp = new object[arguments.Length]; /// tmp[#i] = new PhpReference(PhpVariable.Copy(ClrObject.WrapDynamic(argument#i), CopyReason.PassedByCopy)); /// sc.Stack.AddFrame(tmp); /// return /* .. type conversion .. */ /// } /// } /// </code></remarks> private void ImplementMethod(TypeBuilder tb, MethodInfo method, FieldInfo fld, bool globalFuncs) { // get parameters (i want C# 3.0 NOW!!) ParameterInfo[] pinfo = method.GetParameters(); Type[] ptypes = new Type[pinfo.Length]; for (int i = 0; i < pinfo.Length; i++) { ptypes[i] = pinfo[i].ParameterType; } int argCount = pinfo.Length; string methName = method.Name; object[] attrs = method.GetCustomAttributes(typeof(DuckNameAttribute), false); if (attrs.Length > 0) { methName = ((DuckNameAttribute)attrs[0]).Name; } // define method MethodBuilder mb = tb.DefineMethod(method.Name, MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual | MethodAttributes.Final, method.ReturnType, ptypes); ILEmitter il = new ILEmitter(mb); // Wrap parameters // sc = ScriptContext.Current LocalBuilder sc = il.DeclareLocal(typeof(ScriptContext)); il.Emit(OpCodes.Call, Methods.ScriptContext.GetCurrentContext); il.Stloc(sc); LocalBuilder ar = null; if (argCount > 8) { // tmp = new object[pinfo.Length]; ar = il.DeclareLocal(typeof(object[])); il.Emit(OpCodes.Ldc_I4, pinfo.Length); il.Emit(OpCodes.Newarr, typeof(object)); il.Stloc(ar); } // sc.Stack.AddFrame(...); il.Ldloc(sc); il.Load(Fields.ScriptContext_Stack); for (int i = 0; i < argCount; i++) { if (argCount > 8) { // tmp[i] il.Emit(OpCodes.Ldloc, ar); il.Emit(OpCodes.Ldc_I4, i); } // if (param#i is IDuckType) // param#i.OriginalObject // else // new PhpReference(PhpVariable.Copy(ClrObject.WrapDynamic(param#i), CopyReason.PassedByCopy)); Label lblDuckType = il.DefineLabel(); Label lblEnd = il.DefineLabel(); if (!ptypes[i].IsValueType) { il.Ldarg(i + 1); il.Emit(OpCodes.Isinst, typeof(IDuckType)); il.Emit(OpCodes.Brtrue, lblDuckType); } il.Ldarg(i + 1); if (ptypes[i].IsValueType) { il.Emit(OpCodes.Box, ptypes[i]); } il.Emit(OpCodes.Call, Methods.ClrObject_WrapDynamic); il.LdcI4((int)CopyReason.PassedByCopy); il.Emit(OpCodes.Call, Methods.PhpVariable.Copy); il.Emit(OpCodes.Newobj, Constructors.PhpReference_Object); if (!ptypes[i].IsValueType) { il.Emit(OpCodes.Br, lblEnd); il.MarkLabel(lblDuckType); il.Ldarg(i + 1); il.Emit(OpCodes.Call, typeof(IDuckType).GetProperty("OriginalObject").GetGetMethod()); il.MarkLabel(lblEnd); } if (argCount > 8) { il.Emit(OpCodes.Stelem_Ref); } } if (argCount > 8) { il.Emit(OpCodes.Ldloc, ar); } il.Emit(OpCodes.Call, Methods.PhpStack.AddFrame.Overload(argCount)); if (globalFuncs) { // localVariables = null, namingContext = null // ScriptContex.Call(null, null, "Foo", null, ScriptContext.Current).value; il.LoadLiteral(null); il.LoadLiteral(null); il.LoadLiteral(methName); il.LoadLiteral(null); il.Emit(OpCodes.Ldsflda, il.TypeBuilder.DefineField("<callHint>'lambda", typeof(PHP.Core.Reflection.DRoutineDesc), FieldAttributes.Static | FieldAttributes.Private)); il.Ldloc(sc); il.Emit(OpCodes.Call, Methods.ScriptContext.Call); } else { // Operators.InvokeMethod(this.obj, "Foo", null, ScriptContext.Current).value; il.Ldarg(0); il.Load(fld); il.LoadLiteral(methName); il.LoadLiteral(null); il.Ldloc(sc); il.Emit(OpCodes.Call, Methods.Operators.InvokeMethodStr); } EmitReturn(il, method.ReturnType, true); tb.DefineMethodOverride(mb, method); }
/// <include file='Doc/Nodes.xml' path='doc/method[@name="Emit"]/*'/> internal override void Emit(CodeGenerator /*!*/ codeGenerator) { Statistics.AST.AddNode("SwitchStmt"); ILEmitter il = codeGenerator.IL; // Note: // SwitchStmt is now implemented in the most general (and unefficient) way. The whole switch // is understood as a series of if-elseif-else statements. Label exit_label = il.DefineLabel(); bool fall_through = false; Label fall_through_label = il.DefineLabel(); Label last_default_label = il.DefineLabel(); DefaultItem last_default = GetLastDefaultItem(); LocalBuilder branch_to_lastdefault = null; if (last_default != null) { branch_to_lastdefault = il.DeclareLocal(Types.Bool[0]); il.LdcI4(0); il.Stloc(branch_to_lastdefault); } codeGenerator.BranchingStack.BeginLoop(exit_label, exit_label, codeGenerator.ExceptionBlockNestingLevel); // marks a sequence point containing the discriminator evaluation: codeGenerator.MarkSequencePoint( switchValue.Position.FirstLine, switchValue.Position.FirstColumn, switchValue.Position.LastLine, switchValue.Position.LastColumn + 1); // Evaluate condition value and store the result into local variable codeGenerator.EmitBoxing(switchValue.Emit(codeGenerator)); LocalBuilder condition_value = il.DeclareLocal(Types.Object[0]); il.Stloc(condition_value); foreach (SwitchItem item in switchItems) { item.MarkSequencePoint(codeGenerator); // switch item is either CaseItem ("case xxx:") or DefaultItem ("default") item: CaseItem case_item = item as CaseItem; if (case_item != null) { Label false_label = il.DefineLabel(); // PhpComparer.Default.CompareEq(<switch expr. value>,<case value>); /*changed to static method*/ //il.Emit(OpCodes.Ldsfld, Fields.PhpComparer_Default); codeGenerator.EmitCompareEq( cg => { cg.IL.Ldloc(condition_value); return(PhpTypeCode.Object); }, cg => case_item.EmitCaseValue(cg)); // IF (!STACK) GOTO false_label; il.Emit(OpCodes.Brfalse, false_label); if (fall_through == true) { il.MarkLabel(fall_through_label, true); fall_through = false; } case_item.EmitStatements(codeGenerator); if (fall_through == false) { fall_through_label = il.DefineLabel(); fall_through = true; } il.Emit(OpCodes.Br, fall_through_label); il.MarkLabel(false_label, true); } else { DefaultItem default_item = (DefaultItem)item; // Only the last default branch defined in source code is used. // So skip default while testing "case" items at runtime. Label false_label = il.DefineLabel(); il.Emit(OpCodes.Br, false_label); if (default_item == last_default) { il.MarkLabel(last_default_label, false); } if (fall_through == true) { il.MarkLabel(fall_through_label, true); fall_through = false; } default_item.EmitStatements(codeGenerator); if (fall_through == false) { fall_through_label = il.DefineLabel(); fall_through = true; } il.Emit(OpCodes.Br, fall_through_label); il.MarkLabel(false_label, true); } } // If no case branch matched, branch to last default case if any is defined if (last_default != null) { // marks a sequence point containing the condition evaluation or skip of the default case: codeGenerator.MarkSequencePoint( last_default.Position.FirstLine, last_default.Position.FirstColumn, last_default.Position.LastLine, last_default.Position.LastColumn + 1); Debug.Assert(branch_to_lastdefault != null); Label temp = il.DefineLabel(); // IF (!branch_to_lastdefault) THEN il.Ldloc(branch_to_lastdefault); il.LdcI4(0); il.Emit(OpCodes.Bne_Un, temp); if (true) { // branch_to_lastdefault = TRUE; il.LdcI4(1); il.Stloc(branch_to_lastdefault); // GOTO last_default_label; il.Emit(OpCodes.Br, last_default_label); } il.MarkLabel(temp, true); // END IF; il.ForgetLabel(last_default_label); } if (fall_through == true) { il.MarkLabel(fall_through_label, true); } il.MarkLabel(exit_label); codeGenerator.BranchingStack.EndLoop(); il.ForgetLabel(exit_label); }
/// <summary> /// Emits local variable switch and performs a specified operation on each case. /// </summary> /// <param name="codeGenerator">The code generator.</param> /// <param name="method">The operation performed in each case.</param> internal void EmitSwitch(CodeGenerator codeGenerator, SwitchMethod method) { ILEmitter il = codeGenerator.IL; Debug.Assert(method != null); Label default_case = il.DefineLabel(); Label end_label = il.DefineLabel(); LocalBuilder ivar_local = il.GetTemporaryLocal(Types.String[0], true); LocalBuilder non_interned_local = il.DeclareLocal(Types.String[0]); VariablesTable variables = codeGenerator.CurrentVariablesTable; Label[] labels = new Label[variables.Count]; // non_interned_local = <name expression>; EmitName(codeGenerator); il.Stloc(non_interned_local); // ivar_local = String.IsInterned(non_interned_local) il.Ldloc(non_interned_local); il.Emit(OpCodes.Call, Methods.String_IsInterned); il.Stloc(ivar_local); // switch for every compile-time variable: int i = 0; foreach (VariablesTable.Entry variable in variables) { labels[i] = il.DefineLabel(); // IF (ivar_local == <i-th variable name>) GOTO labels[i]; il.Ldloc(ivar_local); il.Emit(OpCodes.Ldstr, variable.VariableName.ToString()); il.Emit(OpCodes.Beq, labels[i]); i++; } // GOTO default_case: il.Emit(OpCodes.Br, default_case); // operation on each variable: i = 0; foreach (VariablesTable.Entry variable in variables) { // labels[i]: il.MarkLabel(labels[i]); // operation: method(codeGenerator, variable, null); // GOTO end; il.Emit(OpCodes.Br, end_label); i++; } // default case - new variable created at runtime: il.MarkLabel(default_case); method(codeGenerator, null, non_interned_local); // END: il.MarkLabel(end_label); }
/// <summary> /// Generates a dynamic kernel-launcher method that will be just-in-time compiled /// during the first invocation. Using the generated launcher lowers the overhead /// for kernel launching dramatically, since unnecessary operations (like boxing) /// can be avoided. /// </summary> /// <param name="kernel">The kernel to generate a launcher for.</param> /// <param name="customGroupSize">The custom group size for the launching operation.</param> /// <returns>The generated launcher method.</returns> private MethodInfo GenerateKernelLauncherMethod(ILCompiledKernel kernel, int customGroupSize) { var entryPoint = kernel.EntryPoint; AdjustAndVerifyKernelGroupSize(ref customGroupSize, entryPoint); var launcher = entryPoint.CreateLauncherMethod(Context); var emitter = new ILEmitter(launcher.ILGenerator); var cpuKernel = emitter.DeclareLocal(typeof(CPUKernel)); KernelLauncherBuilder.EmitLoadKernelArgument <CPUKernel, ILEmitter>( Kernel.KernelInstanceParamIdx, emitter); emitter.Emit(LocalOperation.Store, cpuKernel); // Create an instance of the custom task type var task = emitter.DeclareLocal(kernel.TaskType); { var sharedMemSize = KernelLauncherBuilder.EmitSharedMemorySizeComputation(entryPoint, emitter); emitter.Emit(LocalOperation.Load, cpuKernel); emitter.EmitCall( typeof(CPUKernel).GetProperty( nameof(CPUKernel.KernelExecutionDelegate), BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance).GetGetMethod(true)); // Load custom user dimension KernelLauncherBuilder.EmitLoadDimensions( entryPoint, emitter, Kernel.KernelParamDimensionIdx, () => emitter.EmitNewObject( typeof(Index3).GetConstructor( new Type[] { typeof(int), typeof(int), typeof(int) }))); // Load dimensions as index3 arguments KernelLauncherBuilder.EmitLoadDimensions( entryPoint, emitter, Kernel.KernelParamDimensionIdx, () => emitter.EmitNewObject( typeof(Index3).GetConstructor( new Type[] { typeof(int), typeof(int), typeof(int) })), customGroupSize); // Load shared-memory size emitter.Emit(LocalOperation.Load, sharedMemSize); // Create new task object emitter.EmitNewObject(kernel.TaskConstructor); // Store task emitter.Emit(LocalOperation.Store, task); } // Assign parameters var parameters = entryPoint.Parameters; for (int i = 0, e = parameters.NumParameters; i < e; ++i) { emitter.Emit(LocalOperation.Load, task); emitter.Emit(ArgumentOperation.Load, i + Kernel.KernelParameterOffset); if (parameters.IsByRef(i)) { emitter.Emit(OpCodes.Ldobj, parameters[i]); } emitter.Emit(OpCodes.Stfld, kernel.TaskArgumentMapping[i]); } // Launch task: ((CPUKernel)kernel).CPUAccelerator.Launch(task); emitter.Emit(LocalOperation.Load, cpuKernel); emitter.EmitCall( typeof(CPUKernel).GetProperty( nameof(CPUKernel.CPUAccelerator)).GetGetMethod(false)); emitter.Emit(LocalOperation.Load, task); emitter.EmitCall( typeof(CPUAccelerator).GetMethod( nameof(CPUAccelerator.Launch), BindingFlags.NonPublic | BindingFlags.Instance)); // End of launch method emitter.Emit(OpCodes.Ret); emitter.Finish(); return(launcher.Finish()); }
/// <summary> /// Generates a dynamic kernel-launcher method that will be just-in-time compiled /// during the first invocation. Using the generated launcher lowers the overhead /// for kernel launching dramatically, since unnecessary operations (like boxing) /// can be avoided. /// </summary> /// <param name="kernel">The kernel to generate a launcher for.</param> /// <param name="customGroupSize"> /// The custom group size for the launching operation. /// </param> /// <returns>The generated launcher method.</returns> private MethodInfo GenerateKernelLauncherMethod( ILCompiledKernel kernel, int customGroupSize) { var entryPoint = kernel.EntryPoint; AdjustAndVerifyKernelGroupSize(ref customGroupSize, entryPoint); // Add support for by ref parameters if (entryPoint.HasByRefParameters) { throw new NotSupportedException( ErrorMessages.NotSupportedByRefKernelParameters); } using var scopedLock = entryPoint.CreateLauncherMethod( Context.RuntimeSystem, out var launcher); var emitter = new ILEmitter(launcher.ILGenerator); // Pretend to map kernel arguments (like a GPU accelerator would perform). var argumentMapper = Backend.ArgumentMapper; argumentMapper.Map(entryPoint); var cpuKernel = emitter.DeclareLocal(typeof(CPUKernel)); KernelLauncherBuilder.EmitLoadKernelArgument <CPUKernel, ILEmitter>( Kernel.KernelInstanceParamIdx, emitter); emitter.Emit(LocalOperation.Store, cpuKernel); // Create an instance of the custom task type var task = emitter.DeclareLocal(kernel.TaskType); { emitter.Emit(LocalOperation.Load, cpuKernel); emitter.EmitCall(CPUKernel.GetKernelExecutionDelegate); // Load custom user dimension KernelLauncherBuilder.EmitLoadKernelConfig( entryPoint, emitter, Kernel.KernelParamDimensionIdx, MaxGridSize, MaxGroupSize); // Load dimensions KernelLauncherBuilder.EmitLoadRuntimeKernelConfig( entryPoint, emitter, Kernel.KernelParamDimensionIdx, MaxGridSize, MaxGroupSize, customGroupSize); // Create new task object emitter.EmitNewObject(kernel.TaskConstructor); // Store task emitter.Emit(LocalOperation.Store, task); } // Assign parameters var parameters = entryPoint.Parameters; for (int i = 0, e = parameters.Count; i < e; ++i) { emitter.Emit(LocalOperation.Load, task); emitter.Emit(ArgumentOperation.Load, i + Kernel.KernelParameterOffset); if (parameters.IsByRef(i)) { emitter.Emit(OpCodes.Ldobj, parameters[i]); } emitter.Emit(OpCodes.Stfld, kernel.TaskArgumentMapping[i]); } // Launch task: ((CPUKernel)kernel).CPUAccelerator.Launch(task); emitter.Emit(LocalOperation.Load, cpuKernel); emitter.EmitCall( typeof(CPUKernel).GetProperty( nameof(CPUKernel.CPUAccelerator)).GetGetMethod(false)); emitter.Emit(LocalOperation.Load, task); emitter.EmitCall( typeof(CPUAccelerator).GetMethod( nameof(CPUAccelerator.Launch), BindingFlags.NonPublic | BindingFlags.Instance)); // End of launch method emitter.Emit(OpCodes.Ret); emitter.Finish(); return(launcher.Finish()); }