예제 #1
0
 public RootBasicBlock(int id, MachineState initialState)
     : base(id, new Instructions(initialState))
 {
 }
예제 #2
0
        // Return instruction to evaluate test to its boolean result, leaving an int32 on the stack.
        //  - beforeState has test operands on stack.
        //  - afterState has the test evaluated AND the resulting int32 consumed.
        // (These states came from the original brtrue, etc instruction).
        public CompareInstruction ToCompareInstruction(int offset, MachineState beforeState, MachineState afterState, PointsTo bottom)
        {
            var state1 = beforeState.PopPushType(Pops, beforeState.RootEnv.Global.Int32Ref, bottom);
            var dummy  = new BoolRef();

            state1.PropogateBackwards(afterState, dummy);
            beforeState.PropogateBackwards(state1, dummy);

            var op = default(CompareOp);

            switch (Op)
            {
            case TestOp.True:
                op = CompareOp.CtruePseudo;
                break;

            case TestOp.False:
                op = CompareOp.CfalsePseudo;
                break;

            case TestOp.Equal:
                op = CompareOp.Ceq;
                break;

            case TestOp.NotEqual:
                op = CompareOp.CnePseudo;
                break;

            case TestOp.LessThanOrEqual:
                op = CompareOp.ClePseudo;
                break;

            case TestOp.LessThan:
                op = CompareOp.Clt;
                break;

            case TestOp.GreaterThanOrEqual:
                op = CompareOp.CgePseudo;
                break;

            case TestOp.GreaterThan:
                op = CompareOp.Cgt;
                break;

            default:
                throw new ArgumentOutOfRangeException();
            }

            return(new CompareInstruction(offset, op, IsUnsigned)
            {
                Type = Type, BeforeState = beforeState, AfterState = state1
            });
        }
예제 #3
0
        // Append instructions to evaluate test to its boolean result, leaving an int32 on the stack.
        // Return the state after the test has been evaluated, but before the int32 is popped.
        public MachineState Eval(Seq <Instruction> instructions, MachineState beforeState, MachineState afterState, PointsTo bottom)
        {
            var cinst = ToCompareInstruction(-1, beforeState, afterState, bottom);

            instructions.Add(cinst);
            return(cinst.AfterState);
        }
예제 #4
0
 // Remove from suffix of instructions those instructions which yield the top-most stack value, and
 // return them as a new instruction block.
 // It is possible for instructions to be left empty, for example if the suffix is the entire block, or
 // the suffix could not be calculated.
 private Instructions SeparateCondition(MachineState initialState, ref Seq<Instruction> instructions)
 {
     var i = FindStartOfStackPrefix(instructions, instructions.Count - 1, 1);
     if (i >= 0)
     {
         var condInstructions = new Seq<Instruction>();
         for (var j = i; j < instructions.Count; j++)
             condInstructions.Add(instructions[j]);
         var cond = new Instructions(instructions[i].BeforeState, condInstructions);
         instructions.RemoveRange(i, instructions.Count - i);
         return cond;
     }
     else
     {
         var cond = new Instructions(initialState, instructions);
         instructions = new Seq<Instruction>();
         return cond;
     }
 }
예제 #5
0
 public LoopCandidateBasicBlock(int id, JST.Identifier label, MachineState initialState)
     : base(id, new Instructions(initialState))
 {
     Label = label;
 }
예제 #6
0
        // ----------------------------------------------------------------------
        // Exceptional transitions
        // ----------------------------------------------------------------------

        // Exceptional control flow may take this source machine state to other target machine state.
        // Account for forward and backward propogation of non-stack related state.
        public void SourceToTargetTransition(MachineState other, BoolRef changed)
        {
            innerState.Value.ArgsLocalsState.SourceToTargetTransition(other.innerState.Value.ArgsLocalsState, changed);
        }
예제 #7
0
        // ----------------------------------------------------------------------
        // Third pass: Reduce basic blocks
        // ----------------------------------------------------------------------

        private Instructions Peephole(MachineState initialState, IImSeq<Instruction> instructions)
        {
            return new Instructions(initialState, instructions).Peephole(tracer);
        }
예제 #8
0
 public void ReadArgLocal(MachineState nextState, ArgLocal argLocal, int index, BoolRef changed)
 {
     innerState.Value.ArgsLocalsState.PropogateBackwards(nextState.innerState.Value.ArgsLocalsState, argLocal, index, true, changed);
 }
예제 #9
0
 public void ReadPointer(MachineState nextState, PointsTo pointsTo, BoolRef changed)
 {
     innerState.Value.ArgsLocalsState.ReadPointer(nextState.innerState.Value.ArgsLocalsState, pointsTo, changed);
 }
예제 #10
0
 // This and other must always be the same state. Throw if no lub. Otherwise return true if this state changed.
 public void Unify(MachineState other, BoolRef changed)
 {
     if (RootEnv != other.RootEnv)
         throw new InvalidOperationException("states must share same environment");
     if (nArgs != other.nArgs || nLocals != other.nLocals)
         throw new InvalidOperationException("states must have the same number of arguments and locals");
     innerState.Unify(other.innerState, (l, r, c) => l.Unify(RootEnv, r, c), changed);
 }
예제 #11
0
        // ----------------------------------------------------------------------
        // Backward propogation of args and locals liveness
        // (we clone and mutate the existing args and locals state)
        // ----------------------------------------------------------------------

        // Ensure any argument or local which is alive in nextState is alive in this state. Return true if this state changed.
        public void PropogateBackwards(MachineState nextState, BoolRef changed)
        {
            innerState.Value.ArgsLocalsState.PropogateBackwards(nextState.innerState.Value.ArgsLocalsState, default(ArgLocal), -1, false, changed);
        }
예제 #12
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);
        }
예제 #13
0
 private MachineState ForwardBlock(InstructionContext context, MachineState initState, BoolRef changed)
 {
     if (context.HasBody)
     {
         var state = initState;
         for (var i = 0; i < context.Block.Body.Count; i++)
         {
             var offset = context.Block.Body[i].Offset;
             if (state == null)
             {
                 // Has an earlier instruction transferred control to here?
                 if (!offsetToBeforeState.TryGetValue(offset, out state))
                 {
                     // CLR spec requires that an instruction only reached by back jumps, and the entry of
                     // a try block, has an empty entry stack. We initially assume no pointers are
                     // stored in arguments or locals.
                     state = new MachineState(methEnv, method.ValueParameters.Count, method.Locals.Count);
                     offsetToBeforeState.Add(offset, state);
                     changed.Set();
                 }
             }
             else
                 // Current instruction cannot be a try
                 UnifyBeforeState(state, offset, changed);
             context.Block.Body[i].BeforeState = state; // not necessarialy final
             state = ForwardInstruction(context, i, state, changed);
             if (!context.Block.Body[i].IsStructural)
                 UnifyAfterState(state, offset, changed);
             context.Block.Body[i].AfterState = state; // not necessarialy final
             if (context.Block.Body[i].NeverReturns)
                 state = null;
         }
         if (state != null)
             throw new InvalidOperationException("fell off of end of instructions");
         return context.Block.Body[context.Block.Body.Count - 1].AfterState;
     }
     else
         return initState;
 }
예제 #14
0
        // ----------------------------------------------------------------------
        // Forward propogation
        // ----------------------------------------------------------------------

        // Return machine state after performing given instruction on entry machine state.
        // Propogates stack shapes and args/locals points-to
        private MachineState ForwardInstruction(InstructionContext context, int index, MachineState state, BoolRef changed)
        {
            var instruction = context.Block.Body[index];
            switch (instruction.Flavor)
            {
            case InstructionFlavor.Unsupported:
                throw new InvalidOperationException("unsupported opcode");
            case InstructionFlavor.Misc:
                {
                    var misci = (MiscInstruction)instruction;
                    switch (misci.Op)
                    {
                    case MiscOp.Nop:
                    case MiscOp.Break:
                        return state;
                    case MiscOp.Dup:
                        return state.Push(state.Peek(0));
                    case MiscOp.Pop:
                        return state.Pop(1);
                    case MiscOp.Ldnull:
                        return state.PushType(global.NullRef, BottomPT);
                    case MiscOp.Ckfinite:
                        state.PeekExpectedType(0, global.DoubleRef, changed);
                        // Assume the instruction can "peek" at top of stack, thus no need for pop/push.
                        return state;
                    case MiscOp.Throw:
                        state.PeekReferenceType(0);
                        return state.DiscardStack();
                    case MiscOp.Rethrow:
                        return state.DiscardStack();
                    case MiscOp.LdindRef:
                        {
                            var elemType = state.PeekPointerToReferenceType(0);
                            return state.PopPushType(1, elemType, BottomPT);
                        }
                    case MiscOp.StindRef:
                        {
                            var expElemType = state.PeekPointerToReferenceType(1);
                            state.PeekExpectedType(0, expElemType, changed);
                            return state.Pop(2);
                        }
                    case MiscOp.LdelemRef:
                        {
                            state.PeekIndexType(0);
                            // WARNING: Type may not be final
                            var elemType = state.PeekArrayOfReferenceType(1);
                            return state.PopPushType(2, elemType, BottomPT);
                        }
                    case MiscOp.StelemRef:
                        state.PeekReferenceType(0);
                        state.PeekIndexType(1);
                        state.PeekArrayOfReferenceType(2);
                        // Since the value type and array element type may be independently generalized,
                        // it is pointless to check that the first is assignable to the second.
                        // Instead this check is done at runtime.
                        return state.Pop(3);
                    case MiscOp.Ldlen:
                        state.PeekArrayOfAnyType(0);
                        return state.PopPushType(1, global.IntNativeRef, BottomPT);
                    case MiscOp.Ret:
                        {
                            if (state.Depth != 0)
                                throw new InvalidOperationException("stack should be empty");
                            return state; // empty
                        }
                    case MiscOp.RetVal:
                        {
                            state.PeekExpectedType(0, method.Result.Type, changed);
                            var newState = state.Pop(1);
                            if (newState.Depth != 0)
                                throw new InvalidOperationException("stack should be empty");
                            return newState; // empty
                        }
                    case MiscOp.Endfilter:
                        {
                            state.PeekExpectedType(0, global.Int32Ref, changed);
                            var newState = state.Pop(1);
                            if (newState.Depth != 0)
                                throw new InvalidOperationException("stack should be empty");
                            return newState; // empty
                        }
                    case MiscOp.Endfinally:
                        {
                            // Control could transfer to an outer finally/fault block, or to the target
                            // of a leave instruction. However these transitions are delt with separately.
                            return state.DiscardStack();
                        }
                    default:
                        throw new ArgumentOutOfRangeException();
                    }
                }
            case InstructionFlavor.Branch:
                {
                    var bri = (BranchInstruction)instruction;
                    switch (bri.Op)
                    {
                    case BranchOp.Br:
                        UnifyBeforeState(state, bri.Target, changed);
                        return state;
                    case BranchOp.Brtrue:
                        {
                            // WARNING: Type may not be final
                            // NOTE: May capture skolemized types
                            bri.Type = state.PeekIntegerOrObjectOrPointerType(0, false);
                            var newState = state.Pop(1);
                            UnifyBeforeState(newState, bri.Target, changed);
                            return newState;
                        }
                    case BranchOp.Brfalse:
                        {
                            // WARNING: Type may not be final
                            // NOTE: May capture skolemized types
                            bri.Type = state.PeekIntegerOrObjectOrPointerType(0, true);
                            var newState = state.Pop(1);
                            UnifyBeforeState(newState, bri.Target, changed);
                            return newState;
                        }
                    case BranchOp.Breq:
                    case BranchOp.Brne:
                        {
                            // WARNING: Type may not be final
                            // NOTE: May capture skolemized types
                            bri.Type = state.Peek2ComparableTypes(0, true);
                            var newState = state.Pop(2);
                            UnifyBeforeState(newState, bri.Target, changed);
                            return newState;
                        }
                    case BranchOp.Leave:
                        {
                            // Control could transfer via finally blocks instead of directly to the leave target.
                            // Propogate only that the stack must be empty at target. Remaining machine state
                            // is dealt with separately.
                            UnifyBeforeState
                                (new MachineState(methEnv, method.ValueParameters.Count, method.Locals.Count),
                                 bri.Target,
                                 changed);
                            return state.DiscardStack();
                        }
                    case BranchOp.BrLt:
                    case BranchOp.BrLe:
                    case BranchOp.BrGt:
                    case BranchOp.BrGe:
                        {
                            // WARNING: Type may not be final
                            // NOTE: May capture skolemized types
                            bri.Type = state.Peek2ComparableTypes(0, false);
                            var newState = state.Pop(2);
                            UnifyBeforeState(newState, bri.Target, changed);
                            return newState;
                        }
                    default:
                        throw new ArgumentOutOfRangeException();
                    }
                }
            case InstructionFlavor.Switch:
                {
                    var switchi = (SwitchInstruction)instruction;
                    state.PeekExpectedType(0, global.Int32Ref, changed);
                    var newState = state.Pop(1);
                    for (var i = 0; i < switchi.CaseTargets.Count; i++)
                        UnifyBeforeState(newState, switchi.CaseTargets[i], changed);
                    return newState;
                }
            case InstructionFlavor.Compare:
                {
                    var cmpi = (CompareInstruction)instruction;
                    // WARNING: Capured type may not be final
                    // NOTE: May capture skolemized types
                    switch (cmpi.Op)
                    {
                    case CompareOp.Ceq:
                    case CompareOp.CnePseudo:
                        cmpi.Type = state.Peek2ComparableTypes(0, true);
                        return state.PopPushType(2, global.Int32Ref, BottomPT);
                    case CompareOp.Clt:
                    case CompareOp.Cgt:
                    case CompareOp.CgePseudo:
                    case CompareOp.ClePseudo:
                        cmpi.Type = state.Peek2ComparableTypes(0, false);
                        return state.PopPushType(2, global.Int32Ref, BottomPT);
                    case CompareOp.CtruePseudo:
                    case CompareOp.CfalsePseudo:
                        cmpi.Type = state.PeekIntegerOrObjectOrPointerType(0, true);
                        return state.PopPushType(1, global.Int32Ref, BottomPT);
                    default:
                        throw new ArgumentOutOfRangeException();
                    }
                }
            case InstructionFlavor.ArgLocal:
                {
                    var argi = (ArgLocalInstruction)instruction;
                    var type = method.ArgLocalType(argi.ArgLocal, argi.Index);
                    switch (argi.Op)
                    {
                    case ArgLocalOp.Ld:
                        return state.PushType(type, state.ArgLocalPointsTo(argi.ArgLocal, argi.Index));
                    case ArgLocalOp.Lda:
                        return state.PushType(methEnv.Global.ManagedPointerTypeConstructorRef.ApplyTo(type), ArgLocalPT(argi.ArgLocal, argi.Index));
                    case ArgLocalOp.St:
                        {
                            state.PeekExpectedType(0, type, changed);
                            var pointsTo = state.PeekPointsTo(0);
                            if (!pointsTo.IsBottom)
                            {
                                if (!(type.Style(methEnv) is ManagedPointerTypeStyle))
                                    throw new InvalidOperationException
                                        ("stack indicates pointer, but parameter or local type does not");
                                if (pointsTo.PointsOutsideOfHeap)
                                    throw new InvalidOperationException("arguments cannot point outside of the heap");
                            }
                            return state.PopAddArgLocalPointsTo(1, argi.ArgLocal, argi.Index, pointsTo);
                        }
                    default:
                        throw new ArgumentOutOfRangeException();
                    }
                }
            case InstructionFlavor.Field:
                {
                    var fieldi = (FieldInstruction)instruction;
                    var fieldEnv = fieldi.Field.Enter(methEnv);
                    var fieldType = fieldEnv.SubstituteType(fieldEnv.Field.FieldType);
                    switch (fieldi.Op)
                    {
                    case FieldOp.Ldfld:
                        if (fieldi.IsStatic)
                            return state.PushType(fieldType, BottomPT);
                        else
                        {
                            fieldi.IsViaPointer = state.PeekDereferencableExpectedType
                                (0, fieldi.Field.DefiningType, true, changed);
                            return state.PopPushType(1, fieldType, BottomPT);
                        }
                    case FieldOp.Ldflda:
                        if (fieldi.IsStatic)
                            return state.PushType(methEnv.Global.ManagedPointerTypeConstructorRef.ApplyTo(fieldType), HeapPT);
                        else
                        {
                            // Underlying type cannot be a struct, otherwise would have a pointer into
                            // the stack
                            fieldi.IsViaPointer = state.PeekDereferencableExpectedType
                                (0, fieldi.Field.DefiningType, false, changed);
                            return state.PopPushType
                                (1, methEnv.Global.ManagedPointerTypeConstructorRef.ApplyTo(fieldType), HeapPT);
                        }
                    case FieldOp.Stfld:
                        if (fieldi.IsStatic)
                        {
                            state.PeekExpectedType(0, fieldType, changed);
                            return state.Pop(1);
                        }
                        else
                        {
                            state.PeekExpectedType(0, fieldType, changed);
                            fieldi.IsViaPointer = state.PeekDereferencableExpectedType
                                (1, fieldi.Field.DefiningType, false, changed);
                            return state.Pop(2);
                        }
                    case FieldOp.Ldtoken:
                        return state.PushType(global.RuntimeFieldHandleRef, BottomPT);
                    default:
                        throw new ArgumentOutOfRangeException();
                    }
                }
            case InstructionFlavor.Method:
                {
                    var methi = (MethodInstruction)instruction;
                    var sig = (CST.MethodSignature)methi.Method.ExternalSignature;
                    switch (methi.Op)
                    {
                    case MethodOp.Call:
                        {
                            for (var i = sig.Parameters.Count - 1; i >= 1; i--)
                                state.PeekExpectedType(sig.Parameters.Count - 1 - i, sig.Parameters[i], changed);
                            if (methi.Constrained != null)
                            {
                                if (!methi.IsVirtual || methi.Method.IsStatic)
                                    throw new InvalidOperationException
                                        ("constrained only valid on virtual calls to instance methods");
                                var thisType = sig.Parameters[0];
                                var constrainedPtr = methEnv.Global.ManagedPointerTypeConstructorRef.ApplyTo(methi.Constrained);
                                var constrainedBox = methEnv.Global.BoxTypeConstructorRef.ApplyTo(methi.Constrained);
                                var cs = methi.Constrained.Style(methEnv);
                                if (cs is ValueTypeStyle)
                                {
                                    if (thisType.Style(methEnv) is ManagedPointerTypeStyle)
                                    {
                                        // We pass the argument pointer as is
                                        if (!methi.Constrained.IsAssignableTo(methEnv, thisType.Arguments[0]))
                                            throw new InvalidOperationException
                                                ("constrained type is not assignable to method's first argument type");
                                    }
                                    else
                                    {
                                        // *Case 1* Morally we deref the argument pointer and box the contents,
                                        // but since no supertype of a value type may mutate the underlying value,
                                        // we don't need to take a copy of the value when boxing, so in practice
                                        // this is a no-op
                                        if (!constrainedBox.IsAssignableTo(methEnv, thisType))
                                            throw new InvalidOperationException
                                                ("constrained type is not assignable to method's first argument type");
                                    }
                                }
                                else if (cs is ReferenceTypeStyle)
                                {
                                    // *Case 2* We dereference the pointer and pass the object reference
                                    if (!methi.Constrained.IsAssignableTo(methEnv, thisType))
                                        throw new InvalidOperationException
                                            ("constrained type is not assignable to method's first argument type");
                                }
                                else if (cs is ParameterTypeStyle)
                                {
                                    // Since we are calling an instance method, we know the first argument cannot be
                                    // a "naked" type parameter, but is either a class or an interface.
                                    // We must decide between cases 1 and 2 above at runtime, but checking as
                                    // per case 1 is sufficient now.
                                    // NOTE: As for box/classcast/isinst below, if the parameter is
                                    // instantiated to a reference type then the type box type is considered
                                    // equivalent to the underyling reference type.
                                    if (!constrainedBox.IsAssignableTo(methEnv, thisType))
                                        throw new InvalidOperationException
                                            ("constrained type is not assignable to method's first argument type");
                                }
                                else
                                    throw new InvalidOperationException
                                        ("constrained must be value, reference or parameter type");

                                state.PeekExpectedType(sig.Parameters.Count - 1, constrainedPtr, changed);
                            }
                            else if (sig.Parameters.Count > 0)
                                state.PeekExpectedType(sig.Parameters.Count - 1, sig.Parameters[0], changed);
                            if (sig.Result == null)
                                return state.Pop(sig.Parameters.Count);
                            else
                                return state.PopPushType(sig.Parameters.Count, sig.Result, BottomPT);
                        }
                    case MethodOp.Ldftn:
                        {
                            // NOTE: Verified CLR allows only the two "blessed" sequences:
                            //   dup; ldvirtftn; newobj <delegate ctor>
                            //   ldftn; newobj <delegate ctor>
                            // It is thus possible to check the delegate will capture an instance which
                            // implements the loaded method. However, we don't check that here.
                            if (methi.IsVirtual)
                            {
                                if (methi.Method.IsStatic)
                                    throw new InvalidOperationException("cannot ldvirtftn of a static method");
                                var objectType = default(TypeRef);
                                if (sig.Parameters[0].Style(methEnv) is ManagedPointerTypeStyle)
                                    // Object should be a box
                                    objectType = methEnv.Global.BoxTypeConstructorRef.ApplyTo(sig.Parameters[0].Arguments[0]);
                                else
                                    // Object should match parameter
                                    objectType = sig.Parameters[0];
                                state.PeekExpectedType(0, objectType, changed);
                                return state.PopPushType(1, sig.WithoutThis().ToCodePointer(methEnv.Global), BottomPT);
                            }
                            else
                            {
                                if (methi.Method.IsStatic)
                                    return state.PushType(sig.ToCodePointer(methEnv.Global), BottomPT);
                                else
                                    return state.PushType(sig.WithoutThis().ToCodePointer(methEnv.Global), BottomPT);
                            }
                        }
                    case MethodOp.Newobj:
                        {
                            if (methi.Method.IsStatic || sig.Result != null)
                                throw new InvalidOperationException("not a constructor");
                            for (var i = sig.Parameters.Count - 1; i >= 1; i--)
                                state.PeekExpectedType(sig.Parameters.Count - 1 - i, sig.Parameters[i], changed);
                            // First argument to constructor is created by runtime. If definining type is
                            // a value type, first argument will be a pointer, but result left on stack
                            // will be the value itself.
                            return state.PopPushType(sig.Parameters.Count - 1, methi.Method.DefiningType, BottomPT);
                        }
                    case MethodOp.Ldtoken:
                        return state.PushType(global.RuntimeMethodHandleRef, BottomPT);
                    default:
                        throw new ArgumentOutOfRangeException();
                    }
                }
            case InstructionFlavor.Type:
                {
                    var typei = (TypeInstruction)instruction;
                    switch (typei.Op)
                    {
                    case TypeOp.Ldobj:
                        state.PeekReadPointerType(0, typei.Type);
                        return state.PopPushType(1, typei.Type, BottomPT);
                    case TypeOp.Stobj:
                        state.PeekExpectedType(0, typei.Type, changed);
                        state.PeekWritePointerType(1, typei.Type);
                        return state.Pop(2);
                    case TypeOp.Cpobj:
                        state.PeekReadPointerType(0, typei.Type);
                        state.PeekWritePointerType(1, typei.Type);
                        return state.Pop(2);
                    case TypeOp.Newarr:
                        state.PeekIndexType(0);
                        return state.PopPushType(1, methEnv.Global.ArrayTypeConstructorRef.ApplyTo(typei.Type), BottomPT);
                    case TypeOp.Initobj:
                        state.PeekWritePointerType(0, typei.Type);
                        return state.Pop(1);
                    case TypeOp.Castclass:
                    case TypeOp.Isinst:
                    case TypeOp.Box:
                        {
                            var resultType = default(TypeRef);
                            var s = typei.Type.Style(methEnv);
                            if (s is NullableTypeStyle)
                                resultType = methEnv.Global.BoxTypeConstructorRef.ApplyTo(typei.Type.Arguments[0]);
                            else if (s is ValueTypeStyle)
                                resultType = methEnv.Global.BoxTypeConstructorRef.ApplyTo(typei.Type);
                            else if (s is ReferenceTypeStyle)
                                resultType = typei.Type;
                            else if (s is ParameterTypeStyle)
                                // NOTE: As for constrained call above, if type parameter is instantitated to
                                // a ref type, then this box type is considered equivalent to the
                                // underlying reference type.
                                resultType = methEnv.Global.BoxTypeConstructorRef.ApplyTo(typei.Type);
                            else
                                throw new InvalidOperationException
                                    ("can only box/cast to reference, value or parameter type");
                            if (typei.Op == TypeOp.Box)
                                state.PeekExpectedType(0, typei.Type, changed);
                            else
                                state.PeekReferenceType(0);
                            return state.PopPushType(1, resultType, BottomPT);
                        }
                    case TypeOp.Unbox:
                        if (!(typei.Type.Style(methEnv) is ValueTypeStyle))
                            // Parameter types are not allowed
                            throw new InvalidOperationException("type must be a value type");
                        state.PeekBoxedType(0, typei.Type, changed);
                        return state.PopPushType(1, methEnv.Global.ManagedPointerTypeConstructorRef.ApplyTo(typei.Type), HeapPT);
                    case TypeOp.UnboxAny:
                        {
                            var s = typei.Type.Style(methEnv);
                            if (s is ValueTypeStyle)
                                state.PeekBoxedType(0, typei.Type, changed);
                            else if (!(s is ReferenceTypeStyle) && !(s is ParameterTypeStyle))
                                throw new InvalidOperationException("type must be value, reference or parameter type");
                            return state.PopPushType(1, typei.Type, BottomPT);
                        }
                    case TypeOp.Ldtoken:
                        return state.PushType(global.RuntimeTypeHandleRef, BottomPT);
                    case TypeOp.Ldelem:
                        state.PeekIndexType(0);
                        state.PeekReadArrayType(1, typei.Type, false);
                        return state.PopPushType(2, typei.Type, BottomPT);
                    case TypeOp.Stelem:
                        state.PeekExpectedType(0, typei.Type, changed);
                        state.PeekIndexType(1);
                        state.PeekWriteArrayType(2, typei.Type);
                        return state.Pop(3);
                    default:
                        throw new ArgumentOutOfRangeException();
                    }
                }
            case InstructionFlavor.LdElemAddr:
                {
                    var ldelemai = (LdElemAddrInstruction)instruction;
                    state.PeekIndexType(0);
                    // WARNING: May prematurely fail for non-readonly loads
                    state.PeekReadArrayType(1, ldelemai.Type, !ldelemai.IsReadonly);
                    return state.PopPushType(2, methEnv.Global.ManagedPointerTypeConstructorRef.ApplyTo(ldelemai.Type), HeapPT);
                }
            case InstructionFlavor.LdInt32:
                return state.PushType(global.Int32Ref, BottomPT);
            case InstructionFlavor.LdInt64:
                return state.PushType(global.Int64Ref, BottomPT);
            case InstructionFlavor.LdSingle:
                return state.PushType(global.DoubleRef, BottomPT);
            case InstructionFlavor.LdDouble:
                return state.PushType(global.DoubleRef, BottomPT);
            case InstructionFlavor.LdString:
                return state.PushType(global.StringRef, BottomPT);
            case InstructionFlavor.Arith:
                {
                    var arithi = (ArithInstruction)instruction;
                    switch (arithi.Op)
                    {
                    case ArithOp.Add:
                    case ArithOp.Sub:
                    case ArithOp.Mul:
                    case ArithOp.Div:
                    case ArithOp.Rem:
                        // NOTE: May capture skolemized types
                        arithi.Type = state.Peek2NumberTypes(0, true);
                        return state.PopPushType(2, arithi.Type, BottomPT);
                    case ArithOp.Neg:
                        // NOTE: May capture skolemized types
                        arithi.Type = state.PeekNumberType(0, true);
                        // Changing underlying value, so pop/push explicitly
                        return state.PopPushType(1, arithi.Type, BottomPT);
                    case ArithOp.BitAnd:
                    case ArithOp.BitOr:
                    case ArithOp.BitXor:
                        // NOTE: May capture skolemized types
                        arithi.Type = state.Peek2NumberTypes(0, false);
                        return state.PopPushType(2, arithi.Type, BottomPT);
                    case ArithOp.BitNot:
                        // NOTE: May capture skolemized types
                        arithi.Type = state.PeekNumberType(0, false);
                        // Changing underlying value, so pop/push explicitly
                        return state.PopPushType(1, arithi.Type, BottomPT);
                    case ArithOp.Shl:
                    case ArithOp.Shr:
                        state.PeekExpectedType(0, global.Int32Ref, changed);
                        // NOTE: May capture skolemized types
                        arithi.Type = state.PeekNumberType(1, false);
                        // Changing underlying value, so pop/push explicitly
                        return state.PopPushType(2, arithi.Type, BottomPT);
                    default:
                        throw new ArgumentOutOfRangeException();
                    }
                }
            case InstructionFlavor.Conv:
                {
                    var convi = (ConvInstruction)instruction;
                    var mustBeInteger = (!convi.WithOverflow && convi.IsSourceUnsigned &&
                                         convi.TargetNumberFlavor == NumberFlavor.Double);
                    // NOTE: May capture skolemized types
                    convi.SourceType = state.PeekNumberType(0, !mustBeInteger);
                    return state.PopPushType(1, TypeRef.NumberFrom(methEnv.Global, convi.TargetNumberFlavor), BottomPT);
                }
            case InstructionFlavor.Try:
                {
                    var tryi = (TryInstruction)instruction;
                    // Isolation:
                    //  - There is no way for the current stack shape to influence or be influenced by
                    //    inference of the try, since the current stack shape must be empty.
                    //  - There is no way for the try to influence the result stack shape, since it must be
                    //    empty.
                    //  - However pointers in arguments and locals may propogate into and out of try body
                    //    via exceptional transitions. The latter are delt with separately.
                    if (state.Depth != 0)
                        throw new InvalidOperationException("stack should be empty");
                    var newState = ForwardBlock
                        (new TryBodyInstructionContext(context, index, tryi.Body), state, changed);
                    for (var j = 0; j < tryi.Handlers.Count; j++)
                    {
                        var h = tryi.Handlers[j];
                        var handlerContext = new TryHandlerInstructionContext(context, index, h.Body, j);
                        var initHandlerState = new MachineState
                            (methEnv, method.ValueParameters.Count, method.Locals.Count);
                        switch (h.Flavor)
                        {
                        case HandlerFlavor.Catch:
                            {
                                var catchh = (CatchTryInstructionHandler)h;
                                ForwardBlock(handlerContext, initHandlerState.PushType(catchh.Type, BottomPT), changed);
                                break;
                            }
                        case HandlerFlavor.Filter:
                            throw new NotSupportedException("filter handler blocks");
                        case HandlerFlavor.Fault:
                        case HandlerFlavor.Finally:
                            ForwardBlock(handlerContext, initHandlerState, changed);
                            break;
                        default:
                            throw new ArgumentOutOfRangeException();
                        }
                    }
                    return newState;
                }
            case InstructionFlavor.IfThenElsePseudo:
            case InstructionFlavor.ShortCircuitingPseudo:
            case InstructionFlavor.StructuralSwitchPseudo:
            case InstructionFlavor.LoopPseudo:
            case InstructionFlavor.WhileDoPseudo:
            case InstructionFlavor.DoWhilePseudo:
            case InstructionFlavor.LoopControlPseudo:
                throw new InvalidOperationException("no machine state inference for psuedo-instructions");
            default:
                throw new ArgumentOutOfRangeException();
            }
        }
예제 #15
0
 private void UnifyAfterState(MachineState state, int offset, BoolRef changed)
 {
     var existing = default(MachineState);
     if (offsetToAfterState.TryGetValue(offset, out existing))
         existing.Unify(state, changed);
     else
     {
         offsetToAfterState.Add(offset, state);
         changed.Set();
     }
 }
예제 #16
0
        // ----------------------------------------------------------------------
        // Entry point
        // ----------------------------------------------------------------------

        public void Infer()
        {
            var instructions = method.Instructions(methEnv.Global);
            var rootContext = new InstructionContext(null, -1, instructions);
            var initState = new MachineState(methEnv, method.ValueParameters.Count, method.Locals.Count);
            var effectiveTransitions = new Set<SourceTarget>();

            AddEffectiveBlockTransitions(effectiveTransitions, rootContext);
            if (tracer != null)
                tracer.Trace
                    ("Effective transitions",
                     w2 =>
                         {
                             foreach (var st in effectiveTransitions)
                             {
                                 st.Append(w2);
                                 w2.EndLine();
                             }
                         });

            var changed = new BoolRef();
            var i = 0;
            do
            {
                changed.Clear();
                ForwardBlock(rootContext, initState, changed);
                BackwardBlock(rootContext, changed);

                foreach (var st in effectiveTransitions)
                {
                    var sourceState = st.Source.BeforeState;
                    var targetState = default(MachineState);
                    if (!offsetToBeforeState.TryGetValue(st.Target, out targetState))
                        throw new InvalidOperationException("no state for target offset");
                    sourceState.SourceToTargetTransition(targetState, changed);
                }

                if (tracer != null)
                {
                    if (changed.Value)
                        tracer.Trace("After machine state inference iteration " + i++, instructions.Append);
                    else
                        tracer.AppendLine("Fixed point after iteration " + i);
                }
            }
            while (changed.Value);
        }