Exemplo n.º 1
0
 public OpcodeTraits(OpcodeFlags flags, String mnemonic, byte opcode, bool extended)
 {
     this.flags = flags;
     this.mnemonic = mnemonic;
     this.opcode = opcode;
     this.extended = extended;
 }
Exemplo n.º 2
0
 public void Add(
     OpcodeKind kind,
     byte number,
     string name,
     OpcodeFlags flags = OpcodeFlags.None)
 {
     opcodes[((int)kind * 32) + number] = new Opcode(kind, number, name, flags);
 }
Exemplo n.º 3
0
        internal Opcode(OpcodeID id)
        {
            this.opcodeID = id;
            this.flags    = OpcodeFlags.Single;
#if DEBUG
            this.uniqueID = Opcode.NextUniqueId();
#endif
        }
 internal SelectOpcode(OpcodeID id, NodeSelectCriteria criteria, OpcodeFlags flags) : base(id)
 {
     this.criteria = criteria;
     base.flags |= flags | OpcodeFlags.Select;
     if (criteria.IsCompressable && ((base.flags & OpcodeFlags.InitialSelect) == OpcodeFlags.None))
     {
         base.flags |= OpcodeFlags.CompressableSelect;
     }
 }
Exemplo n.º 5
0
 internal SelectOpcode(OpcodeID id, NodeSelectCriteria criteria, OpcodeFlags flags) : base(id)
 {
     this.criteria = criteria;
     base.flags   |= flags | OpcodeFlags.Select;
     if (criteria.IsCompressable && ((base.flags & OpcodeFlags.InitialSelect) == OpcodeFlags.None))
     {
         base.flags |= OpcodeFlags.CompressableSelect;
     }
 }
Exemplo n.º 6
0
 internal SelectOpcode(OpcodeID id, NodeSelectCriteria criteria, OpcodeFlags flags)
     : base(id)
 {
     this.criteria = criteria;
     this.flags   |= (flags | OpcodeFlags.Select);
     if (criteria.IsCompressable && (0 == (this.flags & OpcodeFlags.InitialSelect)))
     {
         this.flags |= OpcodeFlags.CompressableSelect;
     }
 }
Exemplo n.º 7
0
        public Opcode(Opcodes opcode, OpcodeFlags flags, uint16_t opA, uint16_t opB)
        {
            this.opcode = opcode;

            operandA   = opA;
            operandB   = opB;
            this.flags = flags;

            ptr = new SmartPointer(0, 8);
        }
Exemplo n.º 8
0
        public void FromBinary(byte[] value)
        {
            if (value.Length != 8)
            {
                throw new IndexOutOfRangeException();
            }

            opcode   = (Opcodes)BitConverter.ToUInt16(value, 0);
            flags    = (OpcodeFlags)BitConverter.ToUInt16(value, 2);
            operandA = new uint16_t(BitConverter.ToUInt16(value, 4));
            operandB = new uint16_t(BitConverter.ToUInt16(value, 6));
        }
Exemplo n.º 9
0
 private static void AddOpcode(
     OpcodeKind kind,
     byte number,
     string name,
     OpcodeFlags flags = OpcodeFlags.None,
     byte fromVersion  = 1,
     byte toVersion    = 8)
 {
     for (byte v = fromVersion; v <= toVersion; v++)
     {
         opcodeTables[v - 1].Add(kind, number, name, flags);
     }
 }
Exemplo n.º 10
0
 internal Opcode(OpcodeKind kind, byte number, string name, OpcodeFlags flags)
 {
     this.Kind             = kind;
     this.Number           = number;
     this.Name             = name;
     this.HasStoreVariable = (flags & OpcodeFlags.Store) != 0;
     this.HasBranch        = (flags & OpcodeFlags.Branch) != 0;
     this.HasZText         = (flags & OpcodeFlags.ZText) != 0;
     this.IsCall           = (flags & OpcodeFlags.Call) != 0;
     this.IsDoubleVariable = (flags & OpcodeFlags.DoubleVar) != 0;
     this.IsFirstOpByRef   = (flags & OpcodeFlags.FirstOpByRef) != 0;
     this.IsJump           = kind == OpcodeKind.OneOp && number == 0x0c;
     this.IsQuit           = kind == OpcodeKind.ZeroOp && number == 0x0a;
     this.IsReturn         = (flags & OpcodeFlags.Return) != 0;
 }
Exemplo n.º 11
0
 internal SelectOpcode(OpcodeID id, NodeSelectCriteria criteria, OpcodeFlags flags)
     : base(id)
 {
     this.criteria = criteria;
     this.flags |= (flags | OpcodeFlags.Select);
     if (criteria.IsCompressable && (0 == (this.flags & OpcodeFlags.InitialSelect)))
     {
         this.flags |= OpcodeFlags.CompressableSelect;
     }
 }
Exemplo n.º 12
0
        Opcodes ParseOpcode(string operation, OpcodeFlags flags)
        {
            Func <bool> no = () => flags == OpcodeFlags.NoOperands;
            Func <bool> ra = () => flags.HasFlag(OpcodeFlags.Register1);
            Func <bool> rb = () => flags.HasFlag(OpcodeFlags.Register2);
            Func <bool> la = () => flags.HasFlag(OpcodeFlags.Literal1);
            Func <bool> lb = () => flags.HasFlag(OpcodeFlags.Literal2);

            Func <Opcodes, Opcodes, Opcodes, Opcodes, Opcodes> RR_LR_RL_LL = (rr, lr, rl, ll) =>
            {
                if (ra() & rb())
                {
                    return(rr);
                }
                else if (ra() & lb())
                {
                    return(rl);
                }
                else if (la() & rb())
                {
                    return(lr);
                }
                else if (la() & la())
                {
                    return(ll);
                }
                else
                {
                    InvalidOperands();
                    return(Opcodes.nop);
                }
            };

            Func <Opcodes, Opcodes, Opcodes> RR_LR = (rr, lr) =>
            {
                if (ra() & rb())
                {
                    return(rr);
                }
                if (la() & rb())
                {
                    return(lr);
                }
                else
                {
                    InvalidOperands();
                    return(Opcodes.nop);
                }
            };

            Func <Opcodes, Opcodes, Opcodes> RR_RL = (r, l) =>
            {
                if (ra() & rb())
                {
                    return(r);
                }
                else if (ra() & lb())
                {
                    return(l);
                }
                else
                {
                    InvalidOperands();
                    return(Opcodes.nop);
                }
            };

            Func <Opcodes, Opcodes> NoOps = o =>
            {
                if (no())
                {
                    return(o);
                }
                else
                {
                    InvalidOperands();
                    return(Opcodes.nop);
                }
            };

            Func <Opcodes, Opcodes> LR = l =>
            {
                if (lb() | ra())
                {
                    InvalidOperands();
                }
                if (la() & rb())
                {
                    return(l);
                }
                else
                {
                    InvalidOperands();
                    return(Opcodes.nop);
                }
            };

            Func <Opcodes, Opcodes, Opcodes> R_L = (r, l) =>
            {
                if (rb() | lb())
                {
                    InvalidOperands();
                }
                if (ra())
                {
                    return(r);
                }
                else if (la())
                {
                    return(l);
                }
                else
                {
                    InvalidOperands();
                    return(Opcodes.nop);
                }
            };

            Func <Opcodes, Opcodes> R = (r) =>
            {
                if (rb() | lb() | la())
                {
                    InvalidOperands();
                }
                if (ra())
                {
                    return(r);
                }
                else
                {
                    InvalidOperands();
                    return(Opcodes.nop);
                }
            };

            Func <Opcodes, Opcodes> L = (l) =>
            {
                if (rb() | lb() | ra())
                {
                    InvalidOperands();
                }
                if (la())
                {
                    return(l);
                }
                else
                {
                    InvalidOperands();
                    return(Opcodes.nop);
                }
            };

            switch (operation)
            {
            case "nop":
                return(NoOps(Opcodes.nop));

            case "read":
                return(RR_RL(Opcodes.readr, Opcodes.readl));

            case "write":
                return(RR_LR_RL_LL(Opcodes.writerr, Opcodes.writelr, Opcodes.writerl, Opcodes.writell));

            case "push":
                return(R_L(Opcodes.pushr, Opcodes.pushl));

            case "mov":
                return(RR_RL(Opcodes.movr, Opcodes.movl));

            case "add":
                return(RR_RL(Opcodes.addr, Opcodes.addl));

            case "sub":
                return(RR_RL(Opcodes.subr, Opcodes.subl));

            case "mult":
                return(RR_RL(Opcodes.multr, Opcodes.multl));

            case "div":
                return(RR_RL(Opcodes.divr, Opcodes.divl));

            case "mod":
                return(RR_RL(Opcodes.modr, Opcodes.modl));

            case "and":
                return(RR_RL(Opcodes.andr, Opcodes.andl));

            case "or":
                return(RR_RL(Opcodes.orr, Opcodes.orl));

            case "xor":
                return(RR_RL(Opcodes.xorr, Opcodes.xorl));

            case "not":
                return(R(Opcodes.not));

            case "neg":
                return(R(Opcodes.neg));

            case "pop":
                return(R(Opcodes.pop));

            case "pusha":
                return(NoOps(Opcodes.pusha));

            case "popa":
                return(NoOps(Opcodes.popa));

            case "hlt":
                return(NoOps(Opcodes.hlt));

            case "int":
                return(R_L(Opcodes.intr, Opcodes.intl));

            case "call":
                return(R_L(Opcodes.callr, Opcodes.calll));

            case "jmp":
                return(R_L(Opcodes.jmpr, Opcodes.jmpl));

            case "je":
                return(L(Opcodes.je));

            case "jne":
                return(L(Opcodes.jne));

            case "jg":
                return(L(Opcodes.jg));

            case "jge":
                return(L(Opcodes.jge));

            case "jl":
                return(L(Opcodes.jl));

            case "jle":
                return(L(Opcodes.jle));

            case "cmp":
                return(RR_RL(Opcodes.cmpr, Opcodes.cmpl));

            case "ret":
                return(NoOps(Opcodes.ret));

            case "deref":
                return(RR_RL(Opcodes.derefr, Opcodes.derefl));

            case "brk":
                return(NoOps(Opcodes.brk));

            case "cpuid":
                return(NoOps(Opcodes.cpuid));

            case "out":
                return(NoOps(Opcodes._out));

            case "in":
                return(NoOps(Opcodes._in));

            case "sa":
                return(RR_LR_RL_LL(Opcodes.sarr, Opcodes.salr, Opcodes.sarl, Opcodes.sall));

            case "sai":
                return(R_L(Opcodes.sair, Opcodes.sail));

            case "inca":
                return(NoOps(Opcodes.inca));

            case "deca":
                return(NoOps(Opcodes.deca));

            case "ls":
                return(RR_RL(Opcodes.lsr, Opcodes.lsl));

            case "rs":
                return(RR_RL(Opcodes.rsr, Opcodes.rsl));
            }

            RaiseError(string.Format("Invalid instruction \"{0}\"", operation));
            return(Opcodes.nop);
        }
Exemplo n.º 13
0
        void ParseInstruction(string line)
        {
            Match match = Regex.Match(line, instructionCapture);

            string operation = match.Groups[1].Value;
            string opA       = match.Groups[2].Value;
            string opB       = match.Groups[4].Value;

            uint16_t    operandA = Int.UInt16;
            uint16_t    operandB = Int.UInt16;
            OpcodeFlags flags    = OpcodeFlags.NoOperands;

            Func <bool> era = () => {
                uint16_t temp;
                if (ExpectRegister(opA, out temp))
                {
                    operandA = temp;
                    return(true);
                }
                else
                {
                    return(false);
                }
            };
            Func <bool> erb = () => {
                uint16_t temp;
                if (ExpectRegister(opB, out temp))
                {
                    operandB = temp;
                    return(true);
                }
                else
                {
                    return(false);
                }
            };
            Func <bool> ela = () => {
                uint16_t temp;
                if (ExpectLiteral(opA, out temp))
                {
                    operandA = temp;
                    return(true);
                }
                else
                {
                    return(false);
                }
            };
            Func <bool> elb = () => {
                uint16_t temp;
                if (ExpectLiteral(opB, out temp))
                {
                    operandB = temp;
                    return(true);
                }
                else
                {
                    return(false);
                }
            };
            Func <bool> esa = () => {
                uint16_t temp;
                if (flags.HasFlag(OpcodeFlags.Literal1))
                {
                    return(false);
                }

                if (ExpectSymbol(opA, out temp))
                {
                    operandA = temp;
                    return(true);
                }
                else
                {
                    return(false);
                }
            };
            Func <bool> esb = () => {
                uint16_t temp;
                if (flags.HasFlag(OpcodeFlags.Literal2))
                {
                    return(false);
                }

                if (ExpectSymbol(opB, out temp))
                {
                    operandB = temp;
                    return(true);
                }
                else
                {
                    return(false);
                }
            };

            try
            {
                if (era())
                {
                    flags = flags | OpcodeFlags.Register1;
                }
                if (erb())
                {
                    flags = flags | OpcodeFlags.Register2;
                }
                if (ela())
                {
                    flags = flags | OpcodeFlags.Literal1;
                }
                if (elb())
                {
                    flags = flags | OpcodeFlags.Literal2;
                }
                if (esa())
                {
                    flags = flags | OpcodeFlags.Literal1;
                }
                if (esb())
                {
                    flags = flags | OpcodeFlags.Literal2;
                }

                Opcodes opcode = ParseOpcode(operation, flags);
                Opcode  oc     = new Opcode(opcode, flags, operandA, operandB);
                buildList.Add(string.Format("${0}", opcodes.Count));
                opcodes.Add(oc);
                address += 8;
            }
            catch
            {
                throw;
            }
        }
Exemplo n.º 14
0
 internal bool TestFlag(OpcodeFlags flag)
 {
     return (0 != (this.flags & flag));
 }
Exemplo n.º 15
0
 public FlagProcessor(OpcodeFlags opcodeFlags, Flags flags)
 {
     this.opcodeFlags = opcodeFlags;
     this.flags       = flags;
 }
Exemplo n.º 16
0
 internal bool TestFlag(OpcodeFlags flag)
 {
     return(OpcodeFlags.None != (this.flags & flag));
 }
Exemplo n.º 17
0
 internal Opcode(OpcodeID id)
 {
     this.opcodeID = id;
     this.flags    = OpcodeFlags.Single;
 }
 internal Opcode(OpcodeID id)
 {
     this.opcodeID = id;
     this.flags = OpcodeFlags.Single;
 }
Exemplo n.º 19
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));
        }
Exemplo n.º 20
0
        internal Opcode(OpcodeID id)
        {
            this.opcodeID = id;
            this.flags = OpcodeFlags.Single;
#if DEBUG
            this.uniqueID = Opcode.NextUniqueId();
#endif
        }