/// <summary>
        /// Subset of <see cref="Scan(ref DependencyList, NodeFactory, MethodIL)"/> that only deals with Marshal.SizeOf.
        /// </summary>
        public static void ScanMarshalOnly(ref DependencyList list, NodeFactory factory, MethodIL methodIL)
        {
            ILReader reader = new ILReader(methodIL.GetILBytes());

            Tracker tracker = new Tracker(methodIL);

            while (reader.HasNext)
            {
                ILOpcode opcode = reader.ReadILOpcode();
                switch (opcode)
                {
                case ILOpcode.ldtoken:
                    tracker.TrackLdTokenToken(reader.ReadILToken());
                    break;

                case ILOpcode.call:
                    var method = methodIL.GetObject(reader.ReadILToken()) as MethodDesc;
                    if (method != null && method.Name == "SizeOf" && IsMarshalSizeOf(method))
                    {
                        TypeDesc type = tracker.GetLastType();
                        if (IsTypeEligibleForMarshalSizeOfTracking(type))
                        {
                            list = list ?? new DependencyList();

                            list.Add(factory.StructMarshallingData((DefType)type), "Marshal.SizeOf");
                        }
                    }
                    break;

                default:
                    reader.Skip(opcode);
                    break;
                }
            }
        }
Beispiel #2
0
        public void TestMDArrayFunctionReading()
        {
            MetadataType mdArrayFunctionResolutionType = _testModule.GetType("", "MDArrayFunctionResolution");
            MethodDesc   methodWithMDArrayUsage        = mdArrayFunctionResolutionType.GetMethods().Single(m => string.Equals(m.Name, "MethodWithUseOfMDArrayFunctions"));
            MethodIL     methodIL  = EcmaMethodIL.Create((EcmaMethod)methodWithMDArrayUsage);
            ILReader     ilReader  = new ILReader(methodIL.GetILBytes());
            int          failures  = 0;
            int          successes = 0;

            while (ilReader.HasNext)
            {
                ILOpcode opcode = ilReader.ReadILOpcode();
                switch (opcode)
                {
                case ILOpcode.call:
                case ILOpcode.newobj:
                    int    token = ilReader.ReadILToken();
                    object tokenReferenceResult = methodIL.GetObject(token, NotFoundBehavior.ReturnNull);
                    if (tokenReferenceResult == null)
                    {
                        failures++;
                        tokenReferenceResult = "null";
                    }
                    else
                    {
                        successes++;
                    }
                    _output.WriteLine($"call {tokenReferenceResult.ToString()}");
                    break;
                }
            }

            Assert.Equal(0, failures);
            Assert.Equal(4, successes);
        }
Beispiel #3
0
        /// <summary>
        /// Find IL offsets at which basic blocks begin.
        /// </summary>
        private static HashSet <int> GetBasicBlockStarts(MethodIL il)
        {
            ILReader      reader   = new ILReader(il.GetILBytes());
            HashSet <int> bbStarts = new HashSet <int>();

            bbStarts.Add(0);
            while (reader.HasNext)
            {
                ILOpcode opc = reader.ReadILOpcode();
                if (opc.IsBranch())
                {
                    int tar = reader.ReadBranchDestination(opc);
                    bbStarts.Add(tar);
                    // Conditional branches can fall through.
                    if (!opc.IsUnconditionalBranch())
                    {
                        bbStarts.Add(reader.Offset);
                    }
                }
                else if (opc == ILOpcode.switch_)
                {
                    uint numCases = reader.ReadILUInt32();
                    int  jmpBase  = reader.Offset + checked ((int)(numCases * 4));
                    // Default case is at jmpBase.
                    bbStarts.Add(jmpBase);

                    for (uint i = 0; i < numCases; i++)
                    {
                        int caseOfs = jmpBase + (int)reader.ReadILUInt32();
                        bbStarts.Add(caseOfs);
                    }
                }
                else if (opc == ILOpcode.ret || opc == ILOpcode.endfinally || opc == ILOpcode.endfilter || opc == ILOpcode.throw_ || opc == ILOpcode.rethrow)
                {
                    if (reader.HasNext)
                    {
                        bbStarts.Add(reader.Offset);
                    }
                }
                else
                {
                    reader.Skip(opc);
                }
            }

            foreach (ILExceptionRegion ehRegion in il.GetExceptionRegions())
            {
                bbStarts.Add(ehRegion.TryOffset);
                bbStarts.Add(ehRegion.TryOffset + ehRegion.TryLength);
                bbStarts.Add(ehRegion.HandlerOffset);
                bbStarts.Add(ehRegion.HandlerOffset + ehRegion.HandlerLength);
                if (ehRegion.Kind.HasFlag(ILExceptionRegionKind.Filter))
                {
                    bbStarts.Add(ehRegion.FilterOffset);
                }
            }

            return(bbStarts);
        }
Beispiel #4
0
            private static bool ScanMethodBodyForFieldAccess(MethodIL body, bool write, out FieldDesc?found)
            {
                // Tries to find the backing field for a property getter/setter.
                // Returns true if this is a method body that we can unambiguously analyze.
                // The found field could still be null if there's no backing store.
                found = null;

                ILReader ilReader = new ILReader(body.GetILBytes());

                while (ilReader.HasNext)
                {
                    ILOpcode opcode = ilReader.ReadILOpcode();
                    switch (opcode)
                    {
                    case ILOpcode.ldsfld when !write:
                    case ILOpcode.ldfld when !write:
                    case ILOpcode.stsfld when write:
                    case ILOpcode.stfld when write:
                    {
                        // This writes/reads multiple fields - can't guess which one is the backing store.
                        // Return failure.
                        if (found != null)
                        {
                            found = null;
                            return(false);
                        }
                        found = (FieldDesc)body.GetObject(ilReader.ReadILToken());
                    }
                    break;

                    default:
                        ilReader.Skip(opcode);
                        break;
                    }
                }

                if (found == null)
                {
                    // Doesn't access any fields. Could be e.g. "Type Foo => typeof(Bar);"
                    // Return success.
                    return(true);
                }

                if (found.OwningType != body.OwningMethod.OwningType ||
                    found.IsStatic != body.OwningMethod.Signature.IsStatic ||
                    !found.HasCustomAttribute("System.Runtime.CompilerServices", "CompilerGeneratedAttribute"))
                {
                    // A couple heuristics to make sure we got the right field.
                    // Return failure.
                    found = null;
                    return(false);
                }

                return(true);
            }
        public static void Scan(ref DependencyList list, NodeFactory factory, MethodIL methodIL)
        {
            ILReader reader = new ILReader(methodIL.GetILBytes());

            Tracker tracker = new Tracker(methodIL);

            // The algorithm here is really primitive: we scan the IL forward in a single pass, remembering
            // the last type/string/token we saw.
            //
            // We then intrinsically recognize a couple methods that consume this information.
            //
            // This has obvious problems since we don't have exact knowledge of the parameters passed
            // (something being in front of a call doesn't mean it's a parameter to the call). But since
            // this is a heuristic, it's okay. We want this to be as fast as possible.
            //
            // The main purposes of this scanner is to make following patterns work:
            //
            // * Enum.GetValues(typeof(Foo)) - this is very common and we need to make sure Foo[] is compiled.
            // * Type.GetType("Foo, Bar").GetMethod("Blah") - framework uses this to work around layering problems.
            // * typeof(Foo<>).MakeGenericType(arg).GetMethod("Blah") - used in e.g. LINQ expressions implementation
            // * Marshal.SizeOf(typeof(Foo)) - very common and we need to make sure interop data is generated

            while (reader.HasNext)
            {
                ILOpcode opcode = reader.ReadILOpcode();
                switch (opcode)
                {
                case ILOpcode.ldstr:
                    tracker.TrackStringToken(reader.ReadILToken());
                    break;

                case ILOpcode.ldtoken:
                    tracker.TrackLdTokenToken(reader.ReadILToken());
                    break;

                case ILOpcode.call:
                case ILOpcode.callvirt:
                    var method = methodIL.GetObject(reader.ReadILToken()) as MethodDesc;
                    if (method != null)
                    {
                        HandleCall(ref list, factory, methodIL, method, ref tracker);
                    }
                    break;

                default:
                    reader.Skip(opcode);
                    break;
                }
            }
        }
Beispiel #6
0
            public int MoveNext(int offset)
            {
                if (_foundEndOfPrevBlock || _methodBranchTargets.Contains(offset))
                {
                    _currentBlockIndex++;
                    _foundEndOfPrevBlock = false;
                }

                var reader = new ILReader(_methodBody.GetILBytes());

                reader.Seek(offset);
                ILOpcode opcode = reader.ReadILOpcode();

                if (opcode.IsControlFlowInstruction())
                {
                    _foundEndOfPrevBlock = true;
                }

                return(CurrentBlockIndex);
            }
Beispiel #7
0
        public static HashSet <int> ComputeBranchTargets(this MethodIL methodBody)
        {
            HashSet <int> branchTargets = new HashSet <int>();
            var           reader        = new ILReader(methodBody.GetILBytes());

            while (reader.HasNext)
            {
                ILOpcode opcode = reader.ReadILOpcode();
                if (opcode >= ILOpcode.br_s && opcode <= ILOpcode.blt_un)
                {
                    branchTargets.Add(reader.ReadBranchDestination(opcode));
                }
                else if (opcode == ILOpcode.switch_)
                {
                    uint count   = reader.ReadILUInt32();
                    int  jmpBase = reader.Offset + (int)(4 * count);
                    for (uint i = 0; i < count; i++)
                    {
                        branchTargets.Add((int)reader.ReadILUInt32() + jmpBase);
                    }
                }
                else
                {
                    reader.Skip(opcode);
                }
            }
            foreach (ILExceptionRegion einfo in methodBody.GetExceptionRegions())
            {
                if (einfo.Kind == ILExceptionRegionKind.Filter)
                {
                    branchTargets.Add(einfo.FilterOffset);
                }
                branchTargets.Add(einfo.HandlerOffset);
            }
            return(branchTargets);
        }
Beispiel #8
0
 public ILStreamReader(MethodIL methodIL)
 {
     _methodIL      = methodIL;
     _ilBytes       = methodIL.GetILBytes();
     _currentOffset = 0;
 }
 public ILStreamReader(MethodIL methodIL)
 {
     _methodIL = methodIL;
     _reader   = new ILReader(methodIL.GetILBytes());
 }
Beispiel #10
0
        public MethodIL GetMethodILWithInlinedSubstitutions(MethodIL method)
        {
            // This attempts to find all basic blocks that are unreachable after applying the substitutions.
            //
            // On a high level, we first find all the basic blocks and instruction boundaries in the IL stream.
            // This is tracked in a sidecar `flags` array that has flags for each byte of the IL stream.
            //
            // Once we have all the basic blocks and instruction boundaries, we do a marking phase to mark
            // the reachable blocks. We use substitutions to tell us what's unreachable. We consider conditional
            // branches "interesting" and whenever we see one, we seek backwards in the IL instruction stream
            // to find the instruction that feeds it. We make sure we don't cross the basic block boundary while
            // doing that. If the conditional instruction is fed by known values (either through the substitutions
            // or because it's an IL constant), we simulate the result of the comparison and only mark
            // the taken branch. We also mark any associated EH regions.
            //
            // The "seek backwards to find what feeds the comparison" only works for a couple known instructions
            // (load constant, call). It can't e.g. skip over arguments to the call.
            //
            // Last step is a sweep - we replace the tail of all unreachable blocks with "br $-2"
            // and nop out the rest. If the basic block is smaller than 2 bytes, we don't touch it.
            // We also eliminate any EH records that correspond to the stubbed out basic block.

            Debug.Assert(method.GetMethodILDefinition() == method);

            ILExceptionRegion[] ehRegions = method.GetExceptionRegions();
            byte[]        methodBytes     = method.GetILBytes();
            OpcodeFlags[] flags           = new OpcodeFlags[methodBytes.Length];

            // Offset 0 is the first basic block
            Stack <int> offsetsToVisit = new Stack <int>();

            offsetsToVisit.Push(0);

            // Basic blocks also start around EH regions
            foreach (ILExceptionRegion ehRegion in ehRegions)
            {
                if (ehRegion.Kind == ILExceptionRegionKind.Filter)
                {
                    offsetsToVisit.Push(ehRegion.FilterOffset);
                }

                offsetsToVisit.Push(ehRegion.HandlerOffset);
            }

            // Identify basic blocks and instruction boundaries
            while (offsetsToVisit.TryPop(out int offset))
            {
                // If this was already visited, we're done
                if (flags[offset] != 0)
                {
                    // Also mark as basic block start in case this was a target of a backwards branch.
                    flags[offset] |= OpcodeFlags.BasicBlockStart;
                    continue;
                }

                flags[offset] |= OpcodeFlags.BasicBlockStart;

                // Read until we reach the end of the basic block
                ILReader reader = new ILReader(methodBytes, offset);
                while (reader.HasNext)
                {
                    offset         = reader.Offset;
                    flags[offset] |= OpcodeFlags.InstructionStart;
                    ILOpcode opcode = reader.ReadILOpcode();
                    if (opcode >= ILOpcode.br_s && opcode <= ILOpcode.blt_un ||
                        opcode == ILOpcode.leave || opcode == ILOpcode.leave_s)
                    {
                        int destination = reader.ReadBranchDestination(opcode);
                        offsetsToVisit.Push(destination);

                        if (opcode != ILOpcode.leave && opcode != ILOpcode.leave_s &&
                            opcode != ILOpcode.br && opcode != ILOpcode.br_s)
                        {
                            // Branches not tested for above are conditional and the flow falls through.
                            offsetsToVisit.Push(reader.Offset);
                        }

                        flags[offset] |= OpcodeFlags.EndBasicBlock;
                    }
                    else if (opcode == ILOpcode.ret ||
                             opcode == ILOpcode.endfilter ||
                             opcode == ILOpcode.endfinally ||
                             opcode == ILOpcode.throw_ ||
                             opcode == ILOpcode.rethrow ||
                             opcode == ILOpcode.jmp)
                    {
                        // Ends basic block.
                        flags[offset] |= OpcodeFlags.EndBasicBlock;

                        reader.Skip(opcode);
                    }
                    else if (opcode == ILOpcode.switch_)
                    {
                        uint count   = reader.ReadILUInt32();
                        int  jmpBase = reader.Offset + (int)(4 * count);
                        for (uint i = 0; i < count; i++)
                        {
                            int destination = (int)reader.ReadILUInt32() + jmpBase;
                            offsetsToVisit.Push(destination);
                        }
                        // We fall through to the next basic block.
                        offsetsToVisit.Push(reader.Offset);
                        flags[offset] |= OpcodeFlags.EndBasicBlock;
                    }
                    else
                    {
                        reader.Skip(opcode);
                    }

                    if ((flags[offset] & OpcodeFlags.EndBasicBlock) != 0)
                    {
                        if (reader.HasNext)
                        {
                            // If the bytes following this basic block are not reachable from anywhere,
                            // the sweeping step would consider them to be part of the last instruction
                            // of the current basic block because of how instruction boundaries are identified.
                            // We wouldn't NOP them out if the current basic block is reachable.
                            //
                            // That's a problem for RyuJIT because RyuJIT looks at these bytes for... reasons.
                            //
                            // We can just do the same thing as RyuJIT and consider those a basic block.
                            offsetsToVisit.Push(reader.Offset);
                        }
                        break;
                    }
                }
            }

            // Mark all reachable basic blocks
            //
            // We also do another round of basic block marking to mark beginning of visible basic blocks
            // after dead branch elimination. This allows us to limit the number of potential small basic blocks
            // that are not interesting (because no code jumps to them anymore), but could prevent us from
            // finishing the process. Unreachable basic blocks smaller than 2 bytes abort the substitution
            // inlining process because we can't neutralize them (turn them into an infinite loop).
            offsetsToVisit.Push(0);
            while (offsetsToVisit.TryPop(out int offset))
            {
                // Mark as a basic block visible after constant propagation.
                flags[offset] |= OpcodeFlags.VisibleBasicBlockStart;

                // If this was already marked, we're done.
                if ((flags[offset] & OpcodeFlags.Mark) != 0)
                {
                    continue;
                }

                ILReader reader = new ILReader(methodBytes, offset);
                while (reader.HasNext)
                {
                    offset         = reader.Offset;
                    flags[offset] |= OpcodeFlags.Mark;
                    ILOpcode opcode = reader.ReadILOpcode();

                    // Mark any applicable EH blocks
                    foreach (ILExceptionRegion ehRegion in ehRegions)
                    {
                        int delta = offset - ehRegion.TryOffset;
                        if (delta >= 0 && delta < ehRegion.TryLength)
                        {
                            if (ehRegion.Kind == ILExceptionRegionKind.Filter)
                            {
                                offsetsToVisit.Push(ehRegion.FilterOffset);
                            }

                            offsetsToVisit.Push(ehRegion.HandlerOffset);

                            // RyuJIT is going to look at this basic block even though it's unreachable.
                            // Consider it visible so that we replace the tail with an endless loop.
                            int handlerEnd = ehRegion.HandlerOffset + ehRegion.HandlerLength;
                            if (handlerEnd < flags.Length)
                            {
                                flags[handlerEnd] |= OpcodeFlags.VisibleBasicBlockStart;
                            }
                        }
                    }

                    // All branches are relevant to basic block tracking
                    if (opcode == ILOpcode.brfalse || opcode == ILOpcode.brfalse_s ||
                        opcode == ILOpcode.brtrue || opcode == ILOpcode.brtrue_s)
                    {
                        int destination = reader.ReadBranchDestination(opcode);
                        if (!TryGetConstantArgument(method, methodBytes, flags, offset, 0, out int constant))
                        {
                            // Can't get the constant - both branches are live.
                            offsetsToVisit.Push(destination);
                            offsetsToVisit.Push(reader.Offset);
                        }
                        else if ((constant == 0 && (opcode == ILOpcode.brfalse || opcode == ILOpcode.brfalse_s)) ||
                                 (constant != 0 && (opcode == ILOpcode.brtrue || opcode == ILOpcode.brtrue_s)))
                        {
                            // Only the "branch taken" is live.
                            // The fallthrough marks the beginning of a visible (but not live) basic block.
                            offsetsToVisit.Push(destination);
                            flags[reader.Offset] |= OpcodeFlags.VisibleBasicBlockStart;
                        }
                        else
                        {
                            // Only fallthrough is live.
                            // The "brach taken" marks the beginning of a visible (but not live) basic block.
                            flags[destination] |= OpcodeFlags.VisibleBasicBlockStart;
                            offsetsToVisit.Push(reader.Offset);
                        }
                    }
                    else if (opcode == ILOpcode.beq || opcode == ILOpcode.beq_s ||
                             opcode == ILOpcode.bne_un || opcode == ILOpcode.bne_un_s)
                    {
                        int destination = reader.ReadBranchDestination(opcode);
                        if (!TryGetConstantArgument(method, methodBytes, flags, offset, 0, out int left) ||
                            !TryGetConstantArgument(method, methodBytes, flags, offset, 1, out int right))
                        {
                            // Can't get the constant - both branches are live.
                            offsetsToVisit.Push(destination);
                            offsetsToVisit.Push(reader.Offset);
                        }
                        else if ((left == right && (opcode == ILOpcode.beq || opcode == ILOpcode.beq_s) ||
                                  (left != right) && (opcode == ILOpcode.bne_un || opcode == ILOpcode.bne_un_s)))
                        {
                            // Only the "branch taken" is live.
                            // The fallthrough marks the beginning of a visible (but not live) basic block.
                            offsetsToVisit.Push(destination);
                            flags[reader.Offset] |= OpcodeFlags.VisibleBasicBlockStart;
                        }
                        else
                        {
                            // Only fallthrough is live.
                            // The "brach taken" marks the beginning of a visible (but not live) basic block.
                            flags[destination] |= OpcodeFlags.VisibleBasicBlockStart;
                            offsetsToVisit.Push(reader.Offset);
                        }
                    }
                    else if (opcode >= ILOpcode.br_s && opcode <= ILOpcode.blt_un ||
                             opcode == ILOpcode.leave || opcode == ILOpcode.leave_s)
                    {
                        int destination = reader.ReadBranchDestination(opcode);
                        offsetsToVisit.Push(destination);
                        if (opcode != ILOpcode.leave && opcode != ILOpcode.leave_s &&
                            opcode != ILOpcode.br && opcode != ILOpcode.br_s)
                        {
                            // Branches not tested for above are conditional and the flow falls through.
                            offsetsToVisit.Push(reader.Offset);
                        }
                        else
                        {
                            // RyuJIT is going to look at this basic block even though it's unreachable.
                            // Consider it visible so that we replace the tail with an endless loop.
                            if (reader.HasNext)
                            {
                                flags[reader.Offset] |= OpcodeFlags.VisibleBasicBlockStart;
                            }
                        }
                    }
                    else if (opcode == ILOpcode.switch_)
                    {
                        uint count   = reader.ReadILUInt32();
                        int  jmpBase = reader.Offset + (int)(4 * count);
                        for (uint i = 0; i < count; i++)
                        {
                            int destination = (int)reader.ReadILUInt32() + jmpBase;
                            offsetsToVisit.Push(destination);
                        }
                        offsetsToVisit.Push(reader.Offset);
                    }
                    else if (opcode == ILOpcode.ret ||
                             opcode == ILOpcode.endfilter ||
                             opcode == ILOpcode.endfinally ||
                             opcode == ILOpcode.throw_ ||
                             opcode == ILOpcode.rethrow ||
                             opcode == ILOpcode.jmp)
                    {
                        reader.Skip(opcode);

                        // RyuJIT is going to look at this basic block even though it's unreachable.
                        // Consider it visible so that we replace the tail with an endless loop.
                        if (reader.HasNext)
                        {
                            flags[reader.Offset] |= OpcodeFlags.VisibleBasicBlockStart;
                        }
                    }
                    else
                    {
                        reader.Skip(opcode);
                    }

                    if ((flags[offset] & OpcodeFlags.EndBasicBlock) != 0)
                    {
                        break;
                    }
                }
            }

            // Now sweep unreachable basic blocks by replacing them with nops
            bool hasUnmarkedIntructions = false;

            foreach (var flag in flags)
            {
                if ((flag & OpcodeFlags.InstructionStart) != 0 &&
                    (flag & OpcodeFlags.Mark) == 0)
                {
                    hasUnmarkedIntructions = true;
                }
            }

            if (!hasUnmarkedIntructions)
            {
                return(method);
            }

            byte[] newBody  = (byte[])methodBytes.Clone();
            int    position = 0;

            while (position < newBody.Length)
            {
                Debug.Assert((flags[position] & OpcodeFlags.InstructionStart) != 0);
                Debug.Assert((flags[position] & OpcodeFlags.VisibleBasicBlockStart) != 0);

                bool erase = (flags[position] & OpcodeFlags.Mark) == 0;

                int basicBlockStart = position;
                do
                {
                    if (erase)
                    {
                        newBody[position] = (byte)ILOpCode.Nop;
                    }
                    position++;
                } while (position < newBody.Length && (flags[position] & OpcodeFlags.VisibleBasicBlockStart) == 0);

                // If we had to nop out this basic block, we need to neutralize it by appending
                // an infinite loop ("br $-2").
                // We append instead of prepend because RyuJIT's importer has trouble with junk unreachable bytes.
                if (erase)
                {
                    if (position - basicBlockStart < 2)
                    {
                        // We cannot neutralize the basic block, so better leave the method alone.
                        // The control would fall through to the next basic block.
                        return(method);
                    }

                    newBody[position - 2] = (byte)ILOpCode.Br_s;
                    newBody[position - 1] = unchecked ((byte)-2);
                }
            }

            // EH regions with unmarked handlers belong to unmarked basic blocks
            // Need to eliminate them because they're not usable.
            ArrayBuilder <ILExceptionRegion> newEHRegions = new ArrayBuilder <ILExceptionRegion>();

            foreach (ILExceptionRegion ehRegion in ehRegions)
            {
                if ((flags[ehRegion.HandlerOffset] & OpcodeFlags.Mark) != 0)
                {
                    newEHRegions.Add(ehRegion);
                }
            }

            // Existing debug information might not match new instruction boundaries (plus there's little point
            // in generating debug information for NOPs) - generate new debug information by filtering
            // out the sequence points associated with nopped out instructions.
            MethodDebugInformation        debugInfo         = method.GetDebugInfo();
            IEnumerable <ILSequencePoint> oldSequencePoints = debugInfo?.GetSequencePoints();

            if (oldSequencePoints != null)
            {
                ArrayBuilder <ILSequencePoint> sequencePoints = new ArrayBuilder <ILSequencePoint>();
                foreach (var sequencePoint in oldSequencePoints)
                {
                    if (sequencePoint.Offset < flags.Length && (flags[sequencePoint.Offset] & OpcodeFlags.Mark) != 0)
                    {
                        sequencePoints.Add(sequencePoint);
                    }
                }

                debugInfo = new SubstitutedDebugInformation(debugInfo, sequencePoints.ToArray());
            }

            return(new SubstitutedMethodIL(method, newBody, newEHRegions.ToArray(), debugInfo));
        }
Beispiel #11
0
        public static FlowGraph Create(MethodIL il)
        {
            HashSet <int> bbStarts = GetBasicBlockStarts(il);

            List <BasicBlock> bbs = new List <BasicBlock>();

            void AddBB(int start, int count)
            {
                if (count > 0)
                {
                    bbs.Add(new BasicBlock(start, count));
                }
            }

            int prevStart = 0;

            foreach (int ofs in bbStarts.OrderBy(o => o))
            {
                AddBB(prevStart, ofs - prevStart);
                prevStart = ofs;
            }

            AddBB(prevStart, il.GetILBytes().Length - prevStart);

            FlowGraph fg = new FlowGraph(bbs);

            // We know where each basic block starts now. Proceed by linking them together.
            ILReader reader = new ILReader(il.GetILBytes());

            foreach (BasicBlock bb in bbs)
            {
                reader.Seek(bb.Start);
                while (reader.HasNext)
                {
                    Debug.Assert(fg.Lookup(reader.Offset) == bb);
                    ILOpcode opc = reader.ReadILOpcode();
                    if (opc.IsBranch())
                    {
                        int tar = reader.ReadBranchDestination(opc);
                        bb.Targets.Add(fg.Lookup(tar));
                        if (!opc.IsUnconditionalBranch())
                        {
                            bb.Targets.Add(fg.Lookup(reader.Offset));
                        }

                        break;
                    }

                    if (opc == ILOpcode.switch_)
                    {
                        uint numCases = reader.ReadILUInt32();
                        int  jmpBase  = reader.Offset + checked ((int)(numCases * 4));
                        bb.Targets.Add(fg.Lookup(jmpBase));

                        for (uint i = 0; i < numCases; i++)
                        {
                            int caseOfs = jmpBase + (int)reader.ReadILUInt32();
                            bb.Targets.Add(fg.Lookup(caseOfs));
                        }

                        break;
                    }

                    if (opc == ILOpcode.ret || opc == ILOpcode.endfinally || opc == ILOpcode.endfilter || opc == ILOpcode.throw_ || opc == ILOpcode.rethrow)
                    {
                        break;
                    }

                    reader.Skip(opc);
                    // Check fall through
                    if (reader.HasNext)
                    {
                        BasicBlock nextBB = fg.Lookup(reader.Offset);
                        if (nextBB != bb)
                        {
                            // Falling through
                            bb.Targets.Add(nextBB);
                            break;
                        }
                    }
                }
            }

            foreach (BasicBlock bb in bbs)
            {
                foreach (BasicBlock tar in bb.Targets)
                {
                    tar.Sources.Add(bb);
                }
            }

            return(fg);
        }
Beispiel #12
0
        public void Scan(MethodIL methodBody)
        {
            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);

            MethodReturnValue = null;
            ILReader reader = new ILReader(methodBody.GetILBytes());

            while (reader.HasNext)
            {
                int curBasicBlock = blockIterator.MoveNext(reader.Offset);

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

                if (currentStack == null)
                {
                    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.ldftn:
                case ILOpcode.sizeof_:
                case ILOpcode.ldc_i8:
                case ILOpcode.ldc_r4:
                case ILOpcode.ldc_r8:
                    PushUnknown(currentStack);
                    reader.Skip(opcode);
                    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;