bool DoTransformMultiDim(Block body, int pos) { if (pos >= body.Instructions.Count - 2) { return(false); } ILVariable v; ILInstruction newarrExpr; IType arrayType; int[] length; ILInstruction instr = body.Instructions[pos]; if (instr.MatchStLoc(out v, out newarrExpr) && MatchNewArr(newarrExpr, out arrayType, out length)) { ILInstruction[] values; int initArrayPos; if (ForwardScanInitializeArrayRuntimeHelper(body, pos + 1, v, arrayType, length, out values, out initArrayPos)) { var block = BlockFromInitializer(v, arrayType, length, values); body.Instructions[pos].ReplaceWith(new StLoc(v, block)); body.Instructions.RemoveAt(initArrayPos); ILInlining.InlineIfPossible(body, pos, context); return(true); } } return(false); }
public void Run(Block block, BlockTransformContext context) { this.context = context; if (!context.Settings.AnonymousMethods) { return; } for (int i = block.Instructions.Count - 1; i >= 0; i--) { if (block.Instructions[i] is IfInstruction inst) { if (CachedDelegateInitializationWithField(inst)) { block.Instructions.RemoveAt(i); continue; } if (CachedDelegateInitializationWithLocal(inst)) { ILInlining.InlineIfPossible(block, ref i, context); continue; } if (CachedDelegateInitializationRoslynInStaticWithLocal(inst) || CachedDelegateInitializationRoslynWithLocal(inst)) { block.Instructions.RemoveAt(i); continue; } } } }
bool DoTransform(Block body, int pos) { if (pos >= body.Instructions.Count - 2) { return(false); } ILInstruction inst = body.Instructions[pos]; if (inst.MatchStLoc(out var v, out var newarrExpr) && MatchNewArr(newarrExpr, out var elementType, out var arrayLength)) { if (ForwardScanInitializeArrayRuntimeHelper(body, pos + 1, v, elementType, arrayLength, out var values, out var initArrayPos)) { context.Step("ForwardScanInitializeArrayRuntimeHelper", inst); var tempStore = context.Function.RegisterVariable(VariableKind.InitializerTarget, v.Type); var block = BlockFromInitializer(tempStore, elementType, arrayLength, values); body.Instructions[pos] = new StLoc(v, block); body.Instructions.RemoveAt(initArrayPos); ILInlining.InlineIfPossible(body, pos, context); return(true); } if (arrayLength.Length == 1) { if (HandleSimpleArrayInitializer(body, pos + 1, v, elementType, arrayLength[0], out values, out var instructionsToRemove)) { context.Step("HandleSimpleArrayInitializer", inst); var block = new Block(BlockKind.ArrayInitializer); var tempStore = context.Function.RegisterVariable(VariableKind.InitializerTarget, v.Type); block.Instructions.Add(new StLoc(tempStore, new NewArr(elementType, arrayLength.Select(l => new LdcI4(l)).ToArray()))); block.Instructions.AddRange(values.SelectWithIndex( (i, value) => { if (value == null) { value = GetNullExpression(elementType); } return(StElem(new LdLoc(tempStore), new[] { new LdcI4(i) }, value, elementType)); } )); block.FinalInstruction = new LdLoc(tempStore); body.Instructions[pos] = new StLoc(v, block); body.Instructions.RemoveRange(pos + 1, instructionsToRemove); ILInlining.InlineIfPossible(body, pos, context); return(true); } if (HandleJaggedArrayInitializer(body, pos + 1, v, elementType, arrayLength[0], out ILVariable finalStore, out values, out instructionsToRemove)) { context.Step("HandleJaggedArrayInitializer", inst); var block = new Block(BlockKind.ArrayInitializer); var tempStore = context.Function.RegisterVariable(VariableKind.InitializerTarget, v.Type); block.Instructions.Add(new StLoc(tempStore, new NewArr(elementType, arrayLength.Select(l => new LdcI4(l)).ToArray()))); block.Instructions.AddRange(values.SelectWithIndex((i, value) => StElem(new LdLoc(tempStore), new[] { new LdcI4(i) }, value, elementType))); block.FinalInstruction = new LdLoc(tempStore); body.Instructions[pos] = new StLoc(finalStore, block); body.Instructions.RemoveRange(pos + 1, instructionsToRemove); ILInlining.InlineIfPossible(body, pos, context); return(true); } } } return(false); }
protected internal override void VisitNewObj(NewObj inst) { if (TransformDecimalCtorToConstant(inst, out LdcDecimal decimalConstant)) { context.Step("TransformDecimalCtorToConstant", inst); inst.ReplaceWith(decimalConstant); return; } Block block; if (TransformSpanTCtorContainingStackAlloc(inst, out ILInstruction locallocSpan)) { context.Step("new Span<T>(stackalloc) -> stackalloc Span<T>", inst); inst.ReplaceWith(locallocSpan); block = null; ILInstruction stmt = locallocSpan; while (stmt.Parent != null) { if (stmt.Parent is Block b) { block = b; break; } stmt = stmt.Parent; } // Special case to eliminate extra store if (stmt.GetNextSibling() is StLoc storeStmt && storeStmt.Value is LdLoc) { ILInlining.InlineIfPossible(block, stmt.ChildIndex, context); } return; } if (TransformArrayInitializers.TransformSpanTArrayInitialization(inst, context, out block)) { context.Step("TransformSpanTArrayInitialization: single-dim", inst); inst.ReplaceWith(block); return; } if (TransformDelegateCtorLdVirtFtnToLdVirtDelegate(inst, out LdVirtDelegate ldVirtDelegate)) { context.Step("new Delegate(target, ldvirtftn Method) -> ldvirtdelegate Delegate Method(target)", inst); inst.ReplaceWith(ldVirtDelegate); return; } base.VisitNewObj(inst); }
bool DoTransformStackAllocInitializer(Block body, int pos) { if (pos >= body.Instructions.Count - 2) { return(false); } ILInstruction inst = body.Instructions[pos]; if (inst.MatchStLoc(out var v, out var locallocExpr) && locallocExpr.MatchLocAlloc(out var lengthInst)) { if (lengthInst.MatchLdcI(out var lengthInBytes) && HandleCpblkInitializer(body, pos + 1, v, lengthInBytes, out var blob, out var elementType)) { context.Step("HandleCpblkInitializer", inst); var block = new Block(BlockKind.StackAllocInitializer); var tempStore = context.Function.RegisterVariable(VariableKind.InitializerTarget, new PointerType(elementType)); block.Instructions.Add(new StLoc(tempStore, locallocExpr)); while (blob.RemainingBytes > 0) { block.Instructions.Add(StElemPtr(tempStore, blob.Offset, new LdcI4(blob.ReadByte()), elementType)); } block.FinalInstruction = new LdLoc(tempStore); body.Instructions[pos] = new StLoc(v, block); body.Instructions.RemoveAt(pos + 1); ILInlining.InlineIfPossible(body, pos, context); ExpressionTransforms.RunOnSingleStatement(body.Instructions[pos], context); return(true); } if (HandleSequentialLocAllocInitializer(body, pos + 1, v, locallocExpr, out elementType, out StObj[] values, out int instructionsToRemove)) { context.Step("HandleSequentialLocAllocInitializer", inst); var block = new Block(BlockKind.StackAllocInitializer); var tempStore = context.Function.RegisterVariable(VariableKind.InitializerTarget, new PointerType(elementType)); block.Instructions.Add(new StLoc(tempStore, locallocExpr)); block.Instructions.AddRange(values.Where(value => value != null).Select(value => RewrapStore(tempStore, value, elementType))); block.FinalInstruction = new LdLoc(tempStore); body.Instructions[pos] = new StLoc(v, block); body.Instructions.RemoveRange(pos + 1, instructionsToRemove); ILInlining.InlineIfPossible(body, pos, context); ExpressionTransforms.RunOnSingleStatement(body.Instructions[pos], context); return(true); } }
bool DoTransformMultiDim(ILFunction function, Block body, int pos) { if (pos >= body.Instructions.Count - 2) { return(false); } ILInstruction inst = body.Instructions[pos]; if (inst.MatchStLoc(out var v, out var newarrExpr) && MatchNewArr(newarrExpr, out var elementType, out var length)) { if (HandleRuntimeHelpersInitializeArray(body, pos + 1, v, elementType, length, out var values, out var initArrayPos)) { context.Step("HandleRuntimeHelpersInitializeArray: multi-dim", inst); var block = BlockFromInitializer(v, elementType, length, values); body.Instructions[pos].ReplaceWith(new StLoc(v, block)); body.Instructions.RemoveAt(initArrayPos); ILInlining.InlineIfPossible(body, pos, context); return(true); } if (HandleSimpleArrayInitializer(function, body, pos + 1, v, elementType, length, out var arrayValues, out var instructionsToRemove)) { context.Step("HandleSimpleArrayInitializer: multi-dim", inst); var block = new Block(BlockKind.ArrayInitializer); var tempStore = context.Function.RegisterVariable(VariableKind.InitializerTarget, v.Type); block.Instructions.Add(new StLoc(tempStore, new NewArr(elementType, length.Select(l => new LdcI4(l)).ToArray()))); block.Instructions.AddRange(arrayValues.Select( t => { var(indices, value) = t; if (value == null) { value = GetNullExpression(elementType); } return(StElem(new LdLoc(tempStore), indices, value, elementType)); } )); block.FinalInstruction = new LdLoc(tempStore); body.Instructions[pos] = new StLoc(v, block); body.Instructions.RemoveRange(pos + 1, instructionsToRemove); ILInlining.InlineIfPossible(body, pos, context); return(true); } } return(false); }
bool DoTransform(Block body, int pos) { ILInstruction inst = body.Instructions[pos]; // Match stloc(v, newobj) if (inst.MatchStLoc(out var v, out var initInst) && (v.Kind == VariableKind.Local || v.Kind == VariableKind.StackSlot)) { IType instType; switch (initInst) { case NewObj newObjInst: if (newObjInst.ILStackWasEmpty && v.Kind == VariableKind.Local && !context.Function.Method.IsConstructor && !context.Function.Method.IsCompilerGeneratedOrIsInCompilerGeneratedClass()) { // on statement level (no other expressions on IL stack), // prefer to keep local variables (but not stack slots), // unless we are in a constructor (where inlining object initializers might be critical // for the base ctor call) or a compiler-generated delegate method, which might be used in a query expression. return(false); } // Do not try to transform display class usages or delegate construction. // DelegateConstruction transform cannot deal with this. if (DelegateConstruction.IsSimpleDisplayClass(newObjInst.Method.DeclaringType)) { return(false); } if (DelegateConstruction.IsDelegateConstruction(newObjInst) || DelegateConstruction.IsPotentialClosure(context, newObjInst)) { return(false); } instType = newObjInst.Method.DeclaringType; break; case DefaultValue defaultVal: if (defaultVal.ILStackWasEmpty && v.Kind == VariableKind.Local && !context.Function.Method.IsConstructor) { // on statement level (no other expressions on IL stack), // prefer to keep local variables (but not stack slots), // unless we are in a constructor (where inlining object initializers might be critical // for the base ctor call) return(false); } instType = defaultVal.Type; break; default: return(false); } int initializerItemsCount = 0; var blockKind = BlockKind.CollectionInitializer; possibleIndexVariables = new Dictionary <ILVariable, (int Index, ILInstruction Value)>(); currentPath = new List <AccessPathElement>(); isCollection = false; pathStack = new Stack <HashSet <AccessPathElement> >(); pathStack.Push(new HashSet <AccessPathElement>()); // Detect initializer type by scanning the following statements // each must be a callvirt with ldloc v as first argument // if the method is a setter we're dealing with an object initializer // if the method is named Add and has at least 2 arguments we're dealing with a collection/dictionary initializer while (pos + initializerItemsCount + 1 < body.Instructions.Count && IsPartOfInitializer(body.Instructions, pos + initializerItemsCount + 1, v, instType, ref blockKind)) { initializerItemsCount++; } // Do not convert the statements into an initializer if there's an incompatible usage of the initializer variable // directly after the possible initializer. if (IsMethodCallOnVariable(body.Instructions[pos + initializerItemsCount + 1], v)) { return(false); } // Calculate the correct number of statements inside the initializer: // All index variables that were used in the initializer have Index set to -1. // We fetch the first unused variable from the list and remove all instructions after its // first usage (i.e. the init store) from the initializer. var index = possibleIndexVariables.Where(info => info.Value.Index > -1).Min(info => (int?)info.Value.Index); if (index != null) { initializerItemsCount = index.Value - pos - 1; } // The initializer would be empty, there's nothing to do here. if (initializerItemsCount <= 0) { return(false); } context.Step("CollectionOrObjectInitializer", inst); // Create a new block and final slot (initializer target variable) var initializerBlock = new Block(blockKind); ILVariable finalSlot = context.Function.RegisterVariable(VariableKind.InitializerTarget, v.Type); initializerBlock.FinalInstruction = new LdLoc(finalSlot); initializerBlock.Instructions.Add(new StLoc(finalSlot, initInst.Clone())); // Move all instructions to the initializer block. for (int i = 1; i <= initializerItemsCount; i++) { switch (body.Instructions[i + pos]) { case CallInstruction call: if (!(call is CallVirt || call is Call)) { continue; } var newCall = call; var newTarget = newCall.Arguments[0]; foreach (var load in newTarget.Descendants.OfType <IInstructionWithVariableOperand>()) { if ((load is LdLoc || load is LdLoca) && load.Variable == v) { load.Variable = finalSlot; } } initializerBlock.Instructions.Add(newCall); break; case StObj stObj: var newStObj = stObj; foreach (var load in newStObj.Target.Descendants.OfType <IInstructionWithVariableOperand>()) { if ((load is LdLoc || load is LdLoca) && load.Variable == v) { load.Variable = finalSlot; } } initializerBlock.Instructions.Add(newStObj); break; case StLoc stLoc: var newStLoc = stLoc; initializerBlock.Instructions.Add(newStLoc); break; } } initInst.ReplaceWith(initializerBlock); body.Instructions.RemoveRange(pos + 1, initializerItemsCount); ILInlining.InlineIfPossible(body, pos, context); } return(true); }
bool DoTransform(Block body, int pos) { if (pos >= body.Instructions.Count - 2) { return(false); } ILInstruction inst = body.Instructions[pos]; ILVariable v; ILInstruction newarrExpr; IType elementType; int[] arrayLength; if (inst.MatchStLoc(out v, out newarrExpr) && MatchNewArr(newarrExpr, out elementType, out arrayLength)) { ILInstruction[] values; int initArrayPos; if (ForwardScanInitializeArrayRuntimeHelper(body, pos + 1, v, elementType, arrayLength, out values, out initArrayPos)) { context.Step("ForwardScanInitializeArrayRuntimeHelper", inst); var tempStore = context.Function.RegisterVariable(VariableKind.InitializerTarget, v.Type); var block = BlockFromInitializer(tempStore, elementType, arrayLength, values); body.Instructions[pos] = new StLoc(v, block); body.Instructions.RemoveAt(initArrayPos); ILInlining.InlineIfPossible(body, pos, context); return(true); } if (arrayLength.Length == 1) { int instructionsToRemove; if (HandleSimpleArrayInitializer(body, pos + 1, v, elementType, arrayLength[0], out values, out instructionsToRemove)) { context.Step("HandleSimpleArrayInitializer", inst); var block = new Block(BlockKind.ArrayInitializer); var tempStore = context.Function.RegisterVariable(VariableKind.InitializerTarget, v.Type); block.Instructions.Add(new StLoc(tempStore, new NewArr(elementType, arrayLength.Select(l => new LdcI4(l)).ToArray()))); block.Instructions.AddRange(values.SelectWithIndex( (i, value) => { if (value == null) { value = GetNullExpression(elementType); } return(StElem(new LdLoc(tempStore), new[] { new LdcI4(i) }, value, elementType)); } )); block.FinalInstruction = new LdLoc(tempStore); body.Instructions[pos] = new StLoc(v, block); body.Instructions.RemoveRange(pos + 1, instructionsToRemove); ILInlining.InlineIfPossible(body, pos, context); return(true); } if (HandleJaggedArrayInitializer(body, pos + 1, v, elementType, arrayLength[0], out ILVariable finalStore, out values, out instructionsToRemove)) { context.Step("HandleJaggedArrayInitializer", inst); var block = new Block(BlockKind.ArrayInitializer); var tempStore = context.Function.RegisterVariable(VariableKind.InitializerTarget, v.Type); block.Instructions.Add(new StLoc(tempStore, new NewArr(elementType, arrayLength.Select(l => new LdcI4(l)).ToArray()))); block.Instructions.AddRange(values.SelectWithIndex((i, value) => StElem(new LdLoc(tempStore), new[] { new LdcI4(i) }, value, elementType))); block.FinalInstruction = new LdLoc(tempStore); body.Instructions[pos] = new StLoc(finalStore, block); body.Instructions.RemoveRange(pos + 1, instructionsToRemove); ILInlining.InlineIfPossible(body, pos, context); return(true); } } // Put in a limit so that we don't consume too much memory if the code allocates a huge array // and populates it extremely sparsly. However, 255 "null" elements in a row actually occur in the Mono C# compiler! // const int maxConsecutiveDefaultValueExpressions = 300; // var operands = new List<ILInstruction>(); // int numberOfInstructionsToRemove = 0; // for (int j = pos + 1; j < body.Instructions.Count; j++) { // var nextExpr = body.Instructions[j] as Void; // int arrayPos; // if (nextExpr != null && nextExpr is a.IsStoreToArray() && // nextExpr.Arguments[0].Match(ILCode.Ldloc, out v3) && // v == v3 && // nextExpr.Arguments[1].Match(ILCode.Ldc_I4, out arrayPos) && // arrayPos >= operands.Count && // arrayPos <= operands.Count + maxConsecutiveDefaultValueExpressions && // !nextExpr.Arguments[2].ContainsReferenceTo(v3)) // { // while (operands.Count < arrayPos) // operands.Add(new ILExpression(ILCode.DefaultValue, elementType)); // operands.Add(nextExpr.Arguments[2]); // numberOfInstructionsToRemove++; // } else { // break; // } // } } return(false); }
bool DoTransform(Block body, int pos) { ILInstruction inst = body.Instructions[pos]; // Match stloc(v, newobj) if (inst.MatchStLoc(out var v, out var initInst) && (v.Kind == VariableKind.Local || v.Kind == VariableKind.StackSlot)) { Block initializerBlock = null; IType instType; switch (initInst) { case NewObj newObjInst: if (newObjInst.ILStackWasEmpty && v.Kind == VariableKind.Local && !context.Function.Method.IsConstructor) { // on statement level (no other expressions on IL stack), // prefer to keep local variables (but not stack slots), // unless we are in a constructor (where inlining object initializers might be critical // for the base ctor call) return(false); } // Do not try to transform display class usages or delegate construction. // DelegateConstruction transform cannot deal with this. if (DelegateConstruction.IsSimpleDisplayClass(newObjInst.Method.DeclaringType)) { return(false); } if (DelegateConstruction.IsDelegateConstruction(newObjInst) || DelegateConstruction.IsPotentialClosure(context, newObjInst)) { return(false); } instType = newObjInst.Method.DeclaringType; break; case DefaultValue defaultVal: instType = defaultVal.Type; break; case Block existingInitializer: if (existingInitializer.Type == BlockType.CollectionInitializer || existingInitializer.Type == BlockType.ObjectInitializer) { initializerBlock = existingInitializer; var value = ((StLoc)initializerBlock.Instructions[0]).Value; if (value is NewObj no) { instType = no.Method.DeclaringType; } else { instType = ((DefaultValue)value).Type; } break; } return(false); default: return(false); } context.Step("CollectionOrObjectInitializer", inst); int initializerItemsCount = 0; var blockType = initializerBlock?.Type ?? BlockType.CollectionInitializer; var possibleIndexVariables = new Dictionary <ILVariable, (int Index, ILInstruction Value)>(); // Detect initializer type by scanning the following statements // each must be a callvirt with ldloc v as first argument // if the method is a setter we're dealing with an object initializer // if the method is named Add and has at least 2 arguments we're dealing with a collection/dictionary initializer while (pos + initializerItemsCount + 1 < body.Instructions.Count && IsPartOfInitializer(body.Instructions, pos + initializerItemsCount + 1, v, instType, ref blockType, possibleIndexVariables)) { initializerItemsCount++; } var index = possibleIndexVariables.Where(info => info.Value.Index > -1).Min(info => (int?)info.Value.Index); if (index != null) { initializerItemsCount = index.Value - pos - 1; } if (initializerItemsCount <= 0) { return(false); } ILVariable finalSlot; if (initializerBlock == null) { initializerBlock = new Block(blockType); finalSlot = context.Function.RegisterVariable(VariableKind.InitializerTarget, v.Type); initializerBlock.FinalInstruction = new LdLoc(finalSlot); initializerBlock.Instructions.Add(new StLoc(finalSlot, initInst.Clone())); } else { finalSlot = ((LdLoc)initializerBlock.FinalInstruction).Variable; } for (int i = 1; i <= initializerItemsCount; i++) { switch (body.Instructions[i + pos]) { case CallInstruction call: if (!(call is CallVirt || call is Call)) { continue; } var newCall = (CallInstruction)call.Clone(); var newTarget = newCall.Arguments[0]; foreach (var load in newTarget.Descendants.OfType <IInstructionWithVariableOperand>()) { if ((load is LdLoc || load is LdLoca) && load.Variable == v) { load.Variable = finalSlot; } } initializerBlock.Instructions.Add(newCall); break; case StObj stObj: var newStObj = (StObj)stObj.Clone(); foreach (var load in newStObj.Target.Descendants.OfType <IInstructionWithVariableOperand>()) { if ((load is LdLoc || load is LdLoca) && load.Variable == v) { load.Variable = finalSlot; } } initializerBlock.Instructions.Add(newStObj); break; case StLoc stLoc: var newStLoc = (StLoc)stLoc.Clone(); initializerBlock.Instructions.Add(newStLoc); break; } } initInst.ReplaceWith(initializerBlock); for (int i = 0; i < initializerItemsCount; i++) { body.Instructions.RemoveAt(pos + 1); } ILInlining.InlineIfPossible(body, ref pos, context); } return(true); }