private void EmitLoadInteriorAddress(ILCodeStream codeStream, int offset) { codeStream.EmitLdArg(0); // this codeStream.Emit(ILOpcode.ldflda, _helperFieldToken); codeStream.EmitLdc(offset); codeStream.Emit(ILOpcode.add); }
void EmitLoadInteriorAddress(ILCodeStream codeStream, int offset) { // This helper field is needed to generate proper GC tracking. There is no direct way // to create interior pointer. if (_helperFieldToken == 0) _helperFieldToken = NewToken(_method.Context.GetWellKnownType(WellKnownType.Object).GetField("m_pEEType")); codeStream.EmitLdArg(0); // this codeStream.Emit(ILOpcode.ldflda, _helperFieldToken); codeStream.EmitLdc(offset); codeStream.Emit(ILOpcode.add); }
public override MethodIL EmitIL() { // We will generate the following code: // // object ret; // object[] args = new object[parameterCount]; // args[0] = param0; // args[1] = param1; // ... // try { // ret = ((Func<object[], object>)dlg.m_helperObject)(args); // } finally { // param0 = (T0)args[0]; // only generated for each byref argument // } // return (TRet)ret; ILEmitter emitter = new ILEmitter(); ILCodeStream codeStream = emitter.NewCodeStream(); TypeDesc objectType = Context.GetWellKnownType(WellKnownType.Object); TypeDesc objectArrayType = objectType.MakeArrayType(); ILLocalVariable argsLocal = emitter.NewLocal(objectArrayType); bool hasReturnValue = !Signature.ReturnType.IsVoid; bool hasRefArgs = false; if (Signature.Length > 0) { codeStream.EmitLdc(Signature.Length); codeStream.Emit(ILOpcode.newarr, emitter.NewToken(objectType)); codeStream.EmitStLoc(argsLocal); for (int i = 0; i < Signature.Length; i++) { TypeDesc paramType = Signature[i]; bool paramIsByRef = false; if (paramType.IsByRef) { hasRefArgs |= paramType.IsByRef; paramIsByRef = true; paramType = ((ByRefType)paramType).ParameterType; } hasRefArgs |= paramType.IsByRef; codeStream.EmitLdLoc(argsLocal); codeStream.EmitLdc(i); codeStream.EmitLdArg(i + 1); TypeDesc boxableParamType = DelegateDynamicInvokeThunk.ConvertToBoxableType(paramType); ILToken boxableParamToken = emitter.NewToken(boxableParamType); if (paramIsByRef) { codeStream.Emit(ILOpcode.ldobj, boxableParamToken); } codeStream.Emit(ILOpcode.box, boxableParamToken); codeStream.Emit(ILOpcode.stelem_ref); } } else { MethodDesc emptyObjectArrayMethod = Context.GetHelperEntryPoint("DelegateHelpers", "GetEmptyObjectArray"); codeStream.Emit(ILOpcode.call, emitter.NewToken(emptyObjectArrayMethod)); codeStream.EmitStLoc(argsLocal); } if (hasRefArgs) { // we emit a try/finally to update the args array even if an exception is thrown // ilgen.BeginTryBody(); } codeStream.EmitLdArg(0); codeStream.Emit(ILOpcode.ldfld, emitter.NewToken(HelperObjectField)); MetadataType funcType = Context.SystemModule.GetKnownType("System", "Func`2"); TypeDesc instantiatedFunc = funcType.MakeInstantiatedType(objectArrayType, objectType); codeStream.Emit(ILOpcode.castclass, emitter.NewToken(instantiatedFunc)); codeStream.EmitLdLoc(argsLocal); MethodDesc invokeMethod = instantiatedFunc.GetKnownMethod("Invoke", null); codeStream.Emit(ILOpcode.callvirt, emitter.NewToken(invokeMethod)); ILLocalVariable retLocal = (ILLocalVariable)(-1); if (hasReturnValue) { retLocal = emitter.NewLocal(objectType); codeStream.EmitStLoc(retLocal); } else { codeStream.Emit(ILOpcode.pop); } if (hasRefArgs) { // ILGeneratorLabel returnLabel = new ILGeneratorLabel(); // ilgen.Emit(OperationCode.Leave, returnLabel); // copy back ref/out args //ilgen.BeginFinallyBlock(); for (int i = 0; i < Signature.Length; i++) { TypeDesc paramType = Signature[i]; if (paramType.IsByRef) { paramType = ((ByRefType)paramType).ParameterType; TypeDesc boxableParamType = DelegateDynamicInvokeThunk.ConvertToBoxableType(paramType); ILToken boxableParamToken = emitter.NewToken(boxableParamType); // Update parameter codeStream.EmitLdArg(i + 1); codeStream.EmitLdLoc(argsLocal); codeStream.EmitLdc(i); codeStream.Emit(ILOpcode.ldelem_ref); codeStream.Emit(ILOpcode.unbox_any, boxableParamToken); codeStream.Emit(ILOpcode.stobj, boxableParamToken); } } // ilgen.Emit(OperationCode.Endfinally); // ilgen.EndTryBody(); // ilgen.MarkLabel(returnLabel); } if (hasReturnValue) { TypeDesc boxableReturnType = DelegateDynamicInvokeThunk.ConvertToBoxableType(Signature.ReturnType); codeStream.EmitLdLoc(retLocal); codeStream.Emit(ILOpcode.unbox_any, emitter.NewToken(boxableReturnType)); } codeStream.Emit(ILOpcode.ret); return(emitter.Link(this)); }
public override MethodIL EmitIL() { ILEmitter emitter = new ILEmitter(); ILCodeStream codeStream = emitter.NewCodeStream(); ArrayType invocationListArrayType = SystemDelegateType.MakeArrayType(); ILLocalVariable delegateArrayLocal = emitter.NewLocal(invocationListArrayType); ILLocalVariable invocationCountLocal = emitter.NewLocal(Context.GetWellKnownType(WellKnownType.Int32)); ILLocalVariable iteratorLocal = emitter.NewLocal(Context.GetWellKnownType(WellKnownType.Int32)); ILLocalVariable delegateToCallLocal = emitter.NewLocal(SystemDelegateType); ILLocalVariable returnValueLocal = 0; if (!Signature.ReturnType.IsVoid) { returnValueLocal = emitter.NewLocal(Signature.ReturnType); } // Fill in delegateArrayLocal // Delegate[] delegateArrayLocal = (Delegate[])this.m_helperObject // ldarg.0 (this pointer) // ldfld Delegate.HelperObjectField // castclass Delegate[] // stloc delegateArrayLocal codeStream.EmitLdArg(0); codeStream.Emit(ILOpcode.ldfld, emitter.NewToken(HelperObjectField)); codeStream.Emit(ILOpcode.castclass, emitter.NewToken(invocationListArrayType)); codeStream.EmitStLoc(delegateArrayLocal); // Fill in invocationCountLocal // int invocationCountLocal = this.m_extraFunctionPointerOrData // ldarg.0 (this pointer) // ldfld Delegate.m_extraFunctionPointerOrData // stloc invocationCountLocal codeStream.EmitLdArg(0); codeStream.Emit(ILOpcode.ldfld, emitter.NewToken(ExtraFunctionPointerOrDataField)); codeStream.EmitStLoc(invocationCountLocal); // Fill in iteratorLocal // int iteratorLocal = 0; // ldc.0 // stloc iteratorLocal codeStream.EmitLdc(0); codeStream.EmitStLoc(iteratorLocal); // Loop across every element of the array. ILCodeLabel startOfLoopLabel = emitter.NewCodeLabel(); codeStream.EmitLabel(startOfLoopLabel); // Implement as do/while loop. We only have this stub in play if we're in the multicast situation // Find the delegate to call // Delegate = delegateToCallLocal = delegateArrayLocal[iteratorLocal]; // ldloc delegateArrayLocal // ldloc iteratorLocal // ldelem System.Delegate // stloc delegateToCallLocal codeStream.EmitLdLoc(delegateArrayLocal); codeStream.EmitLdLoc(iteratorLocal); codeStream.Emit(ILOpcode.ldelem, emitter.NewToken(SystemDelegateType)); codeStream.EmitStLoc(delegateToCallLocal); // Call the delegate // returnValueLocal = delegateToCallLocal(...); // ldloc delegateToCallLocal // ldfld System.Delegate.m_firstParameter // ldarg 1, n // ldloc delegateToCallLocal // ldfld System.Delegate.m_functionPointer // calli returnValueType thiscall (all the params) // IF there is a return value // stloc returnValueLocal codeStream.EmitLdLoc(delegateToCallLocal); codeStream.Emit(ILOpcode.ldfld, emitter.NewToken(FirstParameterField)); for (int i = 0; i < Signature.Length; i++) { codeStream.EmitLdArg(i + 1); } codeStream.EmitLdLoc(delegateToCallLocal); codeStream.Emit(ILOpcode.ldfld, emitter.NewToken(FunctionPointerField)); codeStream.Emit(ILOpcode.calli, emitter.NewToken(Signature)); if (returnValueLocal != 0) { codeStream.EmitStLoc(returnValueLocal); } // Increment iteratorLocal // ++iteratorLocal; // ldloc iteratorLocal // ldc.i4.1 // add // stloc iteratorLocal codeStream.EmitLdLoc(iteratorLocal); codeStream.EmitLdc(1); codeStream.Emit(ILOpcode.add); codeStream.EmitStLoc(iteratorLocal); // Check to see if the loop is done codeStream.EmitLdLoc(invocationCountLocal); codeStream.EmitLdLoc(iteratorLocal); codeStream.Emit(ILOpcode.bne_un, startOfLoopLabel); // Return to caller. If the delegate has a return value, be certain to return that. // return returnValueLocal; // ldloc returnValueLocal // ret if (returnValueLocal != 0) { codeStream.EmitLdLoc(returnValueLocal); } codeStream.Emit(ILOpcode.ret); return(emitter.Link(this)); }
/// <summary> /// Generates a calli sequence that is aware of fat function pointers and can unwrap them into /// a function pointer + instantiation argument if necessary. /// </summary> public static void EmitTransformedCalli(ILEmitter emitter, ILCodeStream codestream, MethodSignature targetSignature) { TypeSystemContext context = targetSignature.ReturnType.Context; int thisPointerParamDelta = 0; if (!targetSignature.IsStatic) { thisPointerParamDelta = 1; } // Start by saving the pointer to call and all the args into locals ILLocalVariable vPointerToCall = emitter.NewLocal(context.GetWellKnownType(WellKnownType.IntPtr)); codestream.EmitStLoc(vPointerToCall); ILLocalVariable[] vParameters = new ILLocalVariable[targetSignature.Length + thisPointerParamDelta]; for (int i = thisPointerParamDelta; i < vParameters.Length; i++) { vParameters[vParameters.Length - i - 1 + thisPointerParamDelta] = emitter.NewLocal(targetSignature[targetSignature.Length - (i - thisPointerParamDelta) - 1]); codestream.EmitStLoc(vParameters[vParameters.Length - i - 1 + thisPointerParamDelta]); } if (!targetSignature.IsStatic) { vParameters[0] = emitter.NewLocal(context.GetWellKnownType(WellKnownType.Object)); codestream.EmitStLoc(vParameters[0]); } // Is this a fat pointer? codestream.EmitLdLoc(vPointerToCall); Debug.Assert(((FatFunctionPointerConstants.Offset - 1) & FatFunctionPointerConstants.Offset) == 0); codestream.EmitLdc(FatFunctionPointerConstants.Offset); codestream.Emit(ILOpcode.and); ILCodeLabel notAFatPointer = emitter.NewCodeLabel(); codestream.Emit(ILOpcode.brfalse, notAFatPointer); // // Fat pointer case // codestream.EmitLdLoc(vPointerToCall); codestream.EmitLdc(FatFunctionPointerConstants.Offset); codestream.Emit(ILOpcode.sub); // Get the pointer to call from the fat pointer codestream.Emit(ILOpcode.dup); codestream.Emit(ILOpcode.ldind_i); codestream.EmitStLoc(vPointerToCall); // Get the instantiation argument codestream.EmitLdc(context.Target.PointerSize); codestream.Emit(ILOpcode.add); codestream.Emit(ILOpcode.ldind_i); codestream.Emit(ILOpcode.ldind_i); ILLocalVariable instArg = emitter.NewLocal(context.GetWellKnownType(WellKnownType.IntPtr)); codestream.EmitStLoc(instArg); // Load this int firstRealParameter = 0; if (!targetSignature.IsStatic) { codestream.EmitLdLoc(vParameters[0]); firstRealParameter = 1; } // Load hidden arg codestream.EmitLdLoc(instArg); // Load rest of args for (int i = firstRealParameter; i < vParameters.Length; i++) { codestream.EmitLdLoc(vParameters[i]); } codestream.EmitLdLoc(vPointerToCall); // The signature has a hidden argument TypeDesc[] newParameters = new TypeDesc[targetSignature.Length + 1]; for (int i = 0; i < targetSignature.Length; i++) { newParameters[i + 1] = targetSignature[i]; } newParameters[0] = context.GetWellKnownType(WellKnownType.IntPtr); MethodSignature newMethodSignature = new MethodSignature(targetSignature.Flags, targetSignature.GenericParameterCount, targetSignature.ReturnType, newParameters); codestream.Emit(ILOpcode.calli, emitter.NewToken(newMethodSignature)); ILCodeLabel done = emitter.NewCodeLabel(); codestream.Emit(ILOpcode.br, done); // // Not a fat pointer case // codestream.EmitLabel(notAFatPointer); for (int i = 0; i < vParameters.Length; i++) { codestream.EmitLdLoc(vParameters[i]); } codestream.EmitLdLoc(vPointerToCall); codestream.Emit(ILOpcode.calli, emitter.NewToken(targetSignature)); codestream.EmitLabel(done); // Workaround for https://github.com/dotnet/corert/issues/2073 codestream.Emit(ILOpcode.nop); }
public static MethodIL EmitIL(MethodDesc method) { Debug.Assert(((MetadataType)method.OwningType).Name == "RuntimeHelpers"); string methodName = method.Name; if (methodName == "GetMethodTable") { ILEmitter emit = new ILEmitter(); ILCodeStream codeStream = emit.NewCodeStream(); codeStream.EmitLdArg(0); codeStream.Emit(ILOpcode.ldflda, emit.NewToken(method.Context.SystemModule.GetKnownType("System.Runtime.CompilerServices", "RawData").GetField("Data"))); codeStream.EmitLdc(-method.Context.Target.PointerSize); codeStream.Emit(ILOpcode.add); codeStream.Emit(ILOpcode.ldind_i); codeStream.Emit(ILOpcode.ret); return(emit.Link(method)); } // All the methods handled below are per-instantiation generic methods if (method.Instantiation.Length != 1 || method.IsTypicalMethodDefinition) { return(null); } TypeDesc elementType = method.Instantiation[0]; // Fallback to non-intrinsic implementation for universal generics if (elementType.IsCanonicalSubtype(CanonicalFormKind.Universal)) { return(null); } bool result; if (methodName == "IsReferenceOrContainsReferences") { result = elementType.IsGCPointer || (elementType is DefType defType && defType.ContainsGCPointers); } else if (methodName == "IsReference") { result = elementType.IsGCPointer; } else if (methodName == "IsBitwiseEquatable") { // Ideally we could detect automatically whether a type is trivially equatable // (i.e., its operator == could be implemented via memcmp). But for now we'll // do the simple thing and hardcode the list of types we know fulfill this contract. // n.b. This doesn't imply that the type's CompareTo method can be memcmp-implemented, // as a method like CompareTo may need to take a type's signedness into account. switch (elementType.UnderlyingType.Category) { case TypeFlags.Boolean: case TypeFlags.Byte: case TypeFlags.SByte: case TypeFlags.Char: case TypeFlags.UInt16: case TypeFlags.Int16: case TypeFlags.UInt32: case TypeFlags.Int32: case TypeFlags.UInt64: case TypeFlags.Int64: case TypeFlags.IntPtr: case TypeFlags.UIntPtr: result = true; break; default: var mdType = elementType as MetadataType; if (mdType != null && mdType.Name == "Rune" && mdType.Namespace == "System.Text") { result = true; } else if (mdType != null && mdType.Name == "Char8" && mdType.Namespace == "System") { result = true; } else { result = false; } break; } } else { return(null); } ILOpcode opcode = result ? ILOpcode.ldc_i4_1 : ILOpcode.ldc_i4_0; return(new ILStubMethodIL(method, new byte[] { (byte)opcode, (byte)ILOpcode.ret }, Array.Empty <LocalVariableDefinition>(), Array.Empty <object>())); }
/// <summary> /// Generates a calli sequence that is aware of fat function pointers and can unwrap them into /// a function pointer + instantiation argument if necessary. /// </summary> public static void EmitTransformedCalli(ILEmitter emitter, ILCodeStream codestream, MethodSignature targetSignature) { TypeSystemContext context = targetSignature.ReturnType.Context; int thisPointerParamDelta = 0; if (!targetSignature.IsStatic) thisPointerParamDelta = 1; // Start by saving the pointer to call and all the args into locals ILLocalVariable vPointerToCall = emitter.NewLocal(context.GetWellKnownType(WellKnownType.IntPtr)); codestream.EmitStLoc(vPointerToCall); ILLocalVariable[] vParameters = new ILLocalVariable[targetSignature.Length + thisPointerParamDelta]; for (int i = thisPointerParamDelta; i < vParameters.Length; i++) { vParameters[vParameters.Length - i - 1 + thisPointerParamDelta] = emitter.NewLocal(targetSignature[targetSignature.Length - (i - thisPointerParamDelta) - 1]); codestream.EmitStLoc(vParameters[vParameters.Length - i - 1 + thisPointerParamDelta]); } if (!targetSignature.IsStatic) { vParameters[0] = emitter.NewLocal(context.GetWellKnownType(WellKnownType.Object)); codestream.EmitStLoc(vParameters[0]); } // Is this a fat pointer? codestream.EmitLdLoc(vPointerToCall); Debug.Assert(((FatFunctionPointerConstants.Offset - 1) & FatFunctionPointerConstants.Offset) == 0); codestream.EmitLdc(FatFunctionPointerConstants.Offset); codestream.Emit(ILOpcode.and); ILCodeLabel notAFatPointer = emitter.NewCodeLabel(); codestream.Emit(ILOpcode.brfalse, notAFatPointer); // // Fat pointer case // codestream.EmitLdLoc(vPointerToCall); codestream.EmitLdc(FatFunctionPointerConstants.Offset); codestream.Emit(ILOpcode.sub); // Get the pointer to call from the fat pointer codestream.Emit(ILOpcode.dup); codestream.Emit(ILOpcode.ldind_i); codestream.EmitStLoc(vPointerToCall); // Get the instantiation argument codestream.EmitLdc(context.Target.PointerSize); codestream.Emit(ILOpcode.add); codestream.Emit(ILOpcode.ldind_i); codestream.Emit(ILOpcode.ldind_i); ILLocalVariable instArg = emitter.NewLocal(context.GetWellKnownType(WellKnownType.IntPtr)); codestream.EmitStLoc(instArg); // Load this int firstRealParameter = 0; if (!targetSignature.IsStatic) { codestream.EmitLdLoc(vParameters[0]); firstRealParameter = 1; } // Load hidden arg codestream.EmitLdLoc(instArg); // Load rest of args for (int i = firstRealParameter; i < vParameters.Length; i++) { codestream.EmitLdLoc(vParameters[i]); } codestream.EmitLdLoc(vPointerToCall); // The signature has a hidden argument TypeDesc[] newParameters = new TypeDesc[targetSignature.Length + 1]; for (int i = 0; i < targetSignature.Length; i++) newParameters[i + 1] = targetSignature[i]; newParameters[0] = context.GetWellKnownType(WellKnownType.IntPtr); MethodSignature newMethodSignature = new MethodSignature(targetSignature.Flags, targetSignature.GenericParameterCount, targetSignature.ReturnType, newParameters); codestream.Emit(ILOpcode.calli, emitter.NewToken(newMethodSignature)); ILCodeLabel done = emitter.NewCodeLabel(); codestream.Emit(ILOpcode.br, done); // // Not a fat pointer case // codestream.EmitLabel(notAFatPointer); for (int i = 0; i < vParameters.Length; i++) { codestream.EmitLdLoc(vParameters[i]); } codestream.EmitLdLoc(vPointerToCall); codestream.Emit(ILOpcode.calli, emitter.NewToken(targetSignature)); codestream.EmitLabel(done); }