From c0ba1b6360117c52f45b1c7e8dd8d7e06782cb7e Mon Sep 17 00:00:00 2001 From: Patrick Nguyen Date: Fri, 27 May 2022 15:33:18 -0700 Subject: [PATCH 1/5] Fix ILGenerator maxstack computation When generating a dynamic method both with many unconditional jumps, the ILGenerator's computation of maxstack is way to large - it adds the max stack of each basic block ending with an unconditional transfer (br, ret, throw, etc). If the generated code has many such bbs (eg, from the "then" clauses of if-then-else constructs) this sum can overflow 2^16. When it does, the maxstack recorded/used is the computed value mod 2^16. When that is less than the correct value, the JIT throws an InvalidProgramException. Keep track of the stack depth at various positions and use it to calculate an adjustment to the depth. Fix #63805 --- .../src/System/Reflection/Emit/ILGenerator.cs | 154 +++++++-- .../System.Reflection.Emit.ILGeneration.sln | 169 +++++----- .../tests/ILGenerator/Emit5Tests.cs | 300 ++++++++++++++++++ ....Reflection.Emit.ILGeneration.Tests.csproj | 1 + 4 files changed, 508 insertions(+), 116 deletions(-) create mode 100644 src/libraries/System.Reflection.Emit.ILGeneration/tests/ILGenerator/Emit5Tests.cs diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/ILGenerator.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/ILGenerator.cs index 26c1ae27f88ad..00ee3ceff9c10 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/ILGenerator.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/ILGenerator.cs @@ -40,7 +40,7 @@ internal static T[] EnlargeArray(T[] incoming, int requiredSize) private int m_length; private byte[] m_ILStream; - private int[]? m_labelList; + private __LabelInfo[]? m_labelList; private int m_labelCount; private __FixupData[]? m_fixupData; @@ -61,10 +61,13 @@ internal static T[] EnlargeArray(T[] incoming, int requiredSize) internal int m_localCount; internal SignatureHelper m_localSignature; - private int m_maxStackSize; // Maximum stack size not counting the exceptions. + private int m_curDepth; // Current stack depth, with -1 meaning unknown. + private int m_targetDepth; // Stack depth at a target of the previous instruction (when it is branching). + private int m_maxDepth; // Running max of the stack depth. - private int m_maxMidStack; // Maximum stack size for a given basic block. - private int m_maxMidStackCur; // Running count of the maximum stack size for the current basic block. + // Adjustment to add to m_maxDepth for incorrect/invalid IL. For example, when branch instructions + // with different stack depths target the same label. + private long m_depthAdjustment; internal int CurrExcStackCount => m_currExcStackCount; @@ -131,28 +134,32 @@ internal void UpdateStackSize(OpCode opcode, int stackchange) // requirements for the function. stackchange specifies the amount // by which the stacksize needs to be updated. - // Special case for the Return. Returns pops 1 if there is a - // non-void return value. - - // Update the running stacksize. m_maxMidStack specifies the maximum - // amount of stack required for the current basic block irrespective of - // where you enter the block. - m_maxMidStackCur += stackchange; - if (m_maxMidStackCur > m_maxMidStack) - m_maxMidStack = m_maxMidStackCur; - else if (m_maxMidStackCur < 0) - m_maxMidStackCur = 0; - - // If the current instruction signifies end of a basic, which basically - // means an unconditional branch, add m_maxMidStack to m_maxStackSize. - // m_maxStackSize will eventually be the sum of the stack requirements for - // each basic block. - if (opcode.EndsUncondJmpBlk()) + if (m_curDepth < 0) + { + // Current depth is "unknown". We get here when: + // * this is unreachable code. + // * the client uses explicit numeric offsets rather than Labels. + m_curDepth = 0; + } + + m_curDepth += stackchange; + if (m_curDepth < 0) { - m_maxStackSize += m_maxMidStack; - m_maxMidStack = 0; - m_maxMidStackCur = 0; + // Stack underflow. Assume our previous depth computation was flawed. + m_depthAdjustment -= m_curDepth; + m_curDepth = 0; } + else if (m_maxDepth < m_curDepth) + m_maxDepth = m_curDepth; + Debug.Assert(m_depthAdjustment >= 0); + Debug.Assert(m_curDepth >= 0); + + // Record the stack depth at a "target" of this instruction. + m_targetDepth = m_curDepth; + + // If the current instruction can't fall through, set the depth to unknown. + if (opcode.EndsUncondJmpBlk()) + m_curDepth = -1; } private int GetMethodToken(MethodBase method, Type[]? optionalParameterTypes, bool useMethodDef) @@ -280,10 +287,11 @@ private int GetLabelPos(Label lbl) if (index < 0 || index >= m_labelCount || m_labelList is null) throw new ArgumentException(SR.Argument_BadLabel); - if (m_labelList[index] < 0) + int pos = m_labelList[index].m_pos; + if (pos < 0) throw new ArgumentException(SR.Argument_BadLabelContent); - return m_labelList[index]; + return pos; } private void AddFixup(Label lbl, int pos, int instSize) @@ -306,11 +314,30 @@ private void AddFixup(Label lbl, int pos, int instSize) m_fixupLabel = lbl, m_fixupInstSize = instSize }; + + int labelIndex = lbl.GetLabelValue(); + if (labelIndex < 0 || labelIndex >= m_labelCount || m_labelList is null) + throw new ArgumentException(SR.Argument_BadLabel); + + int depth = m_labelList[labelIndex].m_depth; + int targetDepth = m_targetDepth; + Debug.Assert(depth >= -1); + Debug.Assert(targetDepth >= -1); + if (depth < targetDepth) + { + // Either unknown depth for this label or this branch location has a larger depth than previously recorded. + // In the latter case, the IL is (likely) invalid, but we just compensate for it. + if (depth >= 0) + m_depthAdjustment += targetDepth - depth; + m_labelList[labelIndex].m_depth = targetDepth; + } } internal int GetMaxStackSize() { - return m_maxStackSize; + // Limit the computed max stack to 2^16 - 1, since the value is mod`ed by 2^16 by other code. + Debug.Assert(m_depthAdjustment >= 0); + return (int)Math.Min(ushort.MaxValue, m_maxDepth + m_depthAdjustment); } private static void SortExceptions(__ExceptionInfo[] exceptions) @@ -926,7 +953,7 @@ public virtual Label BeginExceptionBlock() m_currExcStack = EnlargeArray(m_currExcStack); } - Label endLabel = DefineLabel(); + Label endLabel = DefineLabel(0); __ExceptionInfo exceptionInfo = new __ExceptionInfo(m_length, endLabel); // add the exception to the tracking list @@ -934,6 +961,10 @@ public virtual Label BeginExceptionBlock() // Make this exception the current active exception m_currExcStack[m_currExcStackCount++] = exceptionInfo; + + // Stack depth for "try" starts at zero. + m_curDepth = 0; + return endLabel; } @@ -969,7 +1000,7 @@ public virtual void EndExceptionBlock() // Check if we've already set this label. // The only reason why we might have set this is if we have a finally block. - Label label = m_labelList![endLabel.GetLabelValue()] != -1 + Label label = m_labelList![endLabel.GetLabelValue()].m_pos != -1 ? current.m_finallyEndLabel : endLabel; @@ -990,6 +1021,9 @@ public virtual void BeginExceptFilterBlock() Emit(OpCodes.Leave, current.GetEndLabel()); current.MarkFilterAddr(m_length); + + // Stack depth for "filter" starts at one. + m_curDepth = 1; } public virtual void BeginCatchBlock(Type exceptionType) @@ -1020,6 +1054,9 @@ public virtual void BeginCatchBlock(Type exceptionType) } current.MarkCatchAddr(m_length, exceptionType); + + // Stack depth for "catch" starts at one. + m_curDepth = 1; } public virtual void BeginFaultBlock() @@ -1034,6 +1071,9 @@ public virtual void BeginFaultBlock() Emit(OpCodes.Leave, current.GetEndLabel()); current.MarkFaultAddr(m_length); + + // Stack depth for "fault" starts at zero. + m_curDepth = 0; } public virtual void BeginFinallyBlock() @@ -1055,7 +1095,7 @@ public virtual void BeginFinallyBlock() MarkLabel(endLabel); - Label finallyEndLabel = DefineLabel(); + Label finallyEndLabel = DefineLabel(0); current.SetFinallyEndLabel(finallyEndLabel); // generate leave for try clause @@ -1063,25 +1103,36 @@ public virtual void BeginFinallyBlock() if (catchEndAddr == 0) catchEndAddr = m_length; current.MarkFinallyAddr(m_length, catchEndAddr); + + // Stack depth for "finally" starts at zero. + m_curDepth = 0; } #endregion #region Labels public virtual Label DefineLabel() + { + // We don't know the stack depth at the label yet, so set it to -1. + return DefineLabel(-1); + } + + private Label DefineLabel(int depth) { // Declares a new Label. This is just a token and does not yet represent any particular location // within the stream. In order to set the position of the label within the stream, you must call // Mark Label. + Debug.Assert(depth >= -1); - // Delay init the lable array in case we dont use it - m_labelList ??= new int[DefaultLabelArraySize]; + // Delay init the label array in case we dont use it + m_labelList ??= new __LabelInfo[DefaultLabelArraySize]; if (m_labelCount >= m_labelList.Length) { m_labelList = EnlargeArray(m_labelList); } - m_labelList[m_labelCount] = -1; + m_labelList[m_labelCount].m_pos = -1; + m_labelList[m_labelCount].m_depth = depth; return new Label(m_labelCount++); } @@ -1098,12 +1149,39 @@ public virtual void MarkLabel(Label loc) throw new ArgumentException(SR.Argument_InvalidLabel); } - if (m_labelList[labelIndex] != -1) + if (m_labelList[labelIndex].m_pos != -1) { throw new ArgumentException(SR.Argument_RedefinedLabel); } - m_labelList[labelIndex] = m_length; + m_labelList[labelIndex].m_pos = m_length; + + int depth = m_labelList[labelIndex].m_depth; + if (depth < 0) + { + // Unknown depth for this label, indicating that it hasn't been used yet. + // If m_curDepth is unknown, we're in the Backward branch constraint case. See ECMA-335 III.1.7.5. + // The m_depthAdjustment field will compensate for violations of this constraint, as we + // discover them. That is, here we assume a depth of zero. If a (later) branch to this label + // has a positive stack depth, we'll record that as the new depth and add the delta into + // m_depthAdjustment. + if (m_curDepth < 0) + m_curDepth = 0; + m_labelList[labelIndex].m_depth = m_curDepth; + } + else if (depth < m_curDepth) + { + // A branch location with smaller stack targets this label. In this case, the IL is + // invalid, but we just compensate for it. + m_depthAdjustment += m_curDepth - depth; + m_labelList[labelIndex].m_depth = m_curDepth; + } + else if (depth > m_curDepth) + { + // Either the current depth is unknown, or a branch location with larger stack targets + // this label, so the IL is invalid. In either case, just adjust the current depth. + m_curDepth = depth; + } } #endregion @@ -1287,6 +1365,12 @@ public virtual void EndScope() #endregion } + internal struct __LabelInfo + { + internal int m_pos; // Position in the il stream, with -1 meaning unknown. + internal int m_depth; // Stack depth, with -1 meaning unknown. + } + internal struct __FixupData { internal Label m_fixupLabel; diff --git a/src/libraries/System.Reflection.Emit.ILGeneration/System.Reflection.Emit.ILGeneration.sln b/src/libraries/System.Reflection.Emit.ILGeneration/System.Reflection.Emit.ILGeneration.sln index 225e615fd3208..43dc12f65a668 100644 --- a/src/libraries/System.Reflection.Emit.ILGeneration/System.Reflection.Emit.ILGeneration.sln +++ b/src/libraries/System.Reflection.Emit.ILGeneration/System.Reflection.Emit.ILGeneration.sln @@ -1,4 +1,8 @@ -Microsoft Visual Studio Solution File, Format Version 12.00 + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.2.32505.173 +MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Private.CoreLib", "..\..\coreclr\System.Private.CoreLib\System.Private.CoreLib.csproj", "{B1053D24-237E-4E55-9413-20B34ED79F23}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestUtilities", "..\Common\tests\TestUtilities\TestUtilities.csproj", "{05696F45-ACF1-4C02-B8D9-E8C1F5E28717}" @@ -33,17 +37,23 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gen", "gen", "{155A2103-05C EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution + Checked|Any CPU = Checked|Any CPU + Checked|x64 = Checked|x64 + Checked|x86 = Checked|x86 Debug|Any CPU = Debug|Any CPU Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU Release|x64 = Release|x64 Release|x86 = Release|x86 - Checked|Any CPU = Checked|Any CPU - Checked|x64 = Checked|x64 - Checked|x86 = Checked|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution + {B1053D24-237E-4E55-9413-20B34ED79F23}.Checked|Any CPU.ActiveCfg = Checked|x64 + {B1053D24-237E-4E55-9413-20B34ED79F23}.Checked|Any CPU.Build.0 = Checked|x64 + {B1053D24-237E-4E55-9413-20B34ED79F23}.Checked|x64.ActiveCfg = Checked|x64 + {B1053D24-237E-4E55-9413-20B34ED79F23}.Checked|x64.Build.0 = Checked|x64 + {B1053D24-237E-4E55-9413-20B34ED79F23}.Checked|x86.ActiveCfg = Checked|x86 + {B1053D24-237E-4E55-9413-20B34ED79F23}.Checked|x86.Build.0 = Checked|x86 {B1053D24-237E-4E55-9413-20B34ED79F23}.Debug|Any CPU.ActiveCfg = Debug|x64 {B1053D24-237E-4E55-9413-20B34ED79F23}.Debug|Any CPU.Build.0 = Debug|x64 {B1053D24-237E-4E55-9413-20B34ED79F23}.Debug|x64.ActiveCfg = Debug|x64 @@ -56,12 +66,12 @@ Global {B1053D24-237E-4E55-9413-20B34ED79F23}.Release|x64.Build.0 = Release|x64 {B1053D24-237E-4E55-9413-20B34ED79F23}.Release|x86.ActiveCfg = Release|x86 {B1053D24-237E-4E55-9413-20B34ED79F23}.Release|x86.Build.0 = Release|x86 - {B1053D24-237E-4E55-9413-20B34ED79F23}.Checked|Any CPU.ActiveCfg = Checked|x64 - {B1053D24-237E-4E55-9413-20B34ED79F23}.Checked|Any CPU.Build.0 = Checked|x64 - {B1053D24-237E-4E55-9413-20B34ED79F23}.Checked|x64.ActiveCfg = Checked|x64 - {B1053D24-237E-4E55-9413-20B34ED79F23}.Checked|x64.Build.0 = Checked|x64 - {B1053D24-237E-4E55-9413-20B34ED79F23}.Checked|x86.ActiveCfg = Checked|x86 - {B1053D24-237E-4E55-9413-20B34ED79F23}.Checked|x86.Build.0 = Checked|x86 + {05696F45-ACF1-4C02-B8D9-E8C1F5E28717}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {05696F45-ACF1-4C02-B8D9-E8C1F5E28717}.Checked|Any CPU.Build.0 = Debug|Any CPU + {05696F45-ACF1-4C02-B8D9-E8C1F5E28717}.Checked|x64.ActiveCfg = Debug|Any CPU + {05696F45-ACF1-4C02-B8D9-E8C1F5E28717}.Checked|x64.Build.0 = Debug|Any CPU + {05696F45-ACF1-4C02-B8D9-E8C1F5E28717}.Checked|x86.ActiveCfg = Debug|Any CPU + {05696F45-ACF1-4C02-B8D9-E8C1F5E28717}.Checked|x86.Build.0 = Debug|Any CPU {05696F45-ACF1-4C02-B8D9-E8C1F5E28717}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {05696F45-ACF1-4C02-B8D9-E8C1F5E28717}.Debug|Any CPU.Build.0 = Debug|Any CPU {05696F45-ACF1-4C02-B8D9-E8C1F5E28717}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -74,12 +84,12 @@ Global {05696F45-ACF1-4C02-B8D9-E8C1F5E28717}.Release|x64.Build.0 = Release|Any CPU {05696F45-ACF1-4C02-B8D9-E8C1F5E28717}.Release|x86.ActiveCfg = Release|Any CPU {05696F45-ACF1-4C02-B8D9-E8C1F5E28717}.Release|x86.Build.0 = Release|Any CPU - {05696F45-ACF1-4C02-B8D9-E8C1F5E28717}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {05696F45-ACF1-4C02-B8D9-E8C1F5E28717}.Checked|Any CPU.Build.0 = Debug|Any CPU - {05696F45-ACF1-4C02-B8D9-E8C1F5E28717}.Checked|x64.ActiveCfg = Debug|Any CPU - {05696F45-ACF1-4C02-B8D9-E8C1F5E28717}.Checked|x64.Build.0 = Debug|Any CPU - {05696F45-ACF1-4C02-B8D9-E8C1F5E28717}.Checked|x86.ActiveCfg = Debug|Any CPU - {05696F45-ACF1-4C02-B8D9-E8C1F5E28717}.Checked|x86.Build.0 = Debug|Any CPU + {5EC8FDE3-DD46-4D89-80AC-AEF571C405B1}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {5EC8FDE3-DD46-4D89-80AC-AEF571C405B1}.Checked|Any CPU.Build.0 = Debug|Any CPU + {5EC8FDE3-DD46-4D89-80AC-AEF571C405B1}.Checked|x64.ActiveCfg = Debug|Any CPU + {5EC8FDE3-DD46-4D89-80AC-AEF571C405B1}.Checked|x64.Build.0 = Debug|Any CPU + {5EC8FDE3-DD46-4D89-80AC-AEF571C405B1}.Checked|x86.ActiveCfg = Debug|Any CPU + {5EC8FDE3-DD46-4D89-80AC-AEF571C405B1}.Checked|x86.Build.0 = Debug|Any CPU {5EC8FDE3-DD46-4D89-80AC-AEF571C405B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5EC8FDE3-DD46-4D89-80AC-AEF571C405B1}.Debug|Any CPU.Build.0 = Debug|Any CPU {5EC8FDE3-DD46-4D89-80AC-AEF571C405B1}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -92,12 +102,12 @@ Global {5EC8FDE3-DD46-4D89-80AC-AEF571C405B1}.Release|x64.Build.0 = Release|Any CPU {5EC8FDE3-DD46-4D89-80AC-AEF571C405B1}.Release|x86.ActiveCfg = Release|Any CPU {5EC8FDE3-DD46-4D89-80AC-AEF571C405B1}.Release|x86.Build.0 = Release|Any CPU - {5EC8FDE3-DD46-4D89-80AC-AEF571C405B1}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {5EC8FDE3-DD46-4D89-80AC-AEF571C405B1}.Checked|Any CPU.Build.0 = Debug|Any CPU - {5EC8FDE3-DD46-4D89-80AC-AEF571C405B1}.Checked|x64.ActiveCfg = Debug|Any CPU - {5EC8FDE3-DD46-4D89-80AC-AEF571C405B1}.Checked|x64.Build.0 = Debug|Any CPU - {5EC8FDE3-DD46-4D89-80AC-AEF571C405B1}.Checked|x86.ActiveCfg = Debug|Any CPU - {5EC8FDE3-DD46-4D89-80AC-AEF571C405B1}.Checked|x86.Build.0 = Debug|Any CPU + {8843EA69-AD8F-4C73-8436-1641470199DC}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {8843EA69-AD8F-4C73-8436-1641470199DC}.Checked|Any CPU.Build.0 = Debug|Any CPU + {8843EA69-AD8F-4C73-8436-1641470199DC}.Checked|x64.ActiveCfg = Debug|Any CPU + {8843EA69-AD8F-4C73-8436-1641470199DC}.Checked|x64.Build.0 = Debug|Any CPU + {8843EA69-AD8F-4C73-8436-1641470199DC}.Checked|x86.ActiveCfg = Debug|Any CPU + {8843EA69-AD8F-4C73-8436-1641470199DC}.Checked|x86.Build.0 = Debug|Any CPU {8843EA69-AD8F-4C73-8436-1641470199DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8843EA69-AD8F-4C73-8436-1641470199DC}.Debug|Any CPU.Build.0 = Debug|Any CPU {8843EA69-AD8F-4C73-8436-1641470199DC}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -110,12 +120,12 @@ Global {8843EA69-AD8F-4C73-8436-1641470199DC}.Release|x64.Build.0 = Release|Any CPU {8843EA69-AD8F-4C73-8436-1641470199DC}.Release|x86.ActiveCfg = Release|Any CPU {8843EA69-AD8F-4C73-8436-1641470199DC}.Release|x86.Build.0 = Release|Any CPU - {8843EA69-AD8F-4C73-8436-1641470199DC}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {8843EA69-AD8F-4C73-8436-1641470199DC}.Checked|Any CPU.Build.0 = Debug|Any CPU - {8843EA69-AD8F-4C73-8436-1641470199DC}.Checked|x64.ActiveCfg = Debug|Any CPU - {8843EA69-AD8F-4C73-8436-1641470199DC}.Checked|x64.Build.0 = Debug|Any CPU - {8843EA69-AD8F-4C73-8436-1641470199DC}.Checked|x86.ActiveCfg = Debug|Any CPU - {8843EA69-AD8F-4C73-8436-1641470199DC}.Checked|x86.Build.0 = Debug|Any CPU + {64BBA40A-8DB5-4829-815A-3D612A12222D}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {64BBA40A-8DB5-4829-815A-3D612A12222D}.Checked|Any CPU.Build.0 = Debug|Any CPU + {64BBA40A-8DB5-4829-815A-3D612A12222D}.Checked|x64.ActiveCfg = Debug|Any CPU + {64BBA40A-8DB5-4829-815A-3D612A12222D}.Checked|x64.Build.0 = Debug|Any CPU + {64BBA40A-8DB5-4829-815A-3D612A12222D}.Checked|x86.ActiveCfg = Debug|Any CPU + {64BBA40A-8DB5-4829-815A-3D612A12222D}.Checked|x86.Build.0 = Debug|Any CPU {64BBA40A-8DB5-4829-815A-3D612A12222D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {64BBA40A-8DB5-4829-815A-3D612A12222D}.Debug|Any CPU.Build.0 = Debug|Any CPU {64BBA40A-8DB5-4829-815A-3D612A12222D}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -128,12 +138,12 @@ Global {64BBA40A-8DB5-4829-815A-3D612A12222D}.Release|x64.Build.0 = Release|Any CPU {64BBA40A-8DB5-4829-815A-3D612A12222D}.Release|x86.ActiveCfg = Release|Any CPU {64BBA40A-8DB5-4829-815A-3D612A12222D}.Release|x86.Build.0 = Release|Any CPU - {64BBA40A-8DB5-4829-815A-3D612A12222D}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {64BBA40A-8DB5-4829-815A-3D612A12222D}.Checked|Any CPU.Build.0 = Debug|Any CPU - {64BBA40A-8DB5-4829-815A-3D612A12222D}.Checked|x64.ActiveCfg = Debug|Any CPU - {64BBA40A-8DB5-4829-815A-3D612A12222D}.Checked|x64.Build.0 = Debug|Any CPU - {64BBA40A-8DB5-4829-815A-3D612A12222D}.Checked|x86.ActiveCfg = Debug|Any CPU - {64BBA40A-8DB5-4829-815A-3D612A12222D}.Checked|x86.Build.0 = Debug|Any CPU + {A18E814C-13D6-4859-B6FA-3CAB8673B31F}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {A18E814C-13D6-4859-B6FA-3CAB8673B31F}.Checked|Any CPU.Build.0 = Debug|Any CPU + {A18E814C-13D6-4859-B6FA-3CAB8673B31F}.Checked|x64.ActiveCfg = Debug|Any CPU + {A18E814C-13D6-4859-B6FA-3CAB8673B31F}.Checked|x64.Build.0 = Debug|Any CPU + {A18E814C-13D6-4859-B6FA-3CAB8673B31F}.Checked|x86.ActiveCfg = Debug|Any CPU + {A18E814C-13D6-4859-B6FA-3CAB8673B31F}.Checked|x86.Build.0 = Debug|Any CPU {A18E814C-13D6-4859-B6FA-3CAB8673B31F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A18E814C-13D6-4859-B6FA-3CAB8673B31F}.Debug|Any CPU.Build.0 = Debug|Any CPU {A18E814C-13D6-4859-B6FA-3CAB8673B31F}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -146,12 +156,12 @@ Global {A18E814C-13D6-4859-B6FA-3CAB8673B31F}.Release|x64.Build.0 = Release|Any CPU {A18E814C-13D6-4859-B6FA-3CAB8673B31F}.Release|x86.ActiveCfg = Release|Any CPU {A18E814C-13D6-4859-B6FA-3CAB8673B31F}.Release|x86.Build.0 = Release|Any CPU - {A18E814C-13D6-4859-B6FA-3CAB8673B31F}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {A18E814C-13D6-4859-B6FA-3CAB8673B31F}.Checked|Any CPU.Build.0 = Debug|Any CPU - {A18E814C-13D6-4859-B6FA-3CAB8673B31F}.Checked|x64.ActiveCfg = Debug|Any CPU - {A18E814C-13D6-4859-B6FA-3CAB8673B31F}.Checked|x64.Build.0 = Debug|Any CPU - {A18E814C-13D6-4859-B6FA-3CAB8673B31F}.Checked|x86.ActiveCfg = Debug|Any CPU - {A18E814C-13D6-4859-B6FA-3CAB8673B31F}.Checked|x86.Build.0 = Debug|Any CPU + {EA6F01DF-1F63-49FF-A6E8-CA9104296196}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {EA6F01DF-1F63-49FF-A6E8-CA9104296196}.Checked|Any CPU.Build.0 = Debug|Any CPU + {EA6F01DF-1F63-49FF-A6E8-CA9104296196}.Checked|x64.ActiveCfg = Debug|Any CPU + {EA6F01DF-1F63-49FF-A6E8-CA9104296196}.Checked|x64.Build.0 = Debug|Any CPU + {EA6F01DF-1F63-49FF-A6E8-CA9104296196}.Checked|x86.ActiveCfg = Debug|Any CPU + {EA6F01DF-1F63-49FF-A6E8-CA9104296196}.Checked|x86.Build.0 = Debug|Any CPU {EA6F01DF-1F63-49FF-A6E8-CA9104296196}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EA6F01DF-1F63-49FF-A6E8-CA9104296196}.Debug|Any CPU.Build.0 = Debug|Any CPU {EA6F01DF-1F63-49FF-A6E8-CA9104296196}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -164,12 +174,12 @@ Global {EA6F01DF-1F63-49FF-A6E8-CA9104296196}.Release|x64.Build.0 = Release|Any CPU {EA6F01DF-1F63-49FF-A6E8-CA9104296196}.Release|x86.ActiveCfg = Release|Any CPU {EA6F01DF-1F63-49FF-A6E8-CA9104296196}.Release|x86.Build.0 = Release|Any CPU - {EA6F01DF-1F63-49FF-A6E8-CA9104296196}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {EA6F01DF-1F63-49FF-A6E8-CA9104296196}.Checked|Any CPU.Build.0 = Debug|Any CPU - {EA6F01DF-1F63-49FF-A6E8-CA9104296196}.Checked|x64.ActiveCfg = Debug|Any CPU - {EA6F01DF-1F63-49FF-A6E8-CA9104296196}.Checked|x64.Build.0 = Debug|Any CPU - {EA6F01DF-1F63-49FF-A6E8-CA9104296196}.Checked|x86.ActiveCfg = Debug|Any CPU - {EA6F01DF-1F63-49FF-A6E8-CA9104296196}.Checked|x86.Build.0 = Debug|Any CPU + {1F1F9925-48BB-4384-9C46-ADBDCC7E72D1}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {1F1F9925-48BB-4384-9C46-ADBDCC7E72D1}.Checked|Any CPU.Build.0 = Debug|Any CPU + {1F1F9925-48BB-4384-9C46-ADBDCC7E72D1}.Checked|x64.ActiveCfg = Debug|Any CPU + {1F1F9925-48BB-4384-9C46-ADBDCC7E72D1}.Checked|x64.Build.0 = Debug|Any CPU + {1F1F9925-48BB-4384-9C46-ADBDCC7E72D1}.Checked|x86.ActiveCfg = Debug|Any CPU + {1F1F9925-48BB-4384-9C46-ADBDCC7E72D1}.Checked|x86.Build.0 = Debug|Any CPU {1F1F9925-48BB-4384-9C46-ADBDCC7E72D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1F1F9925-48BB-4384-9C46-ADBDCC7E72D1}.Debug|Any CPU.Build.0 = Debug|Any CPU {1F1F9925-48BB-4384-9C46-ADBDCC7E72D1}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -182,12 +192,12 @@ Global {1F1F9925-48BB-4384-9C46-ADBDCC7E72D1}.Release|x64.Build.0 = Release|Any CPU {1F1F9925-48BB-4384-9C46-ADBDCC7E72D1}.Release|x86.ActiveCfg = Release|Any CPU {1F1F9925-48BB-4384-9C46-ADBDCC7E72D1}.Release|x86.Build.0 = Release|Any CPU - {1F1F9925-48BB-4384-9C46-ADBDCC7E72D1}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {1F1F9925-48BB-4384-9C46-ADBDCC7E72D1}.Checked|Any CPU.Build.0 = Debug|Any CPU - {1F1F9925-48BB-4384-9C46-ADBDCC7E72D1}.Checked|x64.ActiveCfg = Debug|Any CPU - {1F1F9925-48BB-4384-9C46-ADBDCC7E72D1}.Checked|x64.Build.0 = Debug|Any CPU - {1F1F9925-48BB-4384-9C46-ADBDCC7E72D1}.Checked|x86.ActiveCfg = Debug|Any CPU - {1F1F9925-48BB-4384-9C46-ADBDCC7E72D1}.Checked|x86.Build.0 = Debug|Any CPU + {FFA71ACB-EAD8-4242-A5D6-FBB3F704949D}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {FFA71ACB-EAD8-4242-A5D6-FBB3F704949D}.Checked|Any CPU.Build.0 = Debug|Any CPU + {FFA71ACB-EAD8-4242-A5D6-FBB3F704949D}.Checked|x64.ActiveCfg = Debug|Any CPU + {FFA71ACB-EAD8-4242-A5D6-FBB3F704949D}.Checked|x64.Build.0 = Debug|Any CPU + {FFA71ACB-EAD8-4242-A5D6-FBB3F704949D}.Checked|x86.ActiveCfg = Debug|Any CPU + {FFA71ACB-EAD8-4242-A5D6-FBB3F704949D}.Checked|x86.Build.0 = Debug|Any CPU {FFA71ACB-EAD8-4242-A5D6-FBB3F704949D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FFA71ACB-EAD8-4242-A5D6-FBB3F704949D}.Debug|Any CPU.Build.0 = Debug|Any CPU {FFA71ACB-EAD8-4242-A5D6-FBB3F704949D}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -200,12 +210,12 @@ Global {FFA71ACB-EAD8-4242-A5D6-FBB3F704949D}.Release|x64.Build.0 = Release|Any CPU {FFA71ACB-EAD8-4242-A5D6-FBB3F704949D}.Release|x86.ActiveCfg = Release|Any CPU {FFA71ACB-EAD8-4242-A5D6-FBB3F704949D}.Release|x86.Build.0 = Release|Any CPU - {FFA71ACB-EAD8-4242-A5D6-FBB3F704949D}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {FFA71ACB-EAD8-4242-A5D6-FBB3F704949D}.Checked|Any CPU.Build.0 = Debug|Any CPU - {FFA71ACB-EAD8-4242-A5D6-FBB3F704949D}.Checked|x64.ActiveCfg = Debug|Any CPU - {FFA71ACB-EAD8-4242-A5D6-FBB3F704949D}.Checked|x64.Build.0 = Debug|Any CPU - {FFA71ACB-EAD8-4242-A5D6-FBB3F704949D}.Checked|x86.ActiveCfg = Debug|Any CPU - {FFA71ACB-EAD8-4242-A5D6-FBB3F704949D}.Checked|x86.Build.0 = Debug|Any CPU + {3461E542-7F19-4B98-B206-BDF932529A5B}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {3461E542-7F19-4B98-B206-BDF932529A5B}.Checked|Any CPU.Build.0 = Debug|Any CPU + {3461E542-7F19-4B98-B206-BDF932529A5B}.Checked|x64.ActiveCfg = Debug|Any CPU + {3461E542-7F19-4B98-B206-BDF932529A5B}.Checked|x64.Build.0 = Debug|Any CPU + {3461E542-7F19-4B98-B206-BDF932529A5B}.Checked|x86.ActiveCfg = Debug|Any CPU + {3461E542-7F19-4B98-B206-BDF932529A5B}.Checked|x86.Build.0 = Debug|Any CPU {3461E542-7F19-4B98-B206-BDF932529A5B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3461E542-7F19-4B98-B206-BDF932529A5B}.Debug|Any CPU.Build.0 = Debug|Any CPU {3461E542-7F19-4B98-B206-BDF932529A5B}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -218,12 +228,12 @@ Global {3461E542-7F19-4B98-B206-BDF932529A5B}.Release|x64.Build.0 = Release|Any CPU {3461E542-7F19-4B98-B206-BDF932529A5B}.Release|x86.ActiveCfg = Release|Any CPU {3461E542-7F19-4B98-B206-BDF932529A5B}.Release|x86.Build.0 = Release|Any CPU - {3461E542-7F19-4B98-B206-BDF932529A5B}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {3461E542-7F19-4B98-B206-BDF932529A5B}.Checked|Any CPU.Build.0 = Debug|Any CPU - {3461E542-7F19-4B98-B206-BDF932529A5B}.Checked|x64.ActiveCfg = Debug|Any CPU - {3461E542-7F19-4B98-B206-BDF932529A5B}.Checked|x64.Build.0 = Debug|Any CPU - {3461E542-7F19-4B98-B206-BDF932529A5B}.Checked|x86.ActiveCfg = Debug|Any CPU - {3461E542-7F19-4B98-B206-BDF932529A5B}.Checked|x86.Build.0 = Debug|Any CPU + {D2A00A5C-148B-43D2-BD72-4B70D8E990EB}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {D2A00A5C-148B-43D2-BD72-4B70D8E990EB}.Checked|Any CPU.Build.0 = Debug|Any CPU + {D2A00A5C-148B-43D2-BD72-4B70D8E990EB}.Checked|x64.ActiveCfg = Debug|Any CPU + {D2A00A5C-148B-43D2-BD72-4B70D8E990EB}.Checked|x64.Build.0 = Debug|Any CPU + {D2A00A5C-148B-43D2-BD72-4B70D8E990EB}.Checked|x86.ActiveCfg = Debug|Any CPU + {D2A00A5C-148B-43D2-BD72-4B70D8E990EB}.Checked|x86.Build.0 = Debug|Any CPU {D2A00A5C-148B-43D2-BD72-4B70D8E990EB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D2A00A5C-148B-43D2-BD72-4B70D8E990EB}.Debug|Any CPU.Build.0 = Debug|Any CPU {D2A00A5C-148B-43D2-BD72-4B70D8E990EB}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -236,12 +246,12 @@ Global {D2A00A5C-148B-43D2-BD72-4B70D8E990EB}.Release|x64.Build.0 = Release|Any CPU {D2A00A5C-148B-43D2-BD72-4B70D8E990EB}.Release|x86.ActiveCfg = Release|Any CPU {D2A00A5C-148B-43D2-BD72-4B70D8E990EB}.Release|x86.Build.0 = Release|Any CPU - {D2A00A5C-148B-43D2-BD72-4B70D8E990EB}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {D2A00A5C-148B-43D2-BD72-4B70D8E990EB}.Checked|Any CPU.Build.0 = Debug|Any CPU - {D2A00A5C-148B-43D2-BD72-4B70D8E990EB}.Checked|x64.ActiveCfg = Debug|Any CPU - {D2A00A5C-148B-43D2-BD72-4B70D8E990EB}.Checked|x64.Build.0 = Debug|Any CPU - {D2A00A5C-148B-43D2-BD72-4B70D8E990EB}.Checked|x86.ActiveCfg = Debug|Any CPU - {D2A00A5C-148B-43D2-BD72-4B70D8E990EB}.Checked|x86.Build.0 = Debug|Any CPU + {81204A19-8DF3-461B-A5FE-7B85149F5CCD}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {81204A19-8DF3-461B-A5FE-7B85149F5CCD}.Checked|Any CPU.Build.0 = Debug|Any CPU + {81204A19-8DF3-461B-A5FE-7B85149F5CCD}.Checked|x64.ActiveCfg = Debug|Any CPU + {81204A19-8DF3-461B-A5FE-7B85149F5CCD}.Checked|x64.Build.0 = Debug|Any CPU + {81204A19-8DF3-461B-A5FE-7B85149F5CCD}.Checked|x86.ActiveCfg = Debug|Any CPU + {81204A19-8DF3-461B-A5FE-7B85149F5CCD}.Checked|x86.Build.0 = Debug|Any CPU {81204A19-8DF3-461B-A5FE-7B85149F5CCD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {81204A19-8DF3-461B-A5FE-7B85149F5CCD}.Debug|Any CPU.Build.0 = Debug|Any CPU {81204A19-8DF3-461B-A5FE-7B85149F5CCD}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -254,31 +264,28 @@ Global {81204A19-8DF3-461B-A5FE-7B85149F5CCD}.Release|x64.Build.0 = Release|Any CPU {81204A19-8DF3-461B-A5FE-7B85149F5CCD}.Release|x86.ActiveCfg = Release|Any CPU {81204A19-8DF3-461B-A5FE-7B85149F5CCD}.Release|x86.Build.0 = Release|Any CPU - {81204A19-8DF3-461B-A5FE-7B85149F5CCD}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {81204A19-8DF3-461B-A5FE-7B85149F5CCD}.Checked|Any CPU.Build.0 = Debug|Any CPU - {81204A19-8DF3-461B-A5FE-7B85149F5CCD}.Checked|x64.ActiveCfg = Debug|Any CPU - {81204A19-8DF3-461B-A5FE-7B85149F5CCD}.Checked|x64.Build.0 = Debug|Any CPU - {81204A19-8DF3-461B-A5FE-7B85149F5CCD}.Checked|x86.ActiveCfg = Debug|Any CPU - {81204A19-8DF3-461B-A5FE-7B85149F5CCD}.Checked|x86.Build.0 = Debug|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {B1053D24-237E-4E55-9413-20B34ED79F23} = {C723F596-B189-428C-A090-F4965F87A73D} - {A18E814C-13D6-4859-B6FA-3CAB8673B31F} = {C723F596-B189-428C-A090-F4965F87A73D} {05696F45-ACF1-4C02-B8D9-E8C1F5E28717} = {61C529DF-66C4-42E9-AE70-3427838FAFE3} - {EA6F01DF-1F63-49FF-A6E8-CA9104296196} = {61C529DF-66C4-42E9-AE70-3427838FAFE3} {5EC8FDE3-DD46-4D89-80AC-AEF571C405B1} = {A20A0878-5647-4145-B224-C390446B7676} + {8843EA69-AD8F-4C73-8436-1641470199DC} = {155A2103-05C1-4020-8922-4F6275F6AE7E} {64BBA40A-8DB5-4829-815A-3D612A12222D} = {A20A0878-5647-4145-B224-C390446B7676} + {A18E814C-13D6-4859-B6FA-3CAB8673B31F} = {C723F596-B189-428C-A090-F4965F87A73D} + {EA6F01DF-1F63-49FF-A6E8-CA9104296196} = {61C529DF-66C4-42E9-AE70-3427838FAFE3} {1F1F9925-48BB-4384-9C46-ADBDCC7E72D1} = {A20A0878-5647-4145-B224-C390446B7676} - {D2A00A5C-148B-43D2-BD72-4B70D8E990EB} = {A20A0878-5647-4145-B224-C390446B7676} - {81204A19-8DF3-461B-A5FE-7B85149F5CCD} = {A20A0878-5647-4145-B224-C390446B7676} - {8843EA69-AD8F-4C73-8436-1641470199DC} = {155A2103-05C1-4020-8922-4F6275F6AE7E} {FFA71ACB-EAD8-4242-A5D6-FBB3F704949D} = {155A2103-05C1-4020-8922-4F6275F6AE7E} {3461E542-7F19-4B98-B206-BDF932529A5B} = {155A2103-05C1-4020-8922-4F6275F6AE7E} + {D2A00A5C-148B-43D2-BD72-4B70D8E990EB} = {A20A0878-5647-4145-B224-C390446B7676} + {81204A19-8DF3-461B-A5FE-7B85149F5CCD} = {A20A0878-5647-4145-B224-C390446B7676} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {248E0825-3C88-4854-B192-C50C9FDDBBC3} EndGlobalSection + GlobalSection(SharedMSBuildProjectFiles) = preSolution + ..\System.Private.CoreLib\src\System.Private.CoreLib.Shared.projitems*{b1053d24-237e-4e55-9413-20b34ed79f23}*SharedItemsImports = 5 + EndGlobalSection EndGlobal diff --git a/src/libraries/System.Reflection.Emit.ILGeneration/tests/ILGenerator/Emit5Tests.cs b/src/libraries/System.Reflection.Emit.ILGeneration/tests/ILGenerator/Emit5Tests.cs new file mode 100644 index 0000000000000..a7bf79743c747 --- /dev/null +++ b/src/libraries/System.Reflection.Emit.ILGeneration/tests/ILGenerator/Emit5Tests.cs @@ -0,0 +1,300 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using Xunit; + +namespace System.Reflection.Emit.Tests +{ + public class ILGeneratorEmit5 + { + [Fact] + public void MaxStackOverflowTest() + { + Run(1 << 5); + Run(1 << 10); + + // Previously this threw because the computed stack depth was 2^16 + 1, which is 1 mod 2^16 + // and 1 is too small. + Run(1 << 14); + + static void Run(int num) + { + var meth = GetCode(num); + Assert.NotNull(meth); + Assert.Equal(typeof(int), meth.ReturnType); + + var body = meth.GetMethodBody(); + Assert.Equal(4, body.MaxStackSize); // Previously the depth was computed as 4 * num + 1. + + var val = (int)meth.Invoke(null, null); + Assert.Equal(4 * num, val); + } + + /// + /// The parameter is the number of basic blocks. Each has a max stack + /// depth of four. There is one final basic block with max stack of one. The ILGenerator + /// erroneously adds these, so the final value can overflow 2^16. When that result mod 2^16 + /// is less than required, the CLR throws an . + /// + static MethodInfo GetCode(int num) + { + TypeBuilder type = Helpers.DynamicType(TypeAttributes.Public); + MethodBuilder method = type.DefineMethod("meth1", MethodAttributes.Public | MethodAttributes.Static, typeof(int), Type.EmptyTypes); + var ilg = method.GetILGenerator(); + + var loc = ilg.DeclareLocal(typeof(int)); + ilg.Emit(OpCodes.Ldc_I4_0); + ilg.Emit(OpCodes.Stloc, loc); + + for (int i = 0; i < num; i++) + { + ilg.Emit(OpCodes.Ldloc, loc); + ilg.Emit(OpCodes.Ldc_I4_1); + ilg.Emit(OpCodes.Ldc_I4_1); + ilg.Emit(OpCodes.Ldc_I4_2); + ilg.Emit(OpCodes.Add); + ilg.Emit(OpCodes.Add); + ilg.Emit(OpCodes.Add); + ilg.Emit(OpCodes.Stloc, loc); + + // Unconditional jump to next block. + var labNext = ilg.DefineLabel(); + ilg.Emit(OpCodes.Br, labNext); + ilg.MarkLabel(labNext); + } + + ilg.Emit(OpCodes.Ldloc, loc); + ilg.Emit(OpCodes.Ret); + + // Create the type where this method is in + Type createdType = type.CreateTypeInfo().AsType(); + MethodInfo createdMethod = createdType.GetMethod("meth1"); + + return createdMethod; + } + } + + [Fact] + public void MaxStackNonEmptyForward() + { + // This test uses forward branches to "new" basic blocks where the stack depth + // at the branch location is non-empty. + + Run(1 << 0); + Run(1 << 1); + Run(1 << 5); + + // This one seems to overwhelm the jit or something. + // Run(1 << 10); + + void Run(int num) + { + var meth = GetCode(num); + Assert.NotNull(meth); + Assert.Equal(typeof(int), meth.ReturnType); + + var body = meth.GetMethodBody(); + Assert.Equal(2 * num + 3, body.MaxStackSize); // Previously the depth was computed as 4 * num + 1. + + var val = (int)meth.Invoke(null, null); + Assert.Equal(4 * num, val); + } + + static MethodInfo GetCode(int num) + { + TypeBuilder type = Helpers.DynamicType(TypeAttributes.Public); + MethodBuilder method = type.DefineMethod("meth1", MethodAttributes.Public | MethodAttributes.Static, typeof(int), Type.EmptyTypes); + var ilg = method.GetILGenerator(); + + ilg.Emit(OpCodes.Ldc_I4_0); + for (int i = 0; i < num; i++) + { + ilg.Emit(OpCodes.Ldc_I4_1); + ilg.Emit(OpCodes.Ldc_I4_1); + ilg.Emit(OpCodes.Ldc_I4_1); + ilg.Emit(OpCodes.Ldc_I4_1); + ilg.Emit(OpCodes.Add); + ilg.Emit(OpCodes.Add); + + // Unconditional jump to next block. + var labNext = ilg.DefineLabel(); + ilg.Emit(OpCodes.Br, labNext); + ilg.MarkLabel(labNext); + } + + // Each block leaves two values on the stack. Add them into the previous value. + for (int i = 0; i < num; i++) + { + ilg.Emit(OpCodes.Add); + ilg.Emit(OpCodes.Add); + } + + ilg.Emit(OpCodes.Ret); + + // Create the type where this method is in + Type createdType = type.CreateTypeInfo().AsType(); + return createdType.GetMethod("meth1"); + } + } + + [Fact] + public void MaxStackNonEmptyBackward() + { + // This test uses backward branches to "new" basic blocks where the stack depth + // at the branch location is non-empty. + + Run(1 << 1); + Run(1 << 2); + Run(1 << 3); + Run(1 << 4); + Run(1 << 5); + + // This one seems to overwhelm the jit or something. + // Run(1 << 10); + + void Run(int num) + { + var meth = GetCode(num); + Assert.NotNull(meth); + Assert.Equal(typeof(int), meth.ReturnType); + + var body = meth.GetMethodBody(); + Assert.Equal(4 * num + 2, body.MaxStackSize); + + var val = (int)meth.Invoke(null, null); + Assert.Equal(4 * num, val); + } + + static MethodInfo GetCode(int num) + { + TypeBuilder type = Helpers.DynamicType(TypeAttributes.Public); + MethodBuilder method = type.DefineMethod("meth1", MethodAttributes.Public | MethodAttributes.Static, typeof(int), Type.EmptyTypes); + var ilg = method.GetILGenerator(); + + var labels = new Label[num + 1]; + for (int i = 0; i <= num; i++) + labels[i] = ilg.DefineLabel(); + + ilg.Emit(OpCodes.Ldc_I4_0); + ilg.Emit(OpCodes.Br, labels[0]); + + for (int i = num; --i >= 0;) + { + ilg.MarkLabel(labels[i]); + + ilg.Emit(OpCodes.Ldc_I4_1); + ilg.Emit(OpCodes.Ldc_I4_1); + ilg.Emit(OpCodes.Ldc_I4_1); + ilg.Emit(OpCodes.Ldc_I4_1); + ilg.Emit(OpCodes.Add); + ilg.Emit(OpCodes.Add); + + // Unconditional jump to "next" block (which is really before this code). + ilg.Emit(OpCodes.Br, labels[i + 1]); + } + + ilg.MarkLabel(labels[num]); + + // Each block leaves two values on the stack. Add them into the previous value. + for (int i = 0; i < num; i++) + { + ilg.Emit(OpCodes.Add); + ilg.Emit(OpCodes.Add); + } + + ilg.Emit(OpCodes.Ret); + + // Create the type where this method is in + Type createdType = type.CreateTypeInfo().AsType(); + return createdType.GetMethod("meth1"); + } + } + + [Fact] + public void AmbiguousDepth() + { + var meth = GetCode(); + Assert.NotNull(meth); + Assert.Equal(typeof(int), meth.ReturnType); + + var body = meth.GetMethodBody(); + // Observed depth of 2, with "adjustment" of 1. + Assert.Equal(2 + 1, body.MaxStackSize); + + try + { + meth.Invoke(null, new object[] { false }); + Assert.True(false); + } + catch (TargetInvocationException ex) + { + Assert.IsType(ex.InnerException); + } + + static MethodInfo GetCode() + { + TypeBuilder type = Helpers.DynamicType(TypeAttributes.Public); + MethodBuilder method = type.DefineMethod("meth1", MethodAttributes.Public | MethodAttributes.Static, typeof(int), new[] { typeof(bool) }); + var ilg = method.GetILGenerator(); + + // The label is targeted with stack depth zero. + var lab = ilg.DefineLabel(); + ilg.Emit(OpCodes.Ldarg_0); + ilg.Emit(OpCodes.Brfalse, lab); + + // The label is marked with a larger stack depth, one. This IL is invalid. + ilg.Emit(OpCodes.Ldc_I4_1); + ilg.MarkLabel(lab); + + ilg.Emit(OpCodes.Ldc_I4_1); + ilg.Emit(OpCodes.Add); + ilg.Emit(OpCodes.Ret); + + // Create the type where this method is in + Type createdType = type.CreateTypeInfo().AsType(); + return createdType.GetMethod("meth1"); + } + } + + [Fact] + public void UnreachableDepth() + { + var meth = GetCode(); + Assert.NotNull(meth); + Assert.Equal(typeof(int), meth.ReturnType); + + var body = meth.GetMethodBody(); + // Observed depth of 2, with no "adjustment". + Assert.Equal(2, body.MaxStackSize); + + var val = (int)meth.Invoke(null, null); + Assert.Equal(2, val); + + static MethodInfo GetCode() + { + TypeBuilder type = Helpers.DynamicType(TypeAttributes.Public); + MethodBuilder method = type.DefineMethod("meth1", MethodAttributes.Public | MethodAttributes.Static, typeof(int), Type.EmptyTypes); + var ilg = method.GetILGenerator(); + + var lab = ilg.DefineLabel(); + + ilg.Emit(OpCodes.Ldc_I4_1); + ilg.Emit(OpCodes.Ldc_I4_1); + ilg.Emit(OpCodes.Br, lab); + + // Unreachable. + ilg.Emit(OpCodes.Ldarg_0); + + // Depth + ilg.MarkLabel(lab); + ilg.Emit(OpCodes.Add); + ilg.Emit(OpCodes.Ret); + + // Create the type where this method is in + Type createdType = type.CreateTypeInfo().AsType(); + return createdType.GetMethod("meth1"); + } + } + } +} diff --git a/src/libraries/System.Reflection.Emit.ILGeneration/tests/System.Reflection.Emit.ILGeneration.Tests.csproj b/src/libraries/System.Reflection.Emit.ILGeneration/tests/System.Reflection.Emit.ILGeneration.Tests.csproj index fb6a8ef9b6fd0..9c672251db0af 100644 --- a/src/libraries/System.Reflection.Emit.ILGeneration/tests/System.Reflection.Emit.ILGeneration.Tests.csproj +++ b/src/libraries/System.Reflection.Emit.ILGeneration/tests/System.Reflection.Emit.ILGeneration.Tests.csproj @@ -10,6 +10,7 @@ + From aa31c31501a56b1bc41f0e84c9a53eb7df92863b Mon Sep 17 00:00:00 2001 From: Patrick Nguyen Date: Tue, 7 Jun 2022 21:01:52 -0700 Subject: [PATCH 2/5] Fix System.Linq.Expressions tests --- .../Coalesce/BinaryCoalesceTests.cs | 2 +- .../tests/CompilerTests.cs | 2 +- .../tests/StackSpillerTests.cs | 42 +++++++++---------- ...aryArithmeticNegateCheckedNullableTests.cs | 2 +- 4 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/libraries/System.Linq.Expressions/tests/BinaryOperators/Coalesce/BinaryCoalesceTests.cs b/src/libraries/System.Linq.Expressions/tests/BinaryOperators/Coalesce/BinaryCoalesceTests.cs index 50131d51cbc25..793c26f85332f 100644 --- a/src/libraries/System.Linq.Expressions/tests/BinaryOperators/Coalesce/BinaryCoalesceTests.cs +++ b/src/libraries/System.Linq.Expressions/tests/BinaryOperators/Coalesce/BinaryCoalesceTests.cs @@ -506,7 +506,7 @@ public static void VerifyIL_NullableIntCoalesceToNullableInt() f.VerifyIL( @".method valuetype [System.Private.CoreLib]System.Nullable`1 ::lambda_method(class [System.Linq.Expressions]System.Runtime.CompilerServices.Closure,valuetype [System.Private.CoreLib]System.Nullable`1,valuetype [System.Private.CoreLib]System.Nullable`1) { - .maxstack 2 + .maxstack 1 .locals init ( [0] valuetype [System.Private.CoreLib]System.Nullable`1 ) diff --git a/src/libraries/System.Linq.Expressions/tests/CompilerTests.cs b/src/libraries/System.Linq.Expressions/tests/CompilerTests.cs index 1093a8235fc44..069d72e6abcef 100644 --- a/src/libraries/System.Linq.Expressions/tests/CompilerTests.cs +++ b/src/libraries/System.Linq.Expressions/tests/CompilerTests.cs @@ -184,7 +184,7 @@ public static void VerifyIL_Exceptions() f.VerifyIL( @".method int32 ::lambda_method(class [System.Linq.Expressions]System.Runtime.CompilerServices.Closure,int32) { - .maxstack 4 + .maxstack 2 .locals init ( [0] int32 ) diff --git a/src/libraries/System.Linq.Expressions/tests/StackSpillerTests.cs b/src/libraries/System.Linq.Expressions/tests/StackSpillerTests.cs index 480b85028db88..d931126b4ef2b 100644 --- a/src/libraries/System.Linq.Expressions/tests/StackSpillerTests.cs +++ b/src/libraries/System.Linq.Expressions/tests/StackSpillerTests.cs @@ -413,7 +413,7 @@ public static void Spill_RefInstance_IndexAssignment_CodeGen() il: @" .method int32 ::lambda_method(class [System.Linq.Expressions]System.Runtime.CompilerServices.Closure) { - .maxstack 4 + .maxstack 3 .locals init ( [0] valuetype [System.Linq.Expressions.Tests]System.Linq.Expressions.Tests.StackSpillerTests+ValueVector&, [1] int32, @@ -516,7 +516,7 @@ public static void Spill_RefInstance_Index_CodeGen() il: @" .method int32 ::lambda_method(class [System.Linq.Expressions]System.Runtime.CompilerServices.Closure) { - .maxstack 3 + .maxstack 2 .locals init ( [0] valuetype [System.Linq.Expressions.Tests]System.Linq.Expressions.Tests.StackSpillerTests+ValueBar&, [1] int32, @@ -611,7 +611,7 @@ public static void Spill_RefInstance_MemberAssignment_CodeGen() il: @" .method int32 ::lambda_method(class [System.Linq.Expressions]System.Runtime.CompilerServices.Closure) { - .maxstack 3 + .maxstack 2 .locals init ( [0] valuetype [System.Linq.Expressions.Tests]System.Linq.Expressions.Tests.StackSpillerTests+ValueBar&, [1] int32, @@ -716,7 +716,7 @@ public static void Spill_RefInstance_Call_CodeGen() il: @" .method int32 ::lambda_method(class [System.Linq.Expressions]System.Runtime.CompilerServices.Closure) { - .maxstack 3 + .maxstack 2 .locals init ( [0] valuetype [System.Linq.Expressions.Tests]System.Linq.Expressions.Tests.StackSpillerTests+ValueBar&, [1] int32, @@ -810,7 +810,7 @@ public static void Spill_RefArgs_Call_CodeGen() il: @" .method int32 ::lambda_method(class [System.Linq.Expressions]System.Runtime.CompilerServices.Closure) { - .maxstack 3 + .maxstack 2 .locals init ( [0] int32&, [1] int32, @@ -902,7 +902,7 @@ public static void Spill_RefArgs_New_CodeGen() il: @" .method int32 ::lambda_method(class [System.Linq.Expressions]System.Runtime.CompilerServices.Closure) { - .maxstack 5 + .maxstack 4 .locals init ( [0] int32&, [1] int32, @@ -985,7 +985,7 @@ public static void Spill_RefArgs_Invoke_CodeGen() il: @" .method int32 ::lambda_method(class [System.Linq.Expressions]System.Runtime.CompilerServices.Closure) { - .maxstack 4 + .maxstack 3 .locals init ( [0] int32&, [1] int32, @@ -1096,7 +1096,7 @@ public static void Spill_RefArgs_Invoke_Inline_CodeGen() il: @" .method int32 ::lambda_method(class [System.Linq.Expressions]System.Runtime.CompilerServices.Closure) { - .maxstack 3 + .maxstack 2 .locals init ( [0] int32&, [1] int32, @@ -1202,7 +1202,7 @@ public static void Spill_RefInstance_ListInit_CodeGen() il: @" .method valuetype [System.Linq.Expressions.Tests]System.Linq.Expressions.Tests.StackSpillerTests+ValueList ::lambda_method(class [System.Linq.Expressions]System.Runtime.CompilerServices.Closure) { - .maxstack 3 + .maxstack 2 .locals init ( [0] valuetype [System.Linq.Expressions.Tests]System.Linq.Expressions.Tests.StackSpillerTests+ValueList, [1] valuetype [System.Linq.Expressions.Tests]System.Linq.Expressions.Tests.StackSpillerTests+ValueList&, @@ -1309,7 +1309,7 @@ public static void Spill_RefInstance_MemberInit_Assign_Field_CodeGen() il: @" .method valuetype [System.Linq.Expressions.Tests]System.Linq.Expressions.Tests.StackSpillerTests+ValueBar ::lambda_method(class [System.Linq.Expressions]System.Runtime.CompilerServices.Closure) { - .maxstack 3 + .maxstack 2 .locals init ( [0] valuetype [System.Linq.Expressions.Tests]System.Linq.Expressions.Tests.StackSpillerTests+ValueBar, [1] valuetype [System.Linq.Expressions.Tests]System.Linq.Expressions.Tests.StackSpillerTests+ValueBar&, @@ -1416,7 +1416,7 @@ public static void Spill_RefInstance_MemberInit_Assign_Property_CodeGen() il: @" .method valuetype [System.Linq.Expressions.Tests]System.Linq.Expressions.Tests.StackSpillerTests+ValueBar ::lambda_method(class [System.Linq.Expressions]System.Runtime.CompilerServices.Closure) { - .maxstack 3 + .maxstack 2 .locals init ( [0] valuetype [System.Linq.Expressions.Tests]System.Linq.Expressions.Tests.StackSpillerTests+ValueBar, [1] valuetype [System.Linq.Expressions.Tests]System.Linq.Expressions.Tests.StackSpillerTests+ValueBar&, @@ -1513,7 +1513,7 @@ public static void Spill_RefInstance_MemberInit_MemberBind_CodeGen() il: @" .method valuetype [System.Linq.Expressions.Tests]System.Linq.Expressions.Tests.StackSpillerTests+ValueBar ::lambda_method(class [System.Linq.Expressions]System.Runtime.CompilerServices.Closure) { - .maxstack 6 + .maxstack 4 .locals init ( [0] valuetype [System.Linq.Expressions.Tests]System.Linq.Expressions.Tests.StackSpillerTests+ValueBar, [1] valuetype [System.Linq.Expressions.Tests]System.Linq.Expressions.Tests.StackSpillerTests+ValueBar&, @@ -1605,7 +1605,7 @@ public static void Spill_RefInstance_MemberInit_ListBind_CodeGen() il: @" .method valuetype [System.Linq.Expressions.Tests]System.Linq.Expressions.Tests.StackSpillerTests+ValueBar ::lambda_method(class [System.Linq.Expressions]System.Runtime.CompilerServices.Closure) { - .maxstack 3 + .maxstack 2 .locals init ( [0] valuetype [System.Linq.Expressions.Tests]System.Linq.Expressions.Tests.StackSpillerTests+ValueBar, [1] valuetype [System.Linq.Expressions.Tests]System.Linq.Expressions.Tests.StackSpillerTests+ValueBar&, @@ -1692,7 +1692,7 @@ public static void Spill_Optimizations_Constant() e.VerifyIL(@" .method void ::lambda_method(class [System.Linq.Expressions]System.Runtime.CompilerServices.Closure,int32[]) { - .maxstack 4 + .maxstack 3 .locals init ( [0] int32[], [1] int32, @@ -1750,7 +1750,7 @@ public static void Spill_Optimizations_Default() e.VerifyIL(@" .method void ::lambda_method(class [System.Linq.Expressions]System.Runtime.CompilerServices.Closure,int32[]) { - .maxstack 4 + .maxstack 3 .locals init ( [0] int32[], [1] int32, @@ -1806,7 +1806,7 @@ public static void Spill_Optimizations_LiteralField_NotNetFramework() e.VerifyIL(@" .method float64 ::lambda_method(class [System.Linq.Expressions]System.Runtime.CompilerServices.Closure) { - .maxstack 3 + .maxstack 2 .locals init ( [0] float64, [1] float64 @@ -1855,7 +1855,7 @@ public static void Spill_Optimizations_StaticReadOnlyField() e.VerifyIL(@" .method string ::lambda_method(class [System.Linq.Expressions]System.Runtime.CompilerServices.Closure) { - .maxstack 3 + .maxstack 2 .locals init ( [0] string, [1] string @@ -1906,7 +1906,7 @@ public static void Spill_Optimizations_RuntimeVariables1() e.VerifyIL(@" .method void ::lambda_method(class [System.Linq.Expressions]System.Runtime.CompilerServices.Closure,class [System.Private.CoreLib]System.Action`2) { - .maxstack 4 + .maxstack 3 .locals init ( [0] class [System.Private.CoreLib]System.Action`2, [1] int32, @@ -1967,7 +1967,7 @@ public static void Spill_Optimizations_RuntimeVariables2() e.VerifyIL(@" .method void ::lambda_method(class [System.Linq.Expressions]System.Runtime.CompilerServices.Closure,class [System.Private.CoreLib]System.Action`2,int32) { - .maxstack 10 + .maxstack 6 .locals init ( [0] object[], [1] class [System.Private.CoreLib]System.Action`2, @@ -2057,7 +2057,7 @@ public static void Spill_Optimizations_NoSpillBeyondSpillSite1() e.VerifyIL(@" .method int32 ::lambda_method(class [System.Linq.Expressions]System.Runtime.CompilerServices.Closure,class [System.Private.CoreLib]System.Func`4,int32,int32,int32) { - .maxstack 5 + .maxstack 4 .locals init ( [0] class [System.Private.CoreLib]System.Func`4, [1] int32, @@ -2135,7 +2135,7 @@ public static void Spill_Optimizations_NoSpillBeyondSpillSite2() e.VerifyIL(@" .method int32 ::lambda_method(class [System.Linq.Expressions]System.Runtime.CompilerServices.Closure,int32,int32,int32,int32) { - .maxstack 3 + .maxstack 2 .locals init ( [0] int32, [1] int32, diff --git a/src/libraries/System.Linq.Expressions/tests/Unary/UnaryArithmeticNegateCheckedNullableTests.cs b/src/libraries/System.Linq.Expressions/tests/Unary/UnaryArithmeticNegateCheckedNullableTests.cs index 9811cd7b632d3..f67ee3ab6f81a 100644 --- a/src/libraries/System.Linq.Expressions/tests/Unary/UnaryArithmeticNegateCheckedNullableTests.cs +++ b/src/libraries/System.Linq.Expressions/tests/Unary/UnaryArithmeticNegateCheckedNullableTests.cs @@ -212,7 +212,7 @@ public static void VerifyIL_NullableShortNegateChecked() f.VerifyIL( @".method valuetype [System.Private.CoreLib]System.Nullable`1 ::lambda_method(class [System.Linq.Expressions]System.Runtime.CompilerServices.Closure,valuetype [System.Private.CoreLib]System.Nullable`1) { - .maxstack 4 + .maxstack 3 .locals init ( [0] valuetype [System.Private.CoreLib]System.Nullable`1 ) From 8c22d3201507a082e4472ca8911ce5e11d4ab590 Mon Sep 17 00:00:00 2001 From: Patrick Nguyen Date: Wed, 8 Jun 2022 11:35:36 -0700 Subject: [PATCH 3/5] Suppress Emit5 tests in mono --- .../tests/ILGenerator/Emit5Tests.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/libraries/System.Reflection.Emit.ILGeneration/tests/ILGenerator/Emit5Tests.cs b/src/libraries/System.Reflection.Emit.ILGeneration/tests/ILGenerator/Emit5Tests.cs index a7bf79743c747..7a11fd4cb4c63 100644 --- a/src/libraries/System.Reflection.Emit.ILGeneration/tests/ILGenerator/Emit5Tests.cs +++ b/src/libraries/System.Reflection.Emit.ILGeneration/tests/ILGenerator/Emit5Tests.cs @@ -9,6 +9,7 @@ namespace System.Reflection.Emit.Tests public class ILGeneratorEmit5 { [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/63805", TestRuntimes.Mono)] public void MaxStackOverflowTest() { Run(1 << 5); @@ -76,6 +77,7 @@ static MethodInfo GetCode(int num) } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/63805", TestRuntimes.Mono)] public void MaxStackNonEmptyForward() { // This test uses forward branches to "new" basic blocks where the stack depth @@ -139,6 +141,7 @@ static MethodInfo GetCode(int num) } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/63805", TestRuntimes.Mono)] public void MaxStackNonEmptyBackward() { // This test uses backward branches to "new" basic blocks where the stack depth @@ -212,6 +215,7 @@ static MethodInfo GetCode(int num) } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/63805", TestRuntimes.Mono)] public void AmbiguousDepth() { var meth = GetCode(); @@ -258,6 +262,7 @@ static MethodInfo GetCode() } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/63805", TestRuntimes.Mono)] public void UnreachableDepth() { var meth = GetCode(); From 8d55541a398a10cc8e18050ac049af1c0c913f2b Mon Sep 17 00:00:00 2001 From: Patrick Nguyen Date: Tue, 21 Jun 2022 19:36:31 -0700 Subject: [PATCH 4/5] Remove stack handling in MethodBuilder --- .../src/System/Reflection/Emit/MethodBuilder.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/MethodBuilder.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/MethodBuilder.cs index 10da169b3e33f..a5f4fdd8ba7d5 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/MethodBuilder.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/MethodBuilder.cs @@ -309,7 +309,7 @@ internal int GetMaxStack() { if (m_ilGenerator != null) { - return m_ilGenerator.GetMaxStackSize() + ExceptionHandlerCount; + return m_ilGenerator.GetMaxStackSize(); } else { @@ -323,8 +323,6 @@ internal int GetMaxStack() return m_exceptions; } - internal int ExceptionHandlerCount => m_exceptions != null ? m_exceptions.Length : 0; - internal static int CalculateNumberOfExceptions(__ExceptionInfo[]? excp) { int num = 0; From 89cd1fa98f242ae52708979910a241a9429dae68 Mon Sep 17 00:00:00 2001 From: Patrick Nguyen Date: Wed, 22 Jun 2022 11:13:05 -0700 Subject: [PATCH 5/5] retrigger checks