protected virtual void Scan()

in src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/MethodBodyScanner.cs [346:851]


        protected virtual void Scan(MethodIL methodBody, ref InterproceduralState interproceduralState)
        {
            MethodDesc thisMethod = methodBody.OwningMethod;

            ValueBasicBlockPair?[] locals = new ValueBasicBlockPair?[methodBody.GetLocals().Length];

            Dictionary<int, Stack<StackSlot>> knownStacks = new Dictionary<int, Stack<StackSlot>>();
            Stack<StackSlot>? currentStack = new Stack<StackSlot>(methodBody.MaxStack);

            ScanExceptionInformation(knownStacks, methodBody);

            BasicBlockIterator blockIterator = new BasicBlockIterator(methodBody);

            ReturnValue = default(MultiValue);
            ILReader reader = new ILReader(methodBody.GetILBytes());
            while (reader.HasNext)
            {
                ValidateNoReferenceToReference(locals, methodBody, reader.Offset);
                int curBasicBlock = blockIterator.MoveNext(reader.Offset);

                if (knownStacks.TryGetValue(reader.Offset, out var knownStack))
                {
                    if (currentStack == null)
                    {
                        // The stack copy constructor reverses the stack
                        currentStack = new Stack<StackSlot>(knownStack.Reverse());
                    }
                    else
                    {
                        currentStack = MergeStack(currentStack, knownStack);
                    }
                }

                currentStack ??= new Stack<StackSlot>(methodBody.MaxStack);

                int offset = reader.Offset;
                ILOpcode opcode = reader.ReadILOpcode();

                switch (opcode)
                {
                    case ILOpcode.add:
                    case ILOpcode.add_ovf:
                    case ILOpcode.add_ovf_un:
                    case ILOpcode.and:
                    case ILOpcode.div:
                    case ILOpcode.div_un:
                    case ILOpcode.mul:
                    case ILOpcode.mul_ovf:
                    case ILOpcode.mul_ovf_un:
                    case ILOpcode.or:
                    case ILOpcode.rem:
                    case ILOpcode.rem_un:
                    case ILOpcode.sub:
                    case ILOpcode.sub_ovf:
                    case ILOpcode.sub_ovf_un:
                    case ILOpcode.xor:
                    case ILOpcode.cgt:
                    case ILOpcode.cgt_un:
                    case ILOpcode.clt:
                    case ILOpcode.clt_un:
                    case ILOpcode.shl:
                    case ILOpcode.shr:
                    case ILOpcode.shr_un:
                    case ILOpcode.ceq:
                        PopUnknown(currentStack, 2, methodBody, offset);
                        PushUnknown(currentStack);
                        reader.Skip(opcode);
                        break;

                    case ILOpcode.dup:
                        currentStack.Push(currentStack.Peek());
                        break;

                    case ILOpcode.ldnull:
                        currentStack.Push(new StackSlot(NullValue.Instance));
                        break;


                    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:
                        {
                            int value = opcode - ILOpcode.ldc_i4_0;
                            ConstIntValue civ = new ConstIntValue(value);
                            StackSlot slot = new StackSlot(civ);
                            currentStack.Push(slot);
                        }
                        break;

                    case ILOpcode.ldc_i4_m1:
                        {
                            ConstIntValue civ = new ConstIntValue(-1);
                            StackSlot slot = new StackSlot(civ);
                            currentStack.Push(slot);
                        }
                        break;

                    case ILOpcode.ldc_i4:
                        {
                            int value = (int)reader.ReadILUInt32();
                            ConstIntValue civ = new ConstIntValue(value);
                            StackSlot slot = new StackSlot(civ);
                            currentStack.Push(slot);
                        }
                        break;

                    case ILOpcode.ldc_i4_s:
                        {
                            int value = (sbyte)reader.ReadILByte();
                            ConstIntValue civ = new ConstIntValue(value);
                            StackSlot slot = new StackSlot(civ);
                            currentStack.Push(slot);
                        }
                        break;

                    case ILOpcode.arglist:
                    case ILOpcode.sizeof_:
                    case ILOpcode.ldc_i8:
                    case ILOpcode.ldc_r4:
                    case ILOpcode.ldc_r8:
                        PushUnknown(currentStack);
                        reader.Skip(opcode);
                        break;

                    case ILOpcode.ldftn:
                        {
                            if (methodBody.GetObject(reader.ReadILToken()) is MethodDesc methodOperand)
                            {
                                HandleMethodTokenAccess(methodBody, offset, methodOperand);
                                TrackNestedFunctionReference(methodOperand, ref interproceduralState);
                            }

                            PushUnknown(currentStack);
                        }
                        break;

                    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:
                        ScanLdarg(opcode, opcode switch
                        {
                            ILOpcode.ldarg => reader.ReadILUInt16(),
                            ILOpcode.ldarga => reader.ReadILUInt16(),
                            ILOpcode.ldarg_s => reader.ReadILByte(),
                            ILOpcode.ldarga_s => reader.ReadILByte(),
                            _ => opcode - ILOpcode.ldarg_0
                        }, currentStack, thisMethod);
                        break;

                    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:
                        ScanLdloc(methodBody, opcode, opcode switch
                        {
                            ILOpcode.ldloc => reader.ReadILUInt16(),
                            ILOpcode.ldloca => reader.ReadILUInt16(),
                            ILOpcode.ldloc_s => reader.ReadILByte(),
                            ILOpcode.ldloca_s => reader.ReadILByte(),
                            _ => opcode - ILOpcode.ldloc_0
                        }, currentStack, locals);
                        ValidateNoReferenceToReference(locals, methodBody, offset);
                        break;

                    case ILOpcode.ldstr:
                        {
                            StackSlot slot = new StackSlot(new KnownStringValue((string)methodBody.GetObject(reader.ReadILToken())));
                            currentStack.Push(slot);
                        }
                        break;

                    case ILOpcode.ldtoken:
                        object obj = methodBody.GetObject(reader.ReadILToken());
                        ScanLdtoken(methodBody, offset, obj, currentStack);
                        break;

                    case ILOpcode.ldvirtftn:
                        {
                            if (methodBody.GetObject(reader.ReadILToken()) is MethodDesc methodOperand)
                            {
                                HandleMethodTokenAccess(methodBody, offset, methodOperand);
                            }

                            PopUnknown(currentStack, 1, methodBody, offset);
                            PushUnknown(currentStack);
                        }
                        break;

                    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_u1:
                    case ILOpcode.ldind_u2:
                    case ILOpcode.ldind_u4:
                    case ILOpcode.ldlen:
                    case ILOpcode.localloc:
                    case ILOpcode.refanytype:
                    case ILOpcode.refanyval:
                    case ILOpcode.conv_i1:
                    case ILOpcode.conv_i2:
                    case ILOpcode.conv_i4:
                    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_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_u1:
                    case ILOpcode.conv_u2:
                    case ILOpcode.conv_u4:
                    case ILOpcode.conv_i8:
                    case ILOpcode.conv_ovf_i8:
                    case ILOpcode.conv_ovf_i8_un:
                    case ILOpcode.conv_ovf_u8:
                    case ILOpcode.conv_ovf_u8_un:
                    case ILOpcode.conv_u8:
                    case ILOpcode.conv_i:
                    case ILOpcode.conv_ovf_i:
                    case ILOpcode.conv_ovf_i_un:
                    case ILOpcode.conv_u:
                    case ILOpcode.conv_r_un:
                    case ILOpcode.conv_r4:
                    case ILOpcode.conv_r8:
                    case ILOpcode.ldind_ref:
                    case ILOpcode.ldobj:
                    case ILOpcode.mkrefany:
                    case ILOpcode.unbox:
                    case ILOpcode.unbox_any:
                    case ILOpcode.box:
                    case ILOpcode.neg:
                    case ILOpcode.not:
                        PopUnknown(currentStack, 1, methodBody, offset);
                        PushUnknown(currentStack);
                        reader.Skip(opcode);
                        break;

                    case ILOpcode.isinst:
                    case ILOpcode.castclass:
                        // We can consider a NOP because the value doesn't change.
                        // It might change to NULL, but for the purposes of dataflow analysis
                        // it doesn't hurt much.
                        reader.Skip(opcode);
                        break;

                    case ILOpcode.ldfld:
                    case ILOpcode.ldsfld:
                    case ILOpcode.ldflda:
                    case ILOpcode.ldsflda:
                        ScanLdfld(methodBody, offset, opcode, (FieldDesc)methodBody.GetObject(reader.ReadILToken()), currentStack, ref interproceduralState);
                        break;

                    case ILOpcode.newarr:
                        {
                            StackSlot count = PopUnknown(currentStack, 1, methodBody, offset);
                            var arrayElement = (TypeDesc)methodBody.GetObject(reader.ReadILToken());
                            currentStack.Push(new StackSlot(ArrayValue.Create(count.Value, arrayElement)));
                        }
                        break;

                    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:
                    case ILOpcode.stelem_ref:
                        ScanStelem(offset, currentStack, methodBody, curBasicBlock);
                        reader.Skip(opcode);
                        break;

                    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_u1:
                    case ILOpcode.ldelem_u2:
                    case ILOpcode.ldelem_u4:
                    case ILOpcode.ldelem:
                    case ILOpcode.ldelem_ref:
                    case ILOpcode.ldelema:
                        ScanLdelem(opcode, offset, currentStack, methodBody, curBasicBlock);
                        reader.Skip(opcode);
                        break;

                    case ILOpcode.cpblk:
                    case ILOpcode.initblk:
                        PopUnknown(currentStack, 3, methodBody, offset);
                        reader.Skip(opcode);
                        break;

                    case ILOpcode.stfld:
                    case ILOpcode.stsfld:
                        ScanStfld(methodBody, offset, opcode, (FieldDesc)methodBody.GetObject(reader.ReadILToken()), currentStack, locals, ref interproceduralState);
                        break;

                    case ILOpcode.cpobj:
                        PopUnknown(currentStack, 2, methodBody, offset);
                        reader.Skip(opcode);
                        break;

                    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:
                        ScanIndirectStore(methodBody, offset, currentStack, locals, curBasicBlock, ref interproceduralState);
                        ValidateNoReferenceToReference(locals, methodBody, offset);
                        reader.Skip(opcode);
                        break;

                    case ILOpcode.initobj:
                    case ILOpcode.pop:
                        PopUnknown(currentStack, 1, methodBody, offset);
                        reader.Skip(opcode);
                        break;

                    case ILOpcode.starg:
                    case ILOpcode.starg_s:
                        ScanStarg(methodBody, offset, opcode == ILOpcode.starg ? reader.ReadILUInt16() : reader.ReadILByte(), currentStack);
                        break;

                    case ILOpcode.stloc:
                    case ILOpcode.stloc_s:
                    case ILOpcode.stloc_0:
                    case ILOpcode.stloc_1:
                    case ILOpcode.stloc_2:
                    case ILOpcode.stloc_3:
                        ScanStloc(methodBody, offset, opcode switch
                        {
                            ILOpcode.stloc => reader.ReadILUInt16(),
                            ILOpcode.stloc_s => reader.ReadILByte(),
                            _ => opcode - ILOpcode.stloc_0,
                        }, currentStack, locals, curBasicBlock);
                        ValidateNoReferenceToReference(locals, methodBody, offset);
                        break;

                    case ILOpcode.constrained:
                    case ILOpcode.no:
                    case ILOpcode.readonly_:
                    case ILOpcode.tail:
                    case ILOpcode.unaligned:
                    case ILOpcode.volatile_:
                        reader.Skip(opcode);
                        break;

                    case ILOpcode.brfalse:
                    case ILOpcode.brfalse_s:
                    case ILOpcode.brtrue:
                    case ILOpcode.brtrue_s:
                        PopUnknown(currentStack, 1, methodBody, offset);
                        NewKnownStack(knownStacks, reader.ReadBranchDestination(opcode), currentStack);
                        break;

                    case ILOpcode.calli:
                        {
                            var signature = (MethodSignature)methodBody.GetObject(reader.ReadILToken());
                            if (!signature.IsStatic)
                            {
                                PopUnknown(currentStack, 1, methodBody, offset);
                            }

                            // Pop arguments
                            if (signature.Length > 0)
                                PopUnknown(currentStack, signature.Length, methodBody, offset);

                            // Pop function pointer
                            PopUnknown(currentStack, 1, methodBody, offset);

                            // Push return value
                            if (!signature.ReturnType.IsVoid)
                                PushUnknown(currentStack);
                        }
                        break;

                    case ILOpcode.call:
                    case ILOpcode.callvirt:
                    case ILOpcode.newobj:
                        {
                            MethodDesc methodOperand = (MethodDesc)methodBody.GetObject(reader.ReadILToken());
                            TrackNestedFunctionReference(methodOperand, ref interproceduralState);
                            HandleCall(methodBody, opcode, offset, methodOperand, currentStack, locals, ref interproceduralState, curBasicBlock);
                            ValidateNoReferenceToReference(locals, methodBody, offset);
                        }
                        break;

                    case ILOpcode.jmp:
                        // Not generated by mainstream compilers
                        reader.Skip(opcode);
                        break;

                    case ILOpcode.br:
                    case ILOpcode.br_s:
                        NewKnownStack(knownStacks, reader.ReadBranchDestination(opcode), currentStack);
                        ClearStack(ref currentStack);
                        break;

                    case ILOpcode.leave:
                    case ILOpcode.leave_s:
                        ClearStack(ref currentStack);
                        NewKnownStack(knownStacks, reader.ReadBranchDestination(opcode), new Stack<StackSlot>(methodBody.MaxStack));
                        break;

                    case ILOpcode.endfilter:
                    case ILOpcode.endfinally:
                    case ILOpcode.rethrow:
                    case ILOpcode.throw_:
                        ClearStack(ref currentStack);
                        break;

                    case ILOpcode.ret:
                        {
                            bool hasReturnValue = !methodBody.OwningMethod.Signature.ReturnType.IsVoid;
                            if (currentStack.Count != (hasReturnValue ? 1 : 0))
                            {
                                WarnAboutInvalidILInMethod(methodBody, offset);
                            }
                            if (hasReturnValue)
                            {
                                StackSlot retValue = PopUnknown(currentStack, 1, methodBody, offset);
                                // If the return value is a reference, treat it as the value itself for now
                                // We can handle ref return values better later
                                ReturnValue = MultiValueLattice.Meet(ReturnValue, DereferenceValue(methodBody, offset, retValue.Value, locals, ref interproceduralState));
                                ValidateNoReferenceToReference(locals, methodBody, offset);
                            }
                            ClearStack(ref currentStack);
                            break;
                        }

                    case ILOpcode.switch_:
                        {
                            PopUnknown(currentStack, 1, methodBody, offset);

                            uint count = reader.ReadILUInt32();
                            int jmpBase = reader.Offset + (int)(4 * count);
                            for (uint i = 0; i < count; i++)
                            {
                                NewKnownStack(knownStacks, (int)reader.ReadILUInt32() + jmpBase, currentStack);
                            }
                            break;
                        }

                    case ILOpcode.beq:
                    case ILOpcode.beq_s:
                    case ILOpcode.bne_un:
                    case ILOpcode.bne_un_s:
                    case ILOpcode.bge:
                    case ILOpcode.bge_s:
                    case ILOpcode.bge_un:
                    case ILOpcode.bge_un_s:
                    case ILOpcode.bgt:
                    case ILOpcode.bgt_s:
                    case ILOpcode.bgt_un:
                    case ILOpcode.bgt_un_s:
                    case ILOpcode.ble:
                    case ILOpcode.ble_s:
                    case ILOpcode.ble_un:
                    case ILOpcode.ble_un_s:
                    case ILOpcode.blt:
                    case ILOpcode.blt_s:
                    case ILOpcode.blt_un:
                    case ILOpcode.blt_un_s:
                        PopUnknown(currentStack, 2, methodBody, offset);
                        NewKnownStack(knownStacks, reader.ReadBranchDestination(opcode), currentStack);
                        break;
                    default:
                        reader.Skip(opcode);
                        break;
                }
            }
        }