Esempio n. 1
0
 /// <summary>
 /// Lift a C# comparison.
 ///
 /// The output instructions should evaluate to <c>false</c> when any of the <c>nullableVars</c> is <c>null</c>.
 /// Otherwise, the output instruction should evaluate to the same value as the input instruction.
 /// The output instruction should have the same side-effects (incl. exceptions being thrown) as the input instruction.
 /// This means unlike LiftNormal(), we cannot rely on the input instruction not being evaluated if
 /// a variable is <c>null</c>.
 /// </summary>
 Comp LiftCSharpComparison(Comp comp, ComparisonKind newComparisonKind)
 {
     var(left, leftBits)   = DoLift(comp.Left);
     var(right, rightBits) = DoLift(comp.Right);
     if (left != null && right == null && SemanticHelper.IsPure(comp.Right.Flags))
     {
         // Embed non-nullable pure expression in lifted expression.
         right = comp.Right.Clone();
     }
     if (left == null && right != null && SemanticHelper.IsPure(comp.Left.Flags))
     {
         // Embed non-nullable pure expression in lifted expression.
         left = comp.Left.Clone();
     }
     // due to the restrictions on side effects, we only allow instructions that are pure after lifting.
     // (we can't check this before lifting due to the calls to GetValueOrDefault())
     if (left != null && right != null && SemanticHelper.IsPure(left.Flags) && SemanticHelper.IsPure(right.Flags))
     {
         var bits = leftBits ?? rightBits;
         if (rightBits != null)
         {
             bits.UnionWith(rightBits);
         }
         if (!bits.All(0, nullableVars.Count))
         {
             // don't lift if a nullableVar doesn't contribute to the result
             return(null);
         }
         context.Step("NullableLiftingTransform: C# comparison", comp);
         return(new Comp(newComparisonKind, ComparisonLiftingKind.CSharp, comp.InputType, comp.Sign, left, right));
     }
     return(null);
 }
Esempio n. 2
0
 public void Run(ILFunction function, ILTransformContext context)
 {
     try {
         if (this.context != null || this.currentFunction != null)
         {
             throw new InvalidOperationException("Reentrancy in " + nameof(TransformDisplayClassUsage));
         }
         this.context = context;
         var decompilationContext = new SimpleTypeResolveContext(context.Function.Method);
         // Traverse nested functions in post-order:
         // Inner functions are transformed before outer functions
         foreach (var f in function.Descendants.OfType <ILFunction>())
         {
             foreach (var v in f.Variables.ToArray())
             {
                 if (HandleMonoStateMachine(function, v, decompilationContext, f))
                 {
                     continue;
                 }
                 if (!(v.IsSingleDefinition && v.StoreInstructions.SingleOrDefault() is StLoc inst))
                 {
                     continue;
                 }
                 if (IsClosureInit(inst, out ITypeDefinition closureType))
                 {
                     // TODO : figure out whether it is a mono compiled closure, without relying on the type name
                     bool isMono = f.StateMachineCompiledWithMono || closureType.Name.Contains("AnonStorey");
                     displayClasses.Add(inst.Variable, new DisplayClass {
                         IsMono       = isMono,
                         Initializer  = inst,
                         Variable     = v,
                         Definition   = closureType,
                         Variables    = new Dictionary <IField, DisplayClassVariable>(),
                         CaptureScope = isMono && IsMonoNestedCaptureScope(closureType) ? null : inst.Variable.CaptureScope
                     });
                     instructionsToRemove.Add(inst);
                 }
             }
             foreach (var displayClass in displayClasses.Values.OrderByDescending(d => d.Initializer.StartILOffset).ToArray())
             {
                 context.Step($"Transform references to " + displayClass.Variable, displayClass.Initializer);
                 this.currentFunction = f;
                 VisitILFunction(f);
             }
         }
         context.Step($"Remove instructions", function);
         foreach (var store in instructionsToRemove)
         {
             if (store.Parent is Block containingBlock)
             {
                 containingBlock.Instructions.Remove(store);
             }
         }
     } finally {
         instructionsToRemove.Clear();
         displayClasses.Clear();
         this.context         = null;
         this.currentFunction = null;
     }
 }
Esempio n. 3
0
 private void ReplaceReferencesToDisplayClassThis(Dictionary <MethodDefinitionHandle, LocalFunctionInfo> .ValueCollection localFunctions)
 {
     foreach (var info in localFunctions)
     {
         var localFunction = info.Definition;
         if (localFunction.Method.IsStatic)
         {
             continue;
         }
         var thisVar = localFunction.Variables.SingleOrDefault(VariableKindExtensions.IsThis);
         if (thisVar == null)
         {
             continue;
         }
         var compatibleArgument = FindCompatibleArgument(
             info,
             info.UseSites.OfType <CallInstruction>().Select(u => u.Arguments[0]).ToArray(),
             ignoreStructure: true
             );
         if (compatibleArgument == null)
         {
             continue;
         }
         context.Step($"Replace 'this' with {compatibleArgument}", localFunction);
         localFunction.AcceptVisitor(new DelegateConstruction.ReplaceDelegateTargetVisitor(compatibleArgument, thisVar));
         DetermineCaptureAndDeclarationScope(info, -1, compatibleArgument);
     }
 }
Esempio n. 4
0
 static void RunOnBlock(Block block, ILTransformContext context, HashSet <ILVariable> splitVariables = null)
 {
     for (int i = 0; i < block.Instructions.Count; i++)
     {
         if (block.Instructions[i].MatchStLoc(out ILVariable v, out ILInstruction copiedExpr))
         {
             if (v.IsSingleDefinition && v.LoadCount == 0 && v.Kind == VariableKind.StackSlot)
             {
                 // dead store to stack
                 if (SemanticHelper.IsPure(copiedExpr.Flags))
                 {
                     // no-op -> delete
                     context.Step("remove dead store to stack: no-op -> delete", block.Instructions[i]);
                     block.Instructions.RemoveAt(i);
                     // This can open up new inlining opportunities:
                     int c = ILInlining.InlineInto(block, i, InliningOptions.None, context: context);
                     i -= c + 1;
                 }
                 else
                 {
                     // evaluate the value for its side-effects
                     context.Step("remove dead store to stack: evaluate the value for its side-effects", block.Instructions[i]);
                     copiedExpr.AddILRange(block.Instructions[i]);
                     block.Instructions[i] = copiedExpr;
                 }
             }
             else if (v.IsSingleDefinition && CanPerformCopyPropagation(v, copiedExpr, splitVariables))
             {
                 DoPropagate(v, copiedExpr, block, ref i, context);
             }
         }
     }
 }
Esempio n. 5
0
        void IILTransform.Run(ILFunction function, ILTransformContext context)
        {
            if (!context.Settings.AnonymousMethods)
            {
                return;
            }
            this.context = context;
            this.decompilationContext = new SimpleTypeResolveContext(function.Method);
            var orphanedVariableInits    = new List <ILInstruction>();
            var targetsToReplace         = new List <IInstructionWithVariableOperand>();
            var translatedDisplayClasses = new HashSet <ITypeDefinition>();
            var cancellationToken        = context.CancellationToken;

            foreach (var inst in function.Descendants)
            {
                cancellationToken.ThrowIfCancellationRequested();
                if (inst is NewObj call)
                {
                    context.StepStartGroup($"TransformDelegateConstruction {call.StartILOffset}", call);
                    ILFunction f = TransformDelegateConstruction(call, out ILInstruction target);
                    if (f != null && target is IInstructionWithVariableOperand instWithVar)
                    {
                        if (instWithVar.Variable.Kind == VariableKind.Local)
                        {
                            instWithVar.Variable.Kind = VariableKind.DisplayClassLocal;
                        }
                        targetsToReplace.Add(instWithVar);
                    }
                    context.StepEndGroup();
                }
                if (inst.MatchStLoc(out ILVariable targetVariable, out ILInstruction value))
                {
                    var newObj = value as NewObj;
                    // TODO : it is probably not a good idea to remove *all* display-classes
                    // is there a way to minimize the false-positives?
                    if (newObj != null && IsInSimpleDisplayClass(newObj.Method))
                    {
                        targetVariable.CaptureScope = BlockContainer.FindClosestContainer(inst);
                        targetsToReplace.Add((IInstructionWithVariableOperand)inst);
                        translatedDisplayClasses.Add(newObj.Method.DeclaringTypeDefinition);
                    }
                }
            }
            foreach (var target in targetsToReplace.OrderByDescending(t => ((ILInstruction)t).StartILOffset))
            {
                context.Step($"TransformDisplayClassUsages {target.Variable}", (ILInstruction)target);
                function.AcceptVisitor(new TransformDisplayClassUsages(function, target, target.Variable.CaptureScope, orphanedVariableInits, translatedDisplayClasses));
            }
            context.Step($"Remove orphanedVariableInits", function);
            foreach (var store in orphanedVariableInits)
            {
                if (store.Parent is Block containingBlock)
                {
                    containingBlock.Instructions.Remove(store);
                }
            }
        }
Esempio n. 6
0
        /// <summary>
        /// The transform works like this:
        ///
        /// <para>
        /// local functions can either be used in method calls, i.e., call and callvirt instructions,
        /// or can be used as part of the "delegate construction" pattern, i.e., <c>newobj Delegate(&lt;target-expression&gt;, ldftn &lt;method&gt;)</c>.
        /// </para>
        /// As local functions can be declared practically anywhere, we have to take a look at all use-sites and infer the declaration location from that. Use-sites can be call, callvirt and ldftn instructions.
        /// After all use-sites are collected we construct the ILAst of the local function and add it to the parent function.
        /// Then all use-sites of the local-function are transformed to a call to the <c>LocalFunctionMethod</c> or a ldftn of the <c>LocalFunctionMethod</c>.
        /// In a next step we handle all nested local functions.
        /// After all local functions are transformed, we move all local functions that capture any variables to their respective declaration scope.
        /// </summary>
        public void Run(ILFunction function, ILTransformContext context)
        {
            if (!context.Settings.LocalFunctions)
            {
                return;
            }
            this.context        = context;
            this.resolveContext = new SimpleTypeResolveContext(function.Method);
            var localFunctions    = new Dictionary <MethodDefinitionHandle, LocalFunctionInfo>();
            var cancellationToken = context.CancellationToken;

            // Find all local functions declared inside this method, including nested local functions or local functions declared in lambdas.
            FindUseSites(function, context, localFunctions);
            foreach (var(method, info) in localFunctions)
            {
                cancellationToken.ThrowIfCancellationRequested();
                var firstUseSite = info.UseSites[0];
                context.StepStartGroup($"Transform " + info.Definition.Name, info.Definition);
                try {
                    var localFunction = info.Definition;
                    if (!localFunction.Method.IsStatic)
                    {
                        var target = firstUseSite.Arguments[0];
                        context.Step($"Replace 'this' with {target}", localFunction);
                        var thisVar = localFunction.Variables.SingleOrDefault(VariableKindExtensions.IsThis);
                        localFunction.AcceptVisitor(new DelegateConstruction.ReplaceDelegateTargetVisitor(target, thisVar));
                    }

                    foreach (var useSite in info.UseSites)
                    {
                        context.Step("Transform use site at " + useSite.StartILOffset, useSite);
                        if (useSite.OpCode == OpCode.NewObj)
                        {
                            TransformToLocalFunctionReference(localFunction, useSite);
                        }
                        else
                        {
                            DetermineCaptureAndDeclarationScope(localFunction, useSite);
                            TransformToLocalFunctionInvocation(localFunction.ReducedMethod, useSite);
                        }
                    }

                    if (localFunction.DeclarationScope == null)
                    {
                        localFunction.DeclarationScope = (BlockContainer)function.Body;
                    }
                    else if (localFunction.DeclarationScope != function.Body && localFunction.DeclarationScope.Parent is ILFunction declaringFunction)
                    {
                        function.LocalFunctions.Remove(localFunction);
                        declaringFunction.LocalFunctions.Add(localFunction);
                    }
                } finally {
                    context.StepEndGroup();
                }
            }
        }
Esempio n. 7
0
        protected internal override void VisitStLoc(StLoc inst)
        {
            base.VisitStLoc(inst);

            if (inst.Variable.Kind == VariableKind.Local && inst.Variable.IsSingleDefinition && inst.Variable.LoadCount == 0 && inst.Value is StLoc)
            {
                context.Step($"Remove unused variable assignment {inst.Variable.Name}", inst);
                inst.ReplaceWith(inst.Value);
            }
        }
 public void Run(ILFunction function, ILTransformContext context)
 {
     try {
         if (this.context != null || this.currentFunction != null)
         {
             throw new InvalidOperationException("Reentrancy in " + nameof(TransformDisplayClassUsage));
         }
         this.context = context;
         var decompilationContext = new SimpleTypeResolveContext(context.Function.Method);
         // Traverse nested functions in post-order:
         // Inner functions are transformed before outer functions
         foreach (var f in function.Descendants.OfType <ILFunction>())
         {
             foreach (var v in f.Variables.ToArray())
             {
                 if (HandleMonoStateMachine(function, v, decompilationContext, f))
                 {
                     continue;
                 }
                 if (IsClosure(v, out ITypeDefinition closureType, out var inst))
                 {
                     AddOrUpdateDisplayClass(f, v, closureType, inst, localFunctionClosureParameter: false);
                 }
                 if (context.Settings.LocalFunctions && f.Kind == ILFunctionKind.LocalFunction && v.Kind == VariableKind.Parameter && v.Index > -1 && f.Method.Parameters[v.Index.Value] is IParameter p && LocalFunctionDecompiler.IsClosureParameter(p, decompilationContext))
                 {
                     AddOrUpdateDisplayClass(f, v, ((ByReferenceType)p.Type).ElementType.GetDefinition(), f.Body, localFunctionClosureParameter: true);
                 }
             }
             foreach (var displayClass in displayClasses.Values.OrderByDescending(d => d.Initializer.StartILOffset).ToArray())
             {
                 context.Step($"Transform references to " + displayClass.Variable, displayClass.Initializer);
                 this.currentFunction = f;
                 VisitILFunction(f);
             }
         }
         if (instructionsToRemove.Count > 0)
         {
             context.Step($"Remove instructions", function);
             foreach (var store in instructionsToRemove)
             {
                 if (store.Parent is Block containingBlock)
                 {
                     containingBlock.Instructions.Remove(store);
                 }
             }
         }
         RemoveDeadVariableInit.ResetHasInitialValueFlag(function, context);
     } finally {
         instructionsToRemove.Clear();
         displayClasses.Clear();
         this.context         = null;
         this.currentFunction = null;
     }
 }
Esempio n. 9
0
        ILInstruction Lift(IfInstruction ifInst, ILInstruction trueInst, ILInstruction falseInst)
        {
            if (!MatchNullableCtor(trueInst, out var utype, out var exprToLift))
            {
                return(null);
            }
            if (!MatchNull(falseInst, utype))
            {
                return(null);
            }
            ILInstruction lifted;

            if (nullableVars.Count == 1 && MatchGetValueOrDefault(exprToLift, nullableVars[0]))
            {
                // v != null ? call GetValueOrDefault(ldloca v) : null
                // => conv.nop.lifted(ldloc v)
                // This case is handled separately from DoLift() because
                // that doesn't introduce nop-conversions.
                context.Step("if => conv.nop.lifted", ifInst);
                var inputUType = NullableType.GetUnderlyingType(nullableVars[0].Type);
                lifted = new Conv(
                    new LdLoc(nullableVars[0]),
                    inputUType.GetStackType(), inputUType.GetSign(), utype.ToPrimitiveType(),
                    checkForOverflow: false,
                    isLifted: true
                    )
                {
                    ILRange = ifInst.ILRange
                };
            }
            else
            {
                context.Step("NullableLiftingTransform.DoLift", ifInst);
                BitSet bits;
                (lifted, bits) = DoLift(exprToLift);
                if (lifted != null && !bits.All(0, nullableVars.Count))
                {
                    // don't lift if a nullableVar doesn't contribute to the result
                    lifted = null;
                }
            }
            if (lifted != null)
            {
                Debug.Assert(lifted is ILiftableInstruction liftable && liftable.IsLifted &&
                             liftable.UnderlyingResultType == exprToLift.ResultType);
            }
            return(lifted);
        }
Esempio n. 10
0
        internal static bool DoInline(ILVariable v, StLoc originalStore, LdLoc loadInst, InliningOptions options, ILTransformContext context)
        {
            if ((options & InliningOptions.Aggressive) == 0 && originalStore.ILStackWasEmpty)
            {
                return(false);
            }
            context.Step($"Introduce named argument '{v.Name}'", originalStore);
            var call = (CallInstruction)loadInst.Parent;

            if (!(call.Parent is Block namedArgBlock) || namedArgBlock.Kind != BlockKind.CallWithNamedArgs)
            {
                // create namedArgBlock:
                namedArgBlock = new Block(BlockKind.CallWithNamedArgs);
                call.ReplaceWith(namedArgBlock);
                namedArgBlock.FinalInstruction = call;
                if (call.IsInstanceCall)
                {
                    IType thisVarType = call.Method.DeclaringType;
                    if (CallInstruction.ExpectedTypeForThisPointer(thisVarType) == StackType.Ref)
                    {
                        thisVarType = new ByReferenceType(thisVarType);
                    }
                    var function   = call.Ancestors.OfType <ILFunction>().First();
                    var thisArgVar = function.RegisterVariable(VariableKind.NamedArgument, thisVarType, "this_arg");
                    namedArgBlock.Instructions.Add(new StLoc(thisArgVar, call.Arguments[0]));
                    call.Arguments[0] = new LdLoc(thisArgVar);
                }
            }
            v.Kind = VariableKind.NamedArgument;
            namedArgBlock.Instructions.Insert(call.IsInstanceCall ? 1 : 0, originalStore);
            return(true);
        }
Esempio n. 11
0
 public void Run(ILFunction function, ILTransformContext context)
 {
     try {
         if (this.context != null)
         {
             throw new InvalidOperationException("Reentrancy in " + nameof(TransformDisplayClassUsage));
         }
         this.context = context;
         var decompilationContext = new SimpleTypeResolveContext(context.Function.Method);
         // Traverse nested functions in post-order:
         // Inner functions are transformed before outer functions
         foreach (var f in function.Descendants.OfType <ILFunction>())
         {
             foreach (var v in f.Variables.ToArray())
             {
                 if (context.Settings.YieldReturn && HandleMonoStateMachine(function, v, decompilationContext, f))
                 {
                     continue;
                 }
                 if ((context.Settings.AnonymousMethods || context.Settings.ExpressionTrees) && IsClosure(context, v, out ITypeDefinition closureType, out var inst))
                 {
                     if (!CanRemoveAllReferencesTo(context, v))
                     {
                         continue;
                     }
                     if (inst is StObj || inst is StLoc)
                     {
                         instructionsToRemove.Add(inst);
                     }
                     AddOrUpdateDisplayClass(f, v, closureType, inst, localFunctionClosureParameter: false);
                     continue;
                 }
                 if (context.Settings.LocalFunctions && f.Kind == ILFunctionKind.LocalFunction && v.Kind == VariableKind.Parameter && v.Index > -1 && f.Method.Parameters[v.Index.Value] is IParameter p && LocalFunctionDecompiler.IsClosureParameter(p, decompilationContext))
                 {
                     AddOrUpdateDisplayClass(f, v, ((ByReferenceType)p.Type).ElementType.GetDefinition(), f.Body, localFunctionClosureParameter: true);
                     continue;
                 }
                 AnalyzeUseSites(v);
             }
         }
         VisitILFunction(function);
         if (instructionsToRemove.Count > 0)
         {
             context.Step($"Remove instructions", function);
             foreach (var store in instructionsToRemove)
             {
                 if (store.Parent is Block containingBlock)
                 {
                     containingBlock.Instructions.Remove(store);
                 }
             }
         }
         RemoveDeadVariableInit.ResetHasInitialValueFlag(function, context);
     } finally {
         instructionsToRemove.Clear();
         displayClasses.Clear();
         fieldAssignmentsWithVariableValue.Clear();
         this.context = null;
     }
 }
Esempio n. 12
0
        static void DoPropagate(ILVariable v, ILInstruction copiedExpr, Block block, ref int i, ILTransformContext context)
        {
            context.Step($"Copy propagate {v.Name}", copiedExpr);
            // un-inline the arguments of the ldArg instruction
            ILVariable[] uninlinedArgs = new ILVariable[copiedExpr.Children.Count];
            for (int j = 0; j < uninlinedArgs.Length; j++)
            {
                var arg  = copiedExpr.Children[j];
                var type = context.TypeSystem.Compilation.FindType(arg.ResultType.ToKnownTypeCode());
                uninlinedArgs[j] = new ILVariable(VariableKind.StackSlot, type, arg.ResultType, arg.ILRange.Start)
                {
                    Name = "C_" + arg.ILRange.Start
                };
                block.Instructions.Insert(i++, new StLoc(uninlinedArgs[j], arg));
            }
            CollectionExtensions.AddRange(v.Function.Variables, uninlinedArgs);
            // perform copy propagation:
            foreach (var expr in v.LoadInstructions.ToArray())
            {
                var clone = copiedExpr.Clone();
                for (int j = 0; j < uninlinedArgs.Length; j++)
                {
                    clone.Children[j].ReplaceWith(new LdLoc(uninlinedArgs[j]));
                }
                expr.ReplaceWith(clone);
            }
            block.Instructions.RemoveAt(i);
            int c = ILInlining.InlineInto(block, i, aggressive: false, context: context);

            i -= c + 1;
        }
Esempio n. 13
0
        /// <summary>
        /// Introduce a named argument for 'arg' and evaluate it before the other arguments
        /// (except for the "this" pointer)
        /// </summary>
        internal static void IntroduceNamedArgument(ILInstruction arg, ILTransformContext context)
        {
            var call = (CallInstruction)arg.Parent;

            Debug.Assert(context.Function == call.Ancestors.OfType <ILFunction>().First());
            var v = context.Function.RegisterVariable(VariableKind.NamedArgument, arg.ResultType);

            context.Step($"Introduce named argument '{v.Name}'", arg);
            if (!(call.Parent is Block namedArgBlock) || namedArgBlock.Kind != BlockKind.CallWithNamedArgs)
            {
                // create namedArgBlock:
                namedArgBlock = new Block(BlockKind.CallWithNamedArgs);
                call.ReplaceWith(namedArgBlock);
                namedArgBlock.FinalInstruction = call;
                if (call.IsInstanceCall)
                {
                    IType thisVarType = call.Method.DeclaringType;
                    if (CallInstruction.ExpectedTypeForThisPointer(thisVarType) == StackType.Ref)
                    {
                        thisVarType = new ByReferenceType(thisVarType);
                    }
                    var thisArgVar = context.Function.RegisterVariable(VariableKind.NamedArgument, thisVarType, "this_arg");
                    namedArgBlock.Instructions.Add(new StLoc(thisArgVar, call.Arguments[0]));
                    call.Arguments[0] = new LdLoc(thisArgVar);
                }
            }
            int argIndex = arg.ChildIndex;

            Debug.Assert(call.Arguments[argIndex] == arg);
            namedArgBlock.Instructions.Insert(call.IsInstanceCall ? 1 : 0, new StLoc(v, arg));
            call.Arguments[argIndex] = new LdLoc(v);
        }
Esempio n. 14
0
 private bool TryTransform(Block block, int i, ILTransformContext context)
 {
     if (block.Instructions[i] is not StLoc {
         Variable : var s, Value : LdLoca {
             Variable : var v
         }
     } inst1)
     {
         return(false);
     }
     if (block.Instructions.ElementAtOrDefault(i + 1) is not StObj inst2)
     {
         return(false);
     }
     if (!(inst2.Target.MatchLdLoc(s) &&
           TypeUtils.IsCompatibleTypeForMemoryAccess(v.Type, inst2.Type) &&
           inst2.UnalignedPrefix == 0 &&
           !inst2.IsVolatile &&
           inst2.Value is DefaultValue))
     {
         return(false);
     }
     context.Step("LdLocaDupInitObjTransform", inst1);
     block.Instructions[i]     = new StLoc(v, inst2.Value).WithILRange(inst2);
     block.Instructions[i + 1] = inst1;
     return(true);
 }
Esempio n. 15
0
        public void Run(ILFunction function, ILTransformContext context)
        {
            foreach (var catchBlock in function.Descendants.OfType <TryCatchHandler>())
            {
                if (catchBlock.Filter is BlockContainer container &&
                    MatchCatchWhenEntryPoint(catchBlock.Variable, container, container.EntryPoint,
                                             out var exceptionType, out var exceptionSlot, out var whenConditionBlock) &&
                    exceptionType.GetStackType() == catchBlock.Variable.StackType)
                {
                    // set exceptionType
                    catchBlock.Variable.Type = exceptionType;
                    // Block entryPoint (incoming: 1)  {
                    //   stloc temp(isinst exceptionType(ldloc exceptionVar))
                    //   if (comp(ldloc temp != ldnull)) br whenConditionBlock
                    //   br falseBlock
                    // }
                    // =>
                    // Block entryPoint (incoming: 1)  {
                    //   stloc temp(ldloc exceptionSlot)
                    //   br whenConditionBlock
                    // }
                    var instructions = container.EntryPoint.Instructions;
                    if (instructions.Count == 3)
                    {
                        // stloc temp(isinst exceptionType(ldloc exceptionVar))
                        // if (comp(ldloc temp != ldnull)) br whenConditionBlock
                        // br falseBlock
                        context.Step($"Detected catch-when for {catchBlock.Variable.Name} (extra store)", instructions[0]);
                        ((StLoc)instructions[0]).Value = exceptionSlot;
                        instructions[1].ReplaceWith(new Branch(whenConditionBlock));
                        instructions.RemoveAt(2);
                        container.SortBlocks(deleteUnreachableBlocks: true);
                    }
                    else if (instructions.Count == 2)
                    {
                        // if (comp(isinst exceptionType(ldloc exceptionVar) != ldnull)) br whenConditionBlock
                        // br falseBlock
                        context.Step($"Detected catch-when for {catchBlock.Variable.Name}", instructions[0]);
                        instructions[0].ReplaceWith(new Branch(whenConditionBlock));
                        instructions.RemoveAt(1);
                        container.SortBlocks(deleteUnreachableBlocks: true);
                    }

                    PropagateExceptionVariable(context, catchBlock);
                }
            }
        }
Esempio n. 16
0
 internal static bool StObjToStLoc(StObj inst, ILTransformContext context)
 {
     if (inst.Target.MatchLdLoca(out ILVariable v) &&
         TypeUtils.IsCompatibleTypeForMemoryAccess(new ByReferenceType(v.Type), inst.Type) &&
         inst.UnalignedPrefix == 0 &&
         !inst.IsVolatile)
     {
         context.Step($"stobj(ldloca {v.Name}, ...) => stloc {v.Name}(...)", inst);
         inst.ReplaceWith(new StLoc(v, inst.Value));
         return(true);
     }
     return(false);
 }
Esempio n. 17
0
 internal static bool LdObjToLdLoc(LdObj inst, ILTransformContext context)
 {
     if (inst.Target.MatchLdLoca(out ILVariable v) &&
         TypeUtils.IsCompatibleTypeForMemoryAccess(v.Type, inst.Type) &&
         inst.UnalignedPrefix == 0 &&
         !inst.IsVolatile)
     {
         context.Step($"ldobj(ldloca {v.Name}) => ldloc {v.Name}", inst);
         inst.ReplaceWith(new LdLoc(v).WithILRange(inst));
         return(true);
     }
     return(false);
 }
Esempio n. 18
0
        /// <summary>
        /// Inlines the stloc instruction at block.Instructions[pos] into the next instruction.
        ///
        /// Note that this method does not check whether 'v' has only one use;
        /// the caller is expected to validate whether inlining 'v' has any effects on other uses of 'v'.
        /// </summary>
        public static bool InlineOne(StLoc stloc, InliningOptions options, ILTransformContext context)
        {
            ILVariable v     = stloc.Variable;
            Block      block = (Block)stloc.Parent;
            int        pos   = stloc.ChildIndex;

            if (DoInline(v, stloc.Value, block.Instructions.ElementAtOrDefault(pos + 1), options, context))
            {
                // Assign the ranges of the stloc instruction:
                stloc.Value.AddILRange(stloc);
                // Remove the stloc instruction:
                Debug.Assert(block.Instructions[pos] == stloc);
                block.Instructions.RemoveAt(pos);
                return(true);
            }
            else if (v.LoadCount == 0 && v.AddressCount == 0)
            {
                // The variable is never loaded
                if (SemanticHelper.IsPure(stloc.Value.Flags))
                {
                    // Remove completely if the instruction has no effects
                    // (except for reading locals)
                    context.Step("Remove dead store without side effects", stloc);
                    block.Instructions.RemoveAt(pos);
                    return(true);
                }
                else if (v.Kind == VariableKind.StackSlot)
                {
                    context.Step("Remove dead store, but keep expression", stloc);
                    // Assign the ranges of the stloc instruction:
                    stloc.Value.AddILRange(stloc);
                    // Remove the stloc, but keep the inner expression
                    stloc.ReplaceWith(stloc.Value);
                    return(true);
                }
            }
            return(false);
        }
Esempio n. 19
0
        /// <summary>
        /// Inlines 'expr' into 'next', if possible.
        ///
        /// Note that this method does not check whether 'v' has only one use;
        /// the caller is expected to validate whether inlining 'v' has any effects on other uses of 'v'.
        /// </summary>
        static bool DoInline(ILVariable v, ILInstruction inlinedExpression, ILInstruction next, InliningOptions options, ILTransformContext context)
        {
            var r = FindLoadInNext(next, v, inlinedExpression, options);

            if (r.Type == FindResultType.Found || r.Type == FindResultType.NamedArgument)
            {
                var loadInst = r.LoadInst;
                if (loadInst.OpCode == OpCode.LdLoca)
                {
                    if (!IsGeneratedValueTypeTemporary((LdLoca)loadInst, v, inlinedExpression))
                    {
                        return(false);
                    }
                }
                else
                {
                    Debug.Assert(loadInst.OpCode == OpCode.LdLoc);
                    bool aggressive = (options & InliningOptions.Aggressive) != 0;
                    if (!aggressive && v.Kind != VariableKind.StackSlot &&
                        !NonAggressiveInlineInto(next, r, inlinedExpression, v))
                    {
                        return(false);
                    }
                }

                if (r.Type == FindResultType.NamedArgument)
                {
                    NamedArgumentTransform.IntroduceNamedArgument(r.CallArgument, context);
                    // Now that the argument is evaluated early, we can inline as usual
                }

                context.Step($"Inline variable '{v.Name}'", inlinedExpression);
                // Assign the ranges of the ldloc instruction:
                inlinedExpression.AddILRange(loadInst);

                if (loadInst.OpCode == OpCode.LdLoca)
                {
                    // it was an ldloca instruction, so we need to use the pseudo-opcode 'addressof'
                    // to preserve the semantics of the compiler-generated temporary
                    Debug.Assert(((LdLoca)loadInst).Variable == v);
                    loadInst.ReplaceWith(new AddressOf(inlinedExpression, v.Type));
                }
                else
                {
                    loadInst.ReplaceWith(inlinedExpression);
                }
                return(true);
            }
            return(false);
        }
Esempio n. 20
0
 private bool DoTransform(Block block, ILTransformContext context)
 {
     if (!MatchBlock1(block, out var s, out int value, out var br))
     {
         return(false);
     }
     if (!MatchBlock2(br.TargetBlock, s, value, out var exitInst))
     {
         return(false);
     }
     context.Step("RemoveInfeasiblePath", br);
     br.ReplaceWith(exitInst.Clone());
     s.RemoveIfRedundant = true;
     return(true);
 }
Esempio n. 21
0
        /// <summary>
        /// Inlines 'expr' into 'next', if possible.
        ///
        /// Note that this method does not check whether 'v' has only one use;
        /// the caller is expected to validate whether inlining 'v' has any effects on other uses of 'v'.
        /// </summary>
        static bool DoInline(ILVariable v, ILInstruction inlinedExpression, ILInstruction next, InliningOptions options, ILTransformContext context)
        {
            var r = FindLoadInNext(next, v, inlinedExpression, out var loadInst);

            if (r == FindResult.Found)
            {
                if (loadInst.OpCode == OpCode.LdLoca)
                {
                    if (!IsGeneratedValueTypeTemporary(next, (LdLoca)loadInst, v, inlinedExpression))
                    {
                        return(false);
                    }
                }
                else
                {
                    Debug.Assert(loadInst.OpCode == OpCode.LdLoc);
                    bool aggressive = (options & InliningOptions.Aggressive) != 0;
                    if (!aggressive && v.Kind != VariableKind.StackSlot &&
                        !NonAggressiveInlineInto(next, loadInst, inlinedExpression, v))
                    {
                        return(false);
                    }
                }

                context.Step($"Inline variable '{v.Name}'", inlinedExpression);
                // Assign the ranges of the ldloc instruction:
                inlinedExpression.AddILRange(loadInst.ILRange);

                if (loadInst.OpCode == OpCode.LdLoca)
                {
                    // it was an ldloca instruction, so we need to use the pseudo-opcode 'addressof'
                    // to preserve the semantics of the compiler-generated temporary
                    loadInst.ReplaceWith(new AddressOf(inlinedExpression));
                }
                else
                {
                    loadInst.ReplaceWith(inlinedExpression);
                }
                return(true);
            }
            else if (r == FindResult.NamedArgument && (options & InliningOptions.IntroduceNamedArguments) != 0)
            {
                return(NamedArgumentTransform.DoInline(v, (StLoc)inlinedExpression.Parent, (LdLoc)loadInst,
                                                       options, context));
            }
            return(false);
        }
Esempio n. 22
0
 internal static ILInstruction HandleCall(Call inst, ILTransformContext context)
 {
     if (inst.Method.IsConstructor && !inst.Method.IsStatic && inst.Method.DeclaringType.Kind == TypeKind.Struct)
     {
         Debug.Assert(inst.Arguments.Count == inst.Method.Parameters.Count + 1);
         context.Step("Transform call to struct constructor", inst);
         // call(ref, ...)
         // => stobj(ref, newobj(...))
         var newObj = new NewObj(inst.Method);
         newObj.ILRange = inst.ILRange;
         newObj.Arguments.AddRange(inst.Arguments.Skip(1));
         var expr = new StObj(inst.Arguments[0], newObj, inst.Method.DeclaringType);
         inst.ReplaceWith(expr);
         return(expr);
     }
     return(null);
 }
Esempio n. 23
0
        /// <summary>
        /// stobj(target, binary.op(ldobj(target), ...))
        ///   where target is pure
        /// => compound.op(target, ...)
        /// </summary>
        /// <remarks>
        /// Called by ExpressionTransforms.
        /// </remarks>
        internal static bool HandleStObjCompoundAssign(StObj inst, ILTransformContext context)
        {
            if (!(UnwrapSmallIntegerConv(inst.Value, out var conv) is BinaryNumericInstruction binary))
            {
                return(false);
            }
            if (!(binary.Left is LdObj ldobj))
            {
                return(false);
            }
            if (!inst.Target.Match(ldobj.Target).Success)
            {
                return(false);
            }
            if (!SemanticHelper.IsPure(ldobj.Target.Flags))
            {
                return(false);
            }
            // ldobj.Type may just be 'int' (due to ldind.i4) when we're actually operating on a 'ref MyEnum'.
            // Try to determine the real type of the object we're modifying:
            IType targetType = ldobj.Target.InferType();

            if (targetType.Kind == TypeKind.Pointer || targetType.Kind == TypeKind.ByReference)
            {
                targetType = ((TypeWithElementType)targetType).ElementType;
                if (targetType.Kind == TypeKind.Unknown || targetType.GetSize() != ldobj.Type.GetSize())
                {
                    targetType = ldobj.Type;
                }
            }
            else
            {
                targetType = ldobj.Type;
            }
            if (!ValidateCompoundAssign(binary, conv, targetType))
            {
                return(false);
            }
            context.Step("compound assignment", inst);
            inst.ReplaceWith(new CompoundAssignmentInstruction(
                                 binary, binary.Left, binary.Right,
                                 targetType, CompoundAssignmentType.EvaluatesToNewValue));
            return(true);
        }
Esempio n. 24
0
 internal static bool LdObjToLdLoc(LdObj inst, ILTransformContext context)
 {
     if (inst.Target.MatchLdLoca(out ILVariable v) &&
         TypeUtils.IsCompatibleTypeForMemoryAccess(v.Type, inst.Type) &&
         inst.UnalignedPrefix == 0 &&
         !inst.IsVolatile)
     {
         context.Step($"ldobj(ldloca {v.Name}) => ldloc {v.Name}", inst);
         ILInstruction replacement = new LdLoc(v).WithILRange(inst);
         if (v.StackType == StackType.Unknown && inst.Type.Kind != TypeKind.Unknown)
         {
             replacement = new Conv(replacement, inst.Type.ToPrimitiveType(),
                                    checkForOverflow: false, Sign.None);
         }
         inst.ReplaceWith(replacement);
         return(true);
     }
     return(false);
 }
 protected internal override void VisitLdFlda(LdFlda inst)
 {
     base.VisitLdFlda(inst);
     // Get display class info
     if (!IsDisplayClassFieldAccess(inst, out _, out DisplayClass displayClass, out IField field))
         return;
     var keyField = (IField)field.MemberDefinition;
     var v = displayClass.VariablesToDeclare[keyField];
     context.Step($"Replace {field.Name} with captured variable {v.Name}", inst);
     ILVariable variable = v.GetOrDeclare();
     inst.ReplaceWith(new LdLoca(variable).WithILRange(inst));
     // add captured variable to all descendant functions from the declaring function to this use-site function
     foreach (var f in currentFunctions) {
         if (f == variable.Function)
             break;
         f.CapturedVariables.Add(variable);
     }
 }
Esempio n. 26
0
        /// <summary>
        /// Inlines 'expr' into 'next', if possible.
        ///
        /// Note that this method does not check whether 'v' has only one use;
        /// the caller is expected to validate whether inlining 'v' has any effects on other uses of 'v'.
        /// </summary>
        static bool DoInline(ILVariable v, ILInstruction inlinedExpression, ILInstruction next, bool aggressive, ILTransformContext context)
        {
            ILInstruction loadInst;

            if (FindLoadInNext(next, v, inlinedExpression, out loadInst) == true)
            {
                if (loadInst.OpCode == OpCode.LdLoca)
                {
                    if (!IsGeneratedValueTypeTemporary(next, loadInst.Parent, loadInst.ChildIndex, v, inlinedExpression))
                    {
                        return(false);
                    }
                }
                else
                {
                    Debug.Assert(loadInst.OpCode == OpCode.LdLoc);
                    if (!aggressive && v.Kind != VariableKind.StackSlot && !NonAggressiveInlineInto(next, loadInst, inlinedExpression, v))
                    {
                        return(false);
                    }
                }

                context.Step($"Inline variable '{v.Name}'", inlinedExpression);
                // Assign the ranges of the ldloc instruction:
                inlinedExpression.AddILRange(loadInst.ILRange);

                if (loadInst.OpCode == OpCode.LdLoca)
                {
                    // it was an ldloca instruction, so we need to use the pseudo-opcode 'addressof'
                    // to preserve the semantics of the compiler-generated temporary
                    loadInst.ReplaceWith(new AddressOf(inlinedExpression));
                }
                else
                {
                    loadInst.ReplaceWith(inlinedExpression);
                }
                return(true);
            }
            return(false);
        }
Esempio n. 27
0
        internal static void FixComparisonKindLdNull(Comp inst, ILTransformContext context)
        {
            if (inst.IsLifted)
            {
                return;
            }
            if (inst.Right.MatchLdNull())
            {
                if (inst.Kind == ComparisonKind.GreaterThan)
                {
                    context.Step("comp(left > ldnull)  => comp(left != ldnull)", inst);
                    inst.Kind = ComparisonKind.Inequality;
                }
                else if (inst.Kind == ComparisonKind.LessThanOrEqual)
                {
                    context.Step("comp(left <= ldnull) => comp(left == ldnull)", inst);
                    inst.Kind = ComparisonKind.Equality;
                }
            }
            else if (inst.Left.MatchLdNull())
            {
                if (inst.Kind == ComparisonKind.LessThan)
                {
                    context.Step("comp(ldnull < right)  => comp(ldnull != right)", inst);
                    inst.Kind = ComparisonKind.Inequality;
                }
                else if (inst.Kind == ComparisonKind.GreaterThanOrEqual)
                {
                    context.Step("comp(ldnull >= right) => comp(ldnull == right)", inst);
                    inst.Kind = ComparisonKind.Equality;
                }
            }

            if (inst.Right.MatchLdNull() && inst.Left.MatchBox(out var arg, out var type) && type.Kind == TypeKind.TypeParameter)
            {
                if (inst.Kind == ComparisonKind.Equality)
                {
                    context.Step("comp(box T(..) == ldnull) -> comp(.. == ldnull)", inst);
                    inst.Left = arg;
                }
                if (inst.Kind == ComparisonKind.Inequality)
                {
                    context.Step("comp(box T(..) != ldnull) -> comp(.. != ldnull)", inst);
                    inst.Left = arg;
                }
            }
        }
Esempio n. 28
0
        /// <summary>
        /// Called by expression transforms.
        /// Handles the `array[System.Index]` cases.
        /// </summary>
        public static bool HandleLdElema(LdElema ldelema, ILTransformContext context)
        {
            if (!context.Settings.Ranges)
            {
                return(false);
            }
            if (!ldelema.Array.MatchLdLoc(out ILVariable array))
            {
                return(false);
            }
            if (ldelema.Indices.Count != 1)
            {
                return(false);                // the index/range feature doesn't support multi-dimensional arrays
            }
            var index = ldelema.Indices[0];

            if (index is CallInstruction call && call.Method.Name == "GetOffset" && call.Method.DeclaringType.IsKnownType(KnownTypeCode.Index))
            {
                // ldelema T(ldloc array, call GetOffset(..., ldlen.i4(ldloc array)))
                // -> withsystemindex.ldelema T(ldloc array, ...)
                if (call.Arguments.Count != 2)
                {
                    return(false);
                }
                if (!(call.Arguments[1].MatchLdLen(StackType.I4, out var arrayLoad) && arrayLoad.MatchLdLoc(array)))
                {
                    return(false);
                }
                context.Step("ldelema with System.Index", ldelema);
                foreach (var node in call.Arguments[1].Descendants)
                {
                    ldelema.AddILRange(node);
                }
                ldelema.AddILRange(call);
                ldelema.WithSystemIndex = true;
                // The method call had a `ref System.Index` argument for the this pointer, but we want a `System.Index` by-value.
                ldelema.Indices[0] = new LdObj(call.Arguments[0], call.Method.DeclaringType);
                return(true);
            }
Esempio n. 29
0
        internal static void AddressOfLdLocToLdLoca(LdObj inst, ILTransformContext context)
        {
            // ldobj(...(addressof(ldloc V))) where ... can be zero or more ldflda instructions
            // =>
            // ldobj(...(ldloca V))
            var temp  = inst.Target;
            var range = temp.ILRanges;

            while (temp.MatchLdFlda(out var ldfldaTarget, out _))
            {
                temp  = ldfldaTarget;
                range = range.Concat(temp.ILRanges);
            }
            if (temp.MatchAddressOf(out var addressOfTarget, out _) && addressOfTarget.MatchLdLoc(out var v))
            {
                context.Step($"ldobj(...(addressof(ldloca {v.Name}))) => ldobj(...(ldloca {v.Name}))", inst);
                var replacement = new LdLoca(v).WithILRange(addressOfTarget);
                foreach (var r in range)
                {
                    replacement = replacement.WithILRange(r);
                }
                temp.ReplaceWith(replacement);
            }
        }
Esempio n. 30
0
        protected internal override void VisitStLoc(StLoc inst)
        {
            base.VisitStLoc(inst);

            if (inst.Parent is Block && inst.Variable.IsSingleDefinition)
            {
                if (inst.Variable.Kind == VariableKind.Local && inst.Variable.LoadCount == 0 && inst.Value is StLoc)
                {
                    context.Step($"Remove unused variable assignment {inst.Variable.Name}", inst);
                    inst.ReplaceWith(inst.Value);
                    return;
                }
                if (inst.Value.MatchLdLoc(out var displayClassVariable) && displayClasses.TryGetValue(displayClassVariable, out var displayClass))
                {
                    context.Step($"Found copy-assignment of display-class variable {displayClassVariable.Name}", inst);
                    displayClasses.Add(inst.Variable, displayClass);
                    instructionsToRemove.Add(inst);
                    return;
                }
            }
        }