Пример #1
0
        // ----------------------------------------------------------------------
        // Back propogation
        // ----------------------------------------------------------------------

        // Refine given before machine state account for backwards flow from after machine state.
        // Only propogates liveness, thus we only need to look for read/writes of arguments and locals,
        // either directly (via ldarg/starg/ldloc/stloc) or indirectly (via ldind/ldobj/cpobj).
        public void BackwardInstruction(InstructionContext context, int index, MachineState beforeState, MachineState afterState, BoolRef changed)
        {
            var instruction = context.Block.Body[index];
            switch (instruction.Flavor)
            {
            case InstructionFlavor.Misc:
                {
                    var misci = (MiscInstruction)instruction;
                    switch (misci.Op)
                    {
                    case MiscOp.LdindRef:
                        beforeState.ReadPointer(afterState, beforeState.PeekPointsTo(0), changed);
                        return;
                    case MiscOp.StindRef:
                        // May have overwritten an arg or local, but don't know exactly which one, so must
                        // be conservative and leave everything alive
                        break;
                    case MiscOp.Nop:
                    case MiscOp.Break:
                    case MiscOp.Dup:
                    case MiscOp.Pop:
                    case MiscOp.Ldnull:
                    case MiscOp.Ckfinite:
                    case MiscOp.Throw:
                    case MiscOp.Rethrow:
                    case MiscOp.LdelemRef:
                    case MiscOp.StelemRef:
                    case MiscOp.Ldlen:
                    case MiscOp.Ret:
                    case MiscOp.RetVal:
                    case MiscOp.Endfilter:
                    case MiscOp.Endfinally:
                        break;
                    default:
                        throw new ArgumentOutOfRangeException();
                    }
                    break;
                }
            case InstructionFlavor.ArgLocal:
                {
                    var sli = (ArgLocalInstruction)instruction;
                    switch (sli.Op)
                    {
                    case ArgLocalOp.Ld:
                        beforeState.ReadArgLocal(afterState, sli.ArgLocal, sli.Index, changed);
                        return;
                    case ArgLocalOp.Lda:
                        // Assume pointer we are creating is read from, and to be conservative don't
                        // assume it is written to.
                        beforeState.ReadArgLocal(afterState, sli.ArgLocal, sli.Index, changed);   
                        break;
                    case ArgLocalOp.St:
                        beforeState.WriteArgLocal(afterState, sli.ArgLocal, sli.Index, changed);
                        return;
                    default:
                        throw new ArgumentOutOfRangeException();
                    }
                    break;
                }
            case InstructionFlavor.Type:
                {
                    var typei = (TypeInstruction)instruction;
                    switch (typei.Op)
                    {
                    case TypeOp.Ldobj:
                        beforeState.ReadPointer(afterState, beforeState.PeekPointsTo(0), changed);
                        return;
                    case TypeOp.Stobj:
                        // As above, can't be sure which args or locals will be written to
                        break;
                    case TypeOp.Cpobj:
                        // As above, can't be sure which args or locals will be written to
                        // But can handle read safely
                        beforeState.ReadPointer(afterState, beforeState.PeekPointsTo(0), changed);
                        return;
                    case TypeOp.Newarr:
                    case TypeOp.Initobj:
                    case TypeOp.Castclass:
                    case TypeOp.Isinst:
                    case TypeOp.Box:
                    case TypeOp.Unbox:
                    case TypeOp.UnboxAny:
                    case TypeOp.Ldtoken:
                    case TypeOp.Ldelem:
                    case TypeOp.Stelem:
                        break;
                    default:
                        throw new ArgumentOutOfRangeException();
                    }
                    break;
                }
            case InstructionFlavor.Try:
                {
                    var tryi = (TryInstruction)instruction;
                    var tryContext = new TryBodyInstructionContext(context, index, tryi.Body);
                    BackwardBlock(tryContext, changed);
                    for (var j = 0; j < tryi.Handlers.Count; j++)
                    {
                        var h = tryi.Handlers[j];
                        var handlerContext = new TryHandlerInstructionContext(context, index, h.Body, j);
                        BackwardBlock(handlerContext, changed);
                    }
                    return;
                }
            case InstructionFlavor.Method:
                {
                    var methi = (MethodInstruction)instruction;
                    switch (methi.Op)
                    {
                    case MethodOp.Call:
                    case MethodOp.Newobj:
                        {
                            // Assume any pointers passed to call are read from
                            var sig = (CST.MethodSignature)methi.Method.ExternalSignature;
                            var skippedArgs = (methi.Op == MethodOp.Newobj ? 1 : 0);
                            var passedArgs = sig.Parameters.Count - skippedArgs;
                            for (var i = passedArgs - 1; i >= 0; i--)
                            {
                                if (sig.Parameters[skippedArgs + i].Style(methEnv) is CST.ManagedPointerTypeStyle)
                                    beforeState.ReadPointer
                                        (afterState, beforeState.PeekPointsTo(passedArgs - 1 - i), changed);
                            }
                            // Also assume call does not write to any pointers, thus everything remains
                            // alive across call. Ie just fallthough.
                            break;
                        }
                    case MethodOp.Ldftn:
                    case MethodOp.Ldtoken:
                        break;
                    default:
                        throw new ArgumentOutOfRangeException();
                    }
                    break;
                }
            case InstructionFlavor.Unsupported:
            case InstructionFlavor.Field:
            case InstructionFlavor.Branch:
            case InstructionFlavor.Switch:
            case InstructionFlavor.Compare:
            case InstructionFlavor.LdElemAddr:
            case InstructionFlavor.LdInt32:
            case InstructionFlavor.LdInt64:
            case InstructionFlavor.LdSingle:
            case InstructionFlavor.LdDouble:
            case InstructionFlavor.LdString:
            case InstructionFlavor.Arith:
            case InstructionFlavor.Conv:
            case InstructionFlavor.IfThenElsePseudo:
            case InstructionFlavor.ShortCircuitingPseudo:
            case InstructionFlavor.StructuralSwitchPseudo:
            case InstructionFlavor.LoopPseudo:
            case InstructionFlavor.WhileDoPseudo:
            case InstructionFlavor.DoWhilePseudo:
            case InstructionFlavor.LoopControlPseudo:
                break;
            default:
                throw new ArgumentOutOfRangeException();
            }
            // By default, anything alive in after state must be alive in before state.
            beforeState.PropogateBackwards(afterState, changed);
        }
Пример #2
0
 // What are all the source -> target transitions possible due to exceptions from instruction in
 // context's instruction block?
 private void AddEffectiveInstructionTransitions(Set<SourceTarget> transitions, InstructionContext context, int index)
 {
     var instruction = context.Block.Body[index];
     if (instruction.Flavor == InstructionFlavor.Try)
     {
         var tryi = (TryInstruction)instruction;
         var tryContext = new TryBodyInstructionContext(context, index, tryi.Body);
         var exits = new Seq<Instruction>();
         AddExceptionalExits(exits, tryi.Body);
         AddEffectiveBlockTransitions(transitions, tryContext);
         for (var i = 0; i < tryi.Handlers.Count; i++)
         {
             var handler = tryi.Handlers[i];
             var handlerContext = new TryHandlerInstructionContext(context, index, handler.Body, i);
             AddEffectiveBlockTransitions(transitions, handlerContext);
             // Could transition from any exceptional exit point of try body to start of this handler
             foreach (var exit in exits)
                 transitions.Add(new SourceTarget(exit, handler.Body.Body[0].Offset, "throw to handler"));
         }
     }
     else if (instruction.Flavor == InstructionFlavor.Branch)
     {
         var bri = (BranchInstruction)instruction;
         if (bri.Op == BranchOp.Leave)
         {
             // Leave will enter each finally handler between here and the target instruction.
             // Initially, control comes from just the leave instruction itself.
             var sources = new Seq<Instruction> { bri };
             var currContext = context;
             while (true)
             {
                 if (currContext.Block.ContainsOffset(bri.Target))
                 {
                     // Found target of leave
                     foreach (var source in sources)
                         transitions.Add(new SourceTarget(source, bri.Target, "leave to target"));
                     break;
                 }
                 currContext = currContext.ParentContext;
                 if (currContext == null)
                     throw new InvalidOperationException("no target for leave");
                 var outerTryi = currContext.ParentInstruction as TryInstruction;
                 if (outerTryi != null)
                 {
                     var i = outerTryi.FinallyIndex;
                     if (i >= 0)
                     {
                         // Found next outer finally. Leave will go via start of this block, and exit at
                         // each endfinally.
                         var handler = outerTryi.Handlers[i];
                         foreach (var source in sources)
                             transitions.Add(new SourceTarget(source, handler.Body.Body[0].Offset, "leave to finally"));
                         sources = new Seq<Instruction>();
                         foreach (var handlerInstruction in handler.Body.Body)
                         {
                             if (handlerInstruction.Code == InstructionCode.Endfinally)
                                 sources.Add(handlerInstruction);
                         }
                     }
                 }
             }
         }
         // else: normal control-flow transitions cary over the entire machine state, so we deal
         //       with them during forward analysis below
     }
     else if (instruction.Flavor == InstructionFlavor.Misc)
     {
         var misci = (MiscInstruction)instruction;
         if (misci.Op == MiscOp.Endfinally)
         {
             // If entered a finally block because of a leave from a try or catch block, then 
             // could continue to that offset, however we account for that above when handling the leave.
             // If entered a finally or fault block because of an exception, then could continue to start
             // of each enclosing catch, fault or finally block. Since a catch clause may not match, must
             // keep including outer blocks. However, a fault or finally block will always fire, so can stop.
             // ParentInstruction of context will be the try with fault/finally handler we are currently in,
             // so skip it.
             var currContext = context;
             var catchesOnly = true;
             while (catchesOnly && currContext.ParentContext != null)
             {
                 currContext = currContext.ParentContext;
                 var outerTryi = currContext.ParentInstruction as TryInstruction;
                 if (outerTryi != null)
                 {
                     foreach (var outerHandler in outerTryi.Handlers)
                     {
                         transitions.Add(new SourceTarget(instruction, outerHandler.Body.Body[0].Offset, "continue up chain"));
                         if (outerHandler.Flavor == HandlerFlavor.Fault ||
                             outerHandler.Flavor == HandlerFlavor.Finally)
                             catchesOnly = false;
                     }
                 }
             }
             // else: will leave method
         }
         // else: no additional control flow
     }
     // else: no aditional control flow
 }