Example #1
0
        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);
        }
Example #2
0
        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);
        }
Example #3
0
        /// <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);
        }
Example #4
0
 private TypeDesc ResolveTypeToken(int token)
 {
     return((TypeDesc)_methodIL.GetObject(token));
 }
Example #5
0
        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;
        }
Example #6
0
        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");
            }
        }
Example #7
0
        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);
            }
        }