private void ImportCall(ILOpcode opcode, int token) { MethodDesc callee = (MethodDesc)_methodIL.GetObject(token); if (callee.IsIntrinsic) { if (ImportIntrinsicCall(callee)) { return; } } if (callee.IsPInvoke) { ImportRawPInvoke(callee); return; } // we don't really have virtual call support, but we'll treat it as direct for now if (opcode != ILOpcode.call && opcode != ILOpcode.callvirt) { throw new NotImplementedException(); } HandleCall(callee); }
public override Object GetObject(int token, NotFoundBehavior notFoundBehavior) { Object o = _methodIL.GetObject(token, notFoundBehavior); if (o is MethodDesc) { o = ((MethodDesc)o).InstantiateSignature(_typeInstantiation, _methodInstantiation); } else if (o is TypeDesc) { o = ((TypeDesc)o).InstantiateSignature(_typeInstantiation, _methodInstantiation); } else if (o is FieldDesc) { o = ((FieldDesc)o).InstantiateSignature(_typeInstantiation, _methodInstantiation); } else if (o is MethodSignature) { MethodSignature template = (MethodSignature)o; MethodSignatureBuilder builder = new MethodSignatureBuilder(template); builder.ReturnType = template.ReturnType.InstantiateSignature(_typeInstantiation, _methodInstantiation); for (int i = 0; i < template.Length; i++) { builder[i] = template[i].InstantiateSignature(_typeInstantiation, _methodInstantiation); } o = builder.ToSignature(); } return(o); }
/// <summary> /// Computes the maximum number of items that can be pushed onto the CIL evaluation stack. /// </summary> public static int ComputeMaxStack(this MethodIL methodIL) { const int StackHeightNotSet = Int32.MinValue; byte[] ilbytes = methodIL.GetILBytes(); int currentOffset = 0; int stackHeight = 0; int maxStack = 0; // TODO: Use Span<T> for this and stackalloc the array if reasonably sized int[] stackHeights = new int[ilbytes.Length]; for (int i = 0; i < stackHeights.Length; i++) { stackHeights[i] = StackHeightNotSet; } // Catch and filter clauses have a known non-zero stack height. foreach (ILExceptionRegion region in methodIL.GetExceptionRegions()) { if (region.Kind == ILExceptionRegionKind.Catch) { stackHeights[region.HandlerOffset] = 1; } else if (region.Kind == ILExceptionRegionKind.Filter) { stackHeights[region.FilterOffset] = 1; stackHeights[region.HandlerOffset] = 1; } } while (currentOffset < ilbytes.Length) { ILOpcode opcode = (ILOpcode)ilbytes[currentOffset]; if (opcode == ILOpcode.prefix1) { opcode = 0x100 + (ILOpcode)ilbytes[currentOffset + 1]; } // The stack height could be unknown if the previous instruction // was an unconditional control transfer. // In that case we check if we have a known stack height due to // this instruction being a target of a previous branch or an EH block. if (stackHeight == StackHeightNotSet) { stackHeight = stackHeights[currentOffset]; } // If we still don't know the stack height, ECMA-335 III.1.7.5 // "Backward branch constraint" demands the evaluation stack be empty. if (stackHeight == StackHeightNotSet) { stackHeight = 0; } // Remeber the stack height at this offset. Debug.Assert(stackHeights[currentOffset] == StackHeightNotSet || stackHeights[currentOffset] == stackHeight); stackHeights[currentOffset] = stackHeight; bool isVariableSize = false; switch (opcode) { case ILOpcode.arglist: case ILOpcode.dup: case ILOpcode.ldc_i4: case ILOpcode.ldc_i4_0: case ILOpcode.ldc_i4_1: case ILOpcode.ldc_i4_2: case ILOpcode.ldc_i4_3: case ILOpcode.ldc_i4_4: case ILOpcode.ldc_i4_5: case ILOpcode.ldc_i4_6: case ILOpcode.ldc_i4_7: case ILOpcode.ldc_i4_8: case ILOpcode.ldc_i4_m1: case ILOpcode.ldc_i4_s: case ILOpcode.ldc_i8: case ILOpcode.ldc_r4: case ILOpcode.ldc_r8: case ILOpcode.ldftn: case ILOpcode.ldnull: case ILOpcode.ldsfld: case ILOpcode.ldsflda: case ILOpcode.ldstr: case ILOpcode.ldtoken: case ILOpcode.ldarg: case ILOpcode.ldarg_0: case ILOpcode.ldarg_1: case ILOpcode.ldarg_2: case ILOpcode.ldarg_3: case ILOpcode.ldarg_s: case ILOpcode.ldarga: case ILOpcode.ldarga_s: case ILOpcode.ldloc: case ILOpcode.ldloc_0: case ILOpcode.ldloc_1: case ILOpcode.ldloc_2: case ILOpcode.ldloc_3: case ILOpcode.ldloc_s: case ILOpcode.ldloca: case ILOpcode.ldloca_s: case ILOpcode.sizeof_: stackHeight += 1; break; case ILOpcode.add: case ILOpcode.add_ovf: case ILOpcode.add_ovf_un: case ILOpcode.and: case ILOpcode.ceq: case ILOpcode.cgt: case ILOpcode.cgt_un: case ILOpcode.clt: case ILOpcode.clt_un: case ILOpcode.div: case ILOpcode.div_un: case ILOpcode.initobj: case ILOpcode.ldelem: case ILOpcode.ldelem_i: case ILOpcode.ldelem_i1: case ILOpcode.ldelem_i2: case ILOpcode.ldelem_i4: case ILOpcode.ldelem_i8: case ILOpcode.ldelem_r4: case ILOpcode.ldelem_r8: case ILOpcode.ldelem_ref: case ILOpcode.ldelem_u1: case ILOpcode.ldelem_u2: case ILOpcode.ldelem_u4: case ILOpcode.ldelema: case ILOpcode.mkrefany: case ILOpcode.mul: case ILOpcode.mul_ovf: case ILOpcode.mul_ovf_un: case ILOpcode.or: case ILOpcode.pop: case ILOpcode.rem: case ILOpcode.rem_un: case ILOpcode.shl: case ILOpcode.shr: case ILOpcode.shr_un: case ILOpcode.stsfld: case ILOpcode.sub: case ILOpcode.sub_ovf: case ILOpcode.sub_ovf_un: case ILOpcode.xor: case ILOpcode.starg: case ILOpcode.starg_s: case ILOpcode.stloc: case ILOpcode.stloc_0: case ILOpcode.stloc_1: case ILOpcode.stloc_2: case ILOpcode.stloc_3: case ILOpcode.stloc_s: Debug.Assert(stackHeight > 0); stackHeight -= 1; break; case ILOpcode.throw_: Debug.Assert(stackHeight > 0); stackHeight = StackHeightNotSet; break; case ILOpcode.br: case ILOpcode.leave: case ILOpcode.brfalse: case ILOpcode.brtrue: case ILOpcode.beq: case ILOpcode.bge: case ILOpcode.bge_un: case ILOpcode.bgt: case ILOpcode.bgt_un: case ILOpcode.ble: case ILOpcode.ble_un: case ILOpcode.blt: case ILOpcode.blt_un: case ILOpcode.bne_un: { int target = currentOffset + ReadInt32(ilbytes, currentOffset + 1) + 5; int adjustment; bool isConditional; if (opcode == ILOpcode.br || opcode == ILOpcode.leave) { isConditional = false; adjustment = 0; } else if (opcode == ILOpcode.brfalse || opcode == ILOpcode.brtrue) { isConditional = true; adjustment = 1; } else { isConditional = true; adjustment = 2; } Debug.Assert(stackHeight >= adjustment); stackHeight -= adjustment; Debug.Assert(stackHeights[target] == StackHeightNotSet || stackHeights[target] == stackHeight); // Forward branch carries information about stack height at a future // offset. We need to remember it. if (target > currentOffset) { stackHeights[target] = stackHeight; } if (!isConditional) { stackHeight = StackHeightNotSet; } } break; case ILOpcode.br_s: case ILOpcode.leave_s: case ILOpcode.brfalse_s: case ILOpcode.brtrue_s: case ILOpcode.beq_s: case ILOpcode.bge_s: case ILOpcode.bge_un_s: case ILOpcode.bgt_s: case ILOpcode.bgt_un_s: case ILOpcode.ble_s: case ILOpcode.ble_un_s: case ILOpcode.blt_s: case ILOpcode.blt_un_s: case ILOpcode.bne_un_s: { int target = currentOffset + (sbyte)ilbytes[currentOffset + 1] + 2; int adjustment; bool isConditional; if (opcode == ILOpcode.br_s || opcode == ILOpcode.leave_s) { isConditional = false; adjustment = 0; } else if (opcode == ILOpcode.brfalse_s || opcode == ILOpcode.brtrue_s) { isConditional = true; adjustment = 1; } else { isConditional = true; adjustment = 2; } Debug.Assert(stackHeight >= adjustment); stackHeight -= adjustment; Debug.Assert(stackHeights[target] == StackHeightNotSet || stackHeights[target] == stackHeight); // Forward branch carries information about stack height at a future // offset. We need to remember it. if (target > currentOffset) { stackHeights[target] = stackHeight; } if (!isConditional) { stackHeight = StackHeightNotSet; } } break; case ILOpcode.call: case ILOpcode.calli: case ILOpcode.callvirt: case ILOpcode.newobj: { int token = ReadILToken(ilbytes, currentOffset + 1); object obj = methodIL.GetObject(token); MethodSignature sig = obj is MethodSignature ? (MethodSignature)obj : ((MethodDesc)obj).Signature; int adjustment = sig.Length; if (opcode == ILOpcode.newobj) { adjustment--; } else { if (opcode == ILOpcode.calli) { adjustment++; } if (!sig.IsStatic) { adjustment++; } if (!sig.ReturnType.IsVoid) { adjustment--; } } Debug.Assert(stackHeight >= adjustment); stackHeight -= adjustment; } break; case ILOpcode.ret: { bool hasReturnValue = !methodIL.OwningMethod.Signature.ReturnType.IsVoid; if (hasReturnValue) { stackHeight -= 1; } Debug.Assert(stackHeight == 0); stackHeight = StackHeightNotSet; } break; case ILOpcode.cpobj: case ILOpcode.stfld: case ILOpcode.stind_i: case ILOpcode.stind_i1: case ILOpcode.stind_i2: case ILOpcode.stind_i4: case ILOpcode.stind_i8: case ILOpcode.stind_r4: case ILOpcode.stind_r8: case ILOpcode.stind_ref: case ILOpcode.stobj: Debug.Assert(stackHeight > 1); stackHeight -= 2; break; case ILOpcode.cpblk: case ILOpcode.initblk: case ILOpcode.stelem: case ILOpcode.stelem_i: case ILOpcode.stelem_i1: case ILOpcode.stelem_i2: case ILOpcode.stelem_i4: case ILOpcode.stelem_i8: case ILOpcode.stelem_r4: case ILOpcode.stelem_r8: case ILOpcode.stelem_ref: Debug.Assert(stackHeight > 2); stackHeight -= 3; break; case ILOpcode.break_: case ILOpcode.constrained: case ILOpcode.no: case ILOpcode.nop: case ILOpcode.readonly_: case ILOpcode.tail: case ILOpcode.unaligned: case ILOpcode.volatile_: break; case ILOpcode.endfilter: Debug.Assert(stackHeight > 0); stackHeight = StackHeightNotSet; break; case ILOpcode.jmp: case ILOpcode.rethrow: case ILOpcode.endfinally: stackHeight = StackHeightNotSet; break; case ILOpcode.box: case ILOpcode.castclass: case ILOpcode.ckfinite: case ILOpcode.conv_i: case ILOpcode.conv_i1: case ILOpcode.conv_i2: case ILOpcode.conv_i4: case ILOpcode.conv_i8: case ILOpcode.conv_ovf_i: case ILOpcode.conv_ovf_i_un: case ILOpcode.conv_ovf_i1: case ILOpcode.conv_ovf_i1_un: case ILOpcode.conv_ovf_i2: case ILOpcode.conv_ovf_i2_un: case ILOpcode.conv_ovf_i4: case ILOpcode.conv_ovf_i4_un: case ILOpcode.conv_ovf_i8: case ILOpcode.conv_ovf_i8_un: case ILOpcode.conv_ovf_u: case ILOpcode.conv_ovf_u_un: case ILOpcode.conv_ovf_u1: case ILOpcode.conv_ovf_u1_un: case ILOpcode.conv_ovf_u2: case ILOpcode.conv_ovf_u2_un: case ILOpcode.conv_ovf_u4: case ILOpcode.conv_ovf_u4_un: case ILOpcode.conv_ovf_u8: case ILOpcode.conv_ovf_u8_un: case ILOpcode.conv_r_un: case ILOpcode.conv_r4: case ILOpcode.conv_r8: case ILOpcode.conv_u: case ILOpcode.conv_u1: case ILOpcode.conv_u2: case ILOpcode.conv_u4: case ILOpcode.conv_u8: case ILOpcode.isinst: case ILOpcode.ldfld: case ILOpcode.ldflda: case ILOpcode.ldind_i: case ILOpcode.ldind_i1: case ILOpcode.ldind_i2: case ILOpcode.ldind_i4: case ILOpcode.ldind_i8: case ILOpcode.ldind_r4: case ILOpcode.ldind_r8: case ILOpcode.ldind_ref: case ILOpcode.ldind_u1: case ILOpcode.ldind_u2: case ILOpcode.ldind_u4: case ILOpcode.ldlen: case ILOpcode.ldobj: case ILOpcode.ldvirtftn: case ILOpcode.localloc: case ILOpcode.neg: case ILOpcode.newarr: case ILOpcode.not: case ILOpcode.refanytype: case ILOpcode.refanyval: case ILOpcode.unbox: case ILOpcode.unbox_any: Debug.Assert(stackHeight > 0); break; case ILOpcode.switch_: Debug.Assert(stackHeight > 0); isVariableSize = true; stackHeight -= 1; currentOffset += 1 + (ReadInt32(ilbytes, currentOffset + 1) * 4) + 4; break; default: Debug.Fail("Unknown instruction"); break; } if (!isVariableSize) { currentOffset += opcode.GetSize(); } maxStack = Math.Max(maxStack, stackHeight); } return(maxStack); }
private TypeDesc ResolveTypeToken(int token) { return((TypeDesc)_methodIL.GetObject(token)); }
private void StartImportingBasicBlock(BasicBlock basicBlock) { // Import all associated EH regions foreach (ExceptionRegion ehRegion in _exceptionRegions) { ILExceptionRegion region = ehRegion.ILRegion; if (region.TryOffset == basicBlock.StartOffset) { MarkBasicBlock(_basicBlocks[region.HandlerOffset]); if (region.Kind == ILExceptionRegionKind.Filter) { MarkBasicBlock(_basicBlocks[region.FilterOffset]); } // Once https://github.com/dotnet/corert/issues/3460 is done, this should be deleted. // Throwing InvalidProgram is not great, but we want to do *something* if this happens // because doing nothing means problems at runtime. This is not worth piping a // a new exception with a fancy message for. if (region.Kind == ILExceptionRegionKind.Catch) { TypeDesc catchType = (TypeDesc)_methodIL.GetObject(region.ClassToken); if (catchType.IsRuntimeDeterminedSubtype) { ThrowHelper.ThrowInvalidProgramException(); } } } } _currentInstructionOffset = -1; _previousInstructionOffset = -1; }
private void ImportCasting(ILOpcode opcode, int token) { TypeDesc type = (TypeDesc)_methodIL.GetObject(token); // Nullable needs to be unwrapped if (type.IsNullable) { type = type.Instantiation[0]; } if (type.IsRuntimeDeterminedSubtype) { _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.TypeHandle, type), "IsInst/CastClass"); } else { ReadyToRunHelperId helperId; if (opcode == ILOpcode.isinst) { helperId = ReadyToRunHelperId.IsInstanceOf; } else { Debug.Assert(opcode == ILOpcode.castclass); helperId = ReadyToRunHelperId.CastClass; } _dependencies.Add(_factory.ReadyToRunHelper(helperId, type), "IsInst/CastClass"); } }
private void ImportCall(ILOpcode opcode, int token) { // We get both the canonical and runtime determined form - JitInterface mostly operates // on the canonical form. var runtimeDeterminedMethod = (MethodDesc)_methodIL.GetObject(token); var method = (MethodDesc)_canonMethodIL.GetObject(token); if (method.IsRawPInvoke()) { // Raw P/invokes don't have any dependencies. return; } string reason = null; switch (opcode) { case ILOpcode.newobj: reason = "newobj"; break; case ILOpcode.call: reason = "call"; break; case ILOpcode.callvirt: reason = "callvirt"; break; case ILOpcode.ldftn: reason = "ldftn"; break; case ILOpcode.ldvirtftn: reason = "ldvirtftn"; break; default: Debug.Assert(false); break; } if (opcode == ILOpcode.newobj) { TypeDesc owningType = runtimeDeterminedMethod.OwningType; if (owningType.IsString) { // String .ctor handled specially below } else if (owningType.IsGCPointer) { if (owningType.IsRuntimeDeterminedSubtype) { _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.TypeHandle, owningType), reason); } else { _dependencies.Add(_factory.ConstructedTypeSymbol(owningType), reason); } if (owningType.IsMdArray) { _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.NewMultiDimArr_NonVarArg), reason); return; } else { _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.NewObject), reason); } } if (owningType.IsDelegate) { // If this is a verifiable delegate construction sequence, the previous instruction is a ldftn/ldvirtftn if (_previousInstructionOffset >= 0 && _ilBytes[_previousInstructionOffset] == (byte)ILOpcode.prefix1) { // TODO: for ldvirtftn we need to also check for the `dup` instruction, otherwise this is a normal newobj. ILOpcode previousOpcode = (ILOpcode)(0x100 + _ilBytes[_previousInstructionOffset + 1]); if (previousOpcode == ILOpcode.ldvirtftn || previousOpcode == ILOpcode.ldftn) { int delTargetToken = ReadILTokenAt(_previousInstructionOffset + 2); var delTargetMethod = (MethodDesc)_methodIL.GetObject(delTargetToken); TypeDesc canonDelegateType = method.OwningType.ConvertToCanonForm(CanonicalFormKind.Specific); DelegateCreationInfo info = _compilation.GetDelegateCtor(canonDelegateType, delTargetMethod, previousOpcode == ILOpcode.ldvirtftn); if (info.NeedsRuntimeLookup) { _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.DelegateCtor, info), reason); } else { _dependencies.Add(_factory.ReadyToRunHelper(ReadyToRunHelperId.DelegateCtor, info), reason); } return; } } } } if (method.OwningType.IsDelegate && method.Name == "Invoke" && opcode != ILOpcode.ldftn && opcode != ILOpcode.ldvirtftn) { // This call is expanded as an intrinsic; it's not an actual function call. // Before codegen realizes this is an intrinsic, it might still ask questions about // the vtable of this virtual method, so let's make sure it's marked in the scanner's // dependency graph. _dependencies.Add(_factory.VTable(method.OwningType), reason); return; } if (method.IsIntrinsic) { if (IsRuntimeHelpersInitializeArray(method)) { if (_previousInstructionOffset >= 0 && _ilBytes[_previousInstructionOffset] == (byte)ILOpcode.ldtoken) { return; } } if (IsRuntimeTypeHandleGetValueInternal(method)) { if (_previousInstructionOffset >= 0 && _ilBytes[_previousInstructionOffset] == (byte)ILOpcode.ldtoken) { return; } } if (IsActivatorDefaultConstructorOf(method)) { if (runtimeDeterminedMethod.IsRuntimeDeterminedExactMethod) { _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.DefaultConstructor, runtimeDeterminedMethod.Instantiation[0]), reason); } else { MethodDesc ctor = method.Instantiation[0].GetDefaultConstructor(); if (ctor == null) { MetadataType activatorType = _compilation.TypeSystemContext.SystemModule.GetKnownType("System", "Activator"); MetadataType classWithMissingCtor = activatorType.GetKnownNestedType("ClassWithMissingConstructor"); ctor = classWithMissingCtor.GetParameterlessConstructor(); } _dependencies.Add(_factory.CanonicalEntrypoint(ctor), reason); } return; } if (method.OwningType.IsByReferenceOfT && (method.IsConstructor || method.Name == "get_Value")) { return; } if (IsEETypePtrOf(method)) { if (runtimeDeterminedMethod.IsRuntimeDeterminedExactMethod) { _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.TypeHandle, runtimeDeterminedMethod.Instantiation[0]), reason); } else { _dependencies.Add(_factory.ConstructedTypeSymbol(method.Instantiation[0]), reason); } return; } } TypeDesc exactType = method.OwningType; if (method.IsNativeCallable && (opcode != ILOpcode.ldftn && opcode != ILOpcode.ldvirtftn)) { ThrowHelper.ThrowInvalidProgramException(ExceptionStringID.InvalidProgramNativeCallable, method); } bool resolvedConstraint = false; bool forceUseRuntimeLookup = false; MethodDesc methodAfterConstraintResolution = method; if (_constrained != null) { // We have a "constrained." call. Try a partial resolve of the constraint call. Note that this // will not necessarily resolve the call exactly, since we might be compiling // shared generic code - it may just resolve it to a candidate suitable for // JIT compilation, and require a runtime lookup for the actual code pointer // to call. TypeDesc constrained = _constrained; if (constrained.IsRuntimeDeterminedSubtype) { constrained = constrained.ConvertToCanonForm(CanonicalFormKind.Specific); } MethodDesc directMethod = constrained.GetClosestDefType().TryResolveConstraintMethodApprox(method.OwningType, method, out forceUseRuntimeLookup); if (directMethod == null && constrained.IsEnum) { // Constrained calls to methods on enum methods resolve to System.Enum's methods. System.Enum is a reference // type though, so we would fail to resolve and box. We have a special path for those to avoid boxing. directMethod = _compilation.TypeSystemContext.TryResolveConstrainedEnumMethod(constrained, method); } if (directMethod != null) { // Either // 1. no constraint resolution at compile time (!directMethod) // OR 2. no code sharing lookup in call // OR 3. we have have resolved to an instantiating stub methodAfterConstraintResolution = directMethod; Debug.Assert(!methodAfterConstraintResolution.OwningType.IsInterface); resolvedConstraint = true; exactType = constrained; } else if (constrained.IsValueType) { // We'll need to box `this`. Note we use _constrained here, because the other one is canonical. AddBoxingDependencies(_constrained, reason); } } MethodDesc targetMethod = methodAfterConstraintResolution; bool exactContextNeedsRuntimeLookup; if (targetMethod.HasInstantiation) { exactContextNeedsRuntimeLookup = targetMethod.IsSharedByGenericInstantiations; } else { exactContextNeedsRuntimeLookup = exactType.IsCanonicalSubtype(CanonicalFormKind.Any); } // // Determine whether to perform direct call // bool directCall = false; if (targetMethod.Signature.IsStatic) { // Static methods are always direct calls directCall = true; } else if (targetMethod.OwningType.IsInterface) { // Force all interface calls to be interpreted as if they are virtual. directCall = false; } else if ((opcode != ILOpcode.callvirt && opcode != ILOpcode.ldvirtftn) || resolvedConstraint) { directCall = true; } else { if (!targetMethod.IsVirtual || targetMethod.IsFinal || targetMethod.OwningType.IsSealed()) { directCall = true; } } bool allowInstParam = opcode != ILOpcode.ldvirtftn && opcode != ILOpcode.ldftn; if (directCall && !allowInstParam && targetMethod.GetCanonMethodTarget(CanonicalFormKind.Specific).RequiresInstArg()) { // Needs a single address to call this method but the method needs a hidden argument. // We need a fat function pointer for this that captures both things. if (exactContextNeedsRuntimeLookup) { _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.MethodEntry, runtimeDeterminedMethod), reason); } else { _dependencies.Add(_factory.FatFunctionPointer(runtimeDeterminedMethod), reason); } } else if (directCall) { bool referencingArrayAddressMethod = false; if (targetMethod.IsIntrinsic) { // If this is an intrinsic method with a callsite-specific expansion, this will replace // the method with a method the intrinsic expands into. If it's not the special intrinsic, // method stays unchanged. targetMethod = _compilation.ExpandIntrinsicForCallsite(targetMethod, _canonMethod); // Array address method requires special dependency tracking. referencingArrayAddressMethod = targetMethod.IsArrayAddressMethod(); } MethodDesc concreteMethod = targetMethod; targetMethod = targetMethod.GetCanonMethodTarget(CanonicalFormKind.Specific); if (targetMethod.IsConstructor && targetMethod.OwningType.IsString) { _dependencies.Add(_factory.StringAllocator(targetMethod), reason); } else if (exactContextNeedsRuntimeLookup) { if (targetMethod.IsSharedByGenericInstantiations && !resolvedConstraint && !referencingArrayAddressMethod) { ISymbolNode instParam = null; if (targetMethod.RequiresInstMethodDescArg()) { instParam = GetGenericLookupHelper(ReadyToRunHelperId.MethodDictionary, runtimeDeterminedMethod); } else if (targetMethod.RequiresInstMethodTableArg()) { bool hasHiddenParameter = true; if (targetMethod.IsIntrinsic) { if (_factory.TypeSystemContext.IsSpecialUnboxingThunkTargetMethod(targetMethod)) { hasHiddenParameter = false; } } if (hasHiddenParameter) { instParam = GetGenericLookupHelper(ReadyToRunHelperId.TypeHandle, runtimeDeterminedMethod.OwningType); } } if (instParam != null) { _dependencies.Add(instParam, reason); } _dependencies.Add(_factory.CanonicalEntrypoint(targetMethod), reason); } else { Debug.Assert(!forceUseRuntimeLookup); _dependencies.Add(_factory.MethodEntrypoint(targetMethod), reason); if (targetMethod.RequiresInstMethodTableArg() && resolvedConstraint) { if (_constrained.IsRuntimeDeterminedSubtype) { _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.TypeHandle, _constrained), reason); } else { _dependencies.Add(_factory.ConstructedTypeSymbol(_constrained), reason); } } if (referencingArrayAddressMethod && !_isReadOnly) { // Address method is special - it expects an instantiation argument, unless a readonly prefix was applied. _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.TypeHandle, runtimeDeterminedMethod.OwningType), reason); } } } else { ISymbolNode instParam = null; if (targetMethod.RequiresInstMethodDescArg()) { instParam = _compilation.NodeFactory.MethodGenericDictionary(concreteMethod); } else if (targetMethod.RequiresInstMethodTableArg() || (referencingArrayAddressMethod && !_isReadOnly)) { // Ask for a constructed type symbol because we need the vtable to get to the dictionary instParam = _compilation.NodeFactory.ConstructedTypeSymbol(concreteMethod.OwningType); } if (instParam != null) { _dependencies.Add(instParam, reason); } _dependencies.Add(_compilation.NodeFactory.MethodEntrypoint(targetMethod), reason); } } else if (method.HasInstantiation) { // Generic virtual method call if (exactContextNeedsRuntimeLookup) { _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.MethodHandle, runtimeDeterminedMethod), reason); } else { _dependencies.Add(_factory.RuntimeMethodHandle(runtimeDeterminedMethod), reason); } _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.GVMLookupForSlot), reason); } else if (method.OwningType.IsInterface) { if (exactContextNeedsRuntimeLookup) { _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.VirtualDispatchCell, runtimeDeterminedMethod), reason); } else { _dependencies.Add(_factory.InterfaceDispatchCell(method), reason); } } else if (_compilation.HasFixedSlotVTable(method.OwningType)) { // No dependencies: virtual call through the vtable } else { MethodDesc slotDefiningMethod = targetMethod.IsNewSlot ? targetMethod : MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(targetMethod); _dependencies.Add(_factory.VirtualMethodUse(slotDefiningMethod), reason); } }