Skip to content

Commit 83385d0

Browse files
buyaa-nAaronRobinsonMSFT
authored andcommitted
Add initial MaxStack calculation (dotnet#93244)
* Add initial MaxStack calculation and tests * Apply suggestions from code review Co-authored-by: Aaron Robinson <[email protected]> * Make OpCode.StackChange() public and use the method * Apply the approved API shape * Add doc, update the ref API ordering * Add more doc info, add test * Update doc remarks --------- Co-authored-by: Aaron Robinson <[email protected]>
1 parent 44517cf commit 83385d0

File tree

6 files changed

+360
-273
lines changed

6 files changed

+360
-273
lines changed

src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeILGenerator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ internal void InternalEmit(OpCode opcode)
128128
m_ILStream[m_length++] = (byte)opcodeValue;
129129
}
130130

131-
UpdateStackSize(opcode, opcode.StackChange());
131+
UpdateStackSize(opcode, opcode.EvaluationStackDelta);
132132
}
133133

134134
[MethodImpl(MethodImplOptions.AggressiveInlining)]

src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/Opcode.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,18 @@ internal OpCode(OpCodeValues value, int flags)
4545
internal bool EndsUncondJmpBlk() =>
4646
(m_flags & EndsUncondJmpBlkFlag) != 0;
4747

48-
internal int StackChange() =>
48+
/// <summary>
49+
/// The value of how the IL instruction changes the evaluation stack.
50+
/// </summary>
51+
/// <remarks>
52+
/// The difference between how many elements are popped from the stack and how many are pushed onto the stack as a result of the IL instruction.
53+
/// For some IL instructions like <see cref="OpCodes.Call"/> stack change is not fixed and depends on the called reference signature.
54+
/// For such <see cref="OpCodes"/> the <see cref="OpCode.EvaluationStackDelta"/> returns 0. In this case you should not rely on
55+
/// <see cref="OpCode.EvaluationStackDelta"/> for calculating stack size and/or max stack, instead need to evaluate the reference signature.
56+
/// For example, in case the instruction is calling a method reference, need to evaluate the method signature,
57+
/// the push count depends on the returning value, the pop count depends on how many parameters passed.
58+
/// </remarks>
59+
public int EvaluationStackDelta =>
4960
m_flags >> StackChangeShift;
5061

5162
public OperandType OperandType => (OperandType)(m_flags & OperandTypeMask);

src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ILGeneratorImpl.cs

Lines changed: 42 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4-
using System.Buffers.Binary;
54
using System.Reflection.Metadata;
65
using System.Reflection.Metadata.Ecma335;
7-
using System.Runtime.CompilerServices;
86
using System.Runtime.InteropServices;
97

108
namespace System.Reflection.Emit
@@ -17,6 +15,7 @@ internal sealed class ILGeneratorImpl : ILGenerator
1715
private readonly InstructionEncoder _il;
1816
private bool _hasDynamicStackAllocation;
1917
private int _maxStackSize;
18+
private int _currentStack;
2019

2120
internal ILGeneratorImpl(MethodBuilder methodBuilder, int size)
2221
{
@@ -42,40 +41,46 @@ internal ILGeneratorImpl(MethodBuilder methodBuilder, int size)
4241
public override LocalBuilder DeclareLocal(Type localType, bool pinned) => throw new NotImplementedException();
4342
public override Label DefineLabel() => throw new NotImplementedException();
4443

45-
public override void Emit(OpCode opcode)
44+
private void UpdateStackSize(OpCode opCode)
45+
{
46+
_currentStack += opCode.EvaluationStackDelta;
47+
_maxStackSize = Math.Max(_maxStackSize, _currentStack);
48+
}
49+
50+
public void EmitOpcode(OpCode opcode)
4651
{
4752
if (opcode == OpCodes.Localloc)
4853
{
4954
_hasDynamicStackAllocation = true;
5055
}
51-
_il.OpCode((ILOpCode)opcode.Value);
5256

53-
// TODO: for now only count the Opcodes emitted, in order to calculate it correctly we might need to make internal Opcode APIs public
54-
// https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/Opcode.cs#L48
55-
_maxStackSize++;
57+
_il.OpCode((ILOpCode)opcode.Value);
58+
UpdateStackSize(opcode);
5659
}
5760

61+
public override void Emit(OpCode opcode) => EmitOpcode(opcode);
62+
5863
public override void Emit(OpCode opcode, byte arg)
5964
{
60-
_il.OpCode((ILOpCode)opcode.Value);
65+
EmitOpcode(opcode);
6166
_builder.WriteByte(arg);
6267
}
6368

6469
public override void Emit(OpCode opcode, double arg)
6570
{
66-
_il.OpCode((ILOpCode)opcode.Value);
71+
EmitOpcode(opcode);
6772
_builder.WriteDouble(arg);
6873
}
6974

7075
public override void Emit(OpCode opcode, float arg)
7176
{
72-
_il.OpCode((ILOpCode)opcode.Value);
77+
EmitOpcode(opcode);
7378
_builder.WriteSingle(arg);
7479
}
7580

7681
public override void Emit(OpCode opcode, short arg)
7782
{
78-
_il.OpCode((ILOpCode)opcode.Value);
83+
EmitOpcode(opcode);
7984
_builder.WriteInt16(arg);
8085
}
8186

@@ -86,98 +91,91 @@ public override void Emit(OpCode opcode, int arg)
8691
{
8792
if (arg >= -1 && arg <= 8)
8893
{
89-
_il.OpCode(arg switch
94+
EmitOpcode(arg switch
9095
{
91-
-1 => ILOpCode.Ldc_i4_m1,
92-
0 => ILOpCode.Ldc_i4_0,
93-
1 => ILOpCode.Ldc_i4_1,
94-
2 => ILOpCode.Ldc_i4_2,
95-
3 => ILOpCode.Ldc_i4_3,
96-
4 => ILOpCode.Ldc_i4_4,
97-
5 => ILOpCode.Ldc_i4_5,
98-
6 => ILOpCode.Ldc_i4_6,
99-
7 => ILOpCode.Ldc_i4_7,
100-
_ => ILOpCode.Ldc_i4_8,
96+
-1 => OpCodes.Ldc_I4_M1,
97+
0 => OpCodes.Ldc_I4_0,
98+
1 => OpCodes.Ldc_I4_1,
99+
2 => OpCodes.Ldc_I4_2,
100+
3 => OpCodes.Ldc_I4_3,
101+
4 => OpCodes.Ldc_I4_4,
102+
5 => OpCodes.Ldc_I4_5,
103+
6 => OpCodes.Ldc_I4_6,
104+
7 => OpCodes.Ldc_I4_7,
105+
_ => OpCodes.Ldc_I4_8
101106
});
102107
return;
103108
}
104109

105110
if (arg >= -128 && arg <= 127)
106111
{
107-
_il.OpCode(ILOpCode.Ldc_i4_s);
108-
_builder.WriteSByte((sbyte)arg) ;
112+
Emit(OpCodes.Ldc_I4_S, (sbyte)arg);
109113
return;
110114
}
111115
}
112116
else if (opcode.Equals(OpCodes.Ldarg))
113117
{
114118
if ((uint)arg <= 3)
115119
{
116-
_il.OpCode(arg switch
120+
EmitOpcode(arg switch
117121
{
118-
0 => ILOpCode.Ldarg_0,
119-
1 => ILOpCode.Ldarg_1,
120-
2 => ILOpCode.Ldarg_2,
121-
_ => ILOpCode.Ldarg_3,
122+
0 => OpCodes.Ldarg_0,
123+
1 => OpCodes.Ldarg_1,
124+
2 => OpCodes.Ldarg_2,
125+
_ => OpCodes.Ldarg_3,
122126
});
123127
return;
124128
}
125129

126130
if ((uint)arg <= byte.MaxValue)
127131
{
128-
_il.OpCode(ILOpCode.Ldarg_s);
129-
_builder.WriteByte((byte)arg);
132+
Emit(OpCodes.Ldarg_S, (byte)arg);
130133
return;
131134
}
132135

133136
if ((uint)arg <= ushort.MaxValue) // this will be true except on misuse of the opcode
134137
{
135-
_il.OpCode(ILOpCode.Ldarg);
136-
_builder.WriteInt16((short)arg);
138+
Emit(OpCodes.Ldarg, (short)arg);
137139
return;
138140
}
139141
}
140142
else if (opcode.Equals(OpCodes.Ldarga))
141143
{
142144
if ((uint)arg <= byte.MaxValue)
143145
{
144-
_il.OpCode(ILOpCode.Ldarga_s);
145-
_builder.WriteByte((byte)arg);
146+
Emit(OpCodes.Ldarga_S, (byte)arg);
146147
return;
147148
}
148149

149150
if ((uint)arg <= ushort.MaxValue) // this will be true except on misuse of the opcode
150151
{
151-
_il.OpCode(ILOpCode.Ldarga);
152-
_builder.WriteInt16((short)arg);
152+
Emit(OpCodes.Ldarga, (short)arg);
153153
return;
154154
}
155155
}
156156
else if (opcode.Equals(OpCodes.Starg))
157157
{
158158
if ((uint)arg <= byte.MaxValue)
159159
{
160-
_il.OpCode(ILOpCode.Starg_s);
161-
_builder.WriteByte((byte)arg);
160+
Emit(OpCodes.Starg_S, (byte)arg);
162161
return;
163162
}
164163

165164
if ((uint)arg <= ushort.MaxValue) // this will be true except on misuse of the opcode
166165
{
167-
_il.OpCode(ILOpCode.Starg);
168-
_builder.WriteInt16((short)arg);
166+
Emit(OpCodes.Starg, (short)arg);
169167
return;
170168
}
171169
}
172170

173171
// For everything else, put the opcode followed by the arg onto the stream of instructions.
174-
_il.OpCode((ILOpCode)opcode.Value);
172+
EmitOpcode(opcode);
175173
_builder.WriteInt32(arg);
176174
}
177175

178176
public override void Emit(OpCode opcode, long arg)
179177
{
180-
_il.OpCode((ILOpCode)opcode.Value);
178+
EmitOpcode(opcode);
181179
_il.CodeBuilder.WriteInt64(arg);
182180
}
183181

@@ -187,7 +185,7 @@ public override void Emit(OpCode opcode, string str)
187185
// represented by str.
188186
ModuleBuilder modBuilder = (ModuleBuilder)_methodBuilder.Module;
189187
int tempVal = modBuilder.GetStringMetadataToken(str);
190-
_il.OpCode((ILOpCode)opcode.Value);
188+
EmitOpcode(opcode);
191189
_il.Token(tempVal);
192190
}
193191

0 commit comments

Comments
 (0)