From cfaaf7738f4c6dc686dbc2599f480cb56c4285a5 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Sat, 25 Apr 2020 09:45:24 -0400 Subject: [PATCH] Emit shorter variants in ILGenerator.Emit(OpCode, int) (#35427) The Ldloc, Stloc, Ldloca, Ldc_I4, Ldarg, Ldarga, and Starg opcodes all have shorter variants that take up less space in the IL instruction stream. ILGenerator.Emit(OpCode, LocalBuilder) already special cases Ldloc, Stloc, and Ldloca to automatically translate those into their shorter forms where applicable, but similar logic doesn't exist in Emit(OpCode, int) for Ldc_I4, Ldarg, Ldarga, and Starg. Instead, various other libraries higher in the stack that use reflection emit either end up doing all the special-casing with their own helper routines to do the shrinking, or they just forego it and end up with larger IL than is necessary. This PR just moves the logic down into Emit(OpCode, int) such that all uses can benefit, and removes the special-casing duplication from the other libraries. --- .../src/System/Reflection/Emit/ILGenerator.cs | 85 +++++++++++++++++++- .../RuntimeBinder/ComInterop/ComRuntimeHelpers.cs | 27 +------ .../src/System/Linq/Expressions/Compiler/ILGen.cs | 93 +--------------------- .../System/Runtime/Serialization/CodeGenerator.cs | 72 ++--------------- .../src/System/Xml/Serialization/CodeGenerator.cs | 64 +-------------- .../src/System/Xml/Xsl/IlGen/GenerateHelper.cs | 56 ++----------- .../Text/RegularExpressions/RegexCompiler.cs | 28 +------ 7 files changed, 108 insertions(+), 317 deletions(-) diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Reflection/Emit/ILGenerator.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Reflection/Emit/ILGenerator.cs index 93c517f..c8f6feb 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Reflection/Emit/ILGenerator.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Reflection/Emit/ILGenerator.cs @@ -396,7 +396,90 @@ namespace System.Reflection.Emit public virtual void Emit(OpCode opcode, int arg) { - // Puts opcode onto the stream of instructions followed by arg + // Special-case several opcodes that have shorter variants for common values. + if (opcode.Equals(OpCodes.Ldc_I4)) + { + if (arg >= -1 && arg <= 8) + { + opcode = arg switch + { + -1 => OpCodes.Ldc_I4_M1, + 0 => OpCodes.Ldc_I4_0, + 1 => OpCodes.Ldc_I4_1, + 2 => OpCodes.Ldc_I4_2, + 3 => OpCodes.Ldc_I4_3, + 4 => OpCodes.Ldc_I4_4, + 5 => OpCodes.Ldc_I4_5, + 6 => OpCodes.Ldc_I4_6, + 7 => OpCodes.Ldc_I4_7, + _ => OpCodes.Ldc_I4_8, + }; + Emit(opcode); + return; + } + + if (arg >= -128 && arg <= 127) + { + Emit(OpCodes.Ldc_I4_S, (sbyte)arg); + return; + } + } + else if (opcode.Equals(OpCodes.Ldarg)) + { + if ((uint)arg <= 3) + { + Emit(arg switch + { + 0 => OpCodes.Ldarg_0, + 1 => OpCodes.Ldarg_1, + 2 => OpCodes.Ldarg_2, + _ => OpCodes.Ldarg_3, + }); + return; + } + + if ((uint)arg <= byte.MaxValue) + { + Emit(OpCodes.Ldarg_S, (byte)arg); + return; + } + + if ((uint)arg <= ushort.MaxValue) // this will be true except on misuse of the opcode + { + Emit(OpCodes.Ldarg, (short)arg); + return; + } + } + else if (opcode.Equals(OpCodes.Ldarga)) + { + if ((uint)arg <= byte.MaxValue) + { + Emit(OpCodes.Ldarga_S, (byte)arg); + return; + } + + if ((uint)arg <= ushort.MaxValue) // this will be true except on misuse of the opcode + { + Emit(OpCodes.Ldarga, (short)arg); + return; + } + } + else if (opcode.Equals(OpCodes.Starg)) + { + if ((uint)arg <= byte.MaxValue) + { + Emit(OpCodes.Starg_S, (byte)arg); + return; + } + + if ((uint)arg <= ushort.MaxValue) // this will be true except on misuse of the opcode + { + Emit(OpCodes.Starg, (short)arg); + return; + } + } + + // For everything else, put the opcode followed by the arg onto the stream of instructions. EnsureCapacity(7); InternalEmit(opcode); PutInteger4(arg); diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComRuntimeHelpers.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComRuntimeHelpers.cs index 3c32a0e..9e9ade6 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComRuntimeHelpers.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComRuntimeHelpers.cs @@ -342,32 +342,7 @@ namespace Microsoft.CSharp.RuntimeBinder.ComInterop private static void EmitLoadArg(ILGenerator il, int index) { Requires.Condition(index >= 0, nameof(index)); - - switch (index) - { - case 0: - il.Emit(OpCodes.Ldarg_0); - break; - case 1: - il.Emit(OpCodes.Ldarg_1); - break; - case 2: - il.Emit(OpCodes.Ldarg_2); - break; - case 3: - il.Emit(OpCodes.Ldarg_3); - break; - default: - if (index <= byte.MaxValue) - { - il.Emit(OpCodes.Ldarg_S, (byte)index); - } - else - { - il.Emit(OpCodes.Ldarg, index); - } - break; - } + il.Emit(OpCodes.Ldarg, index); } private static readonly object s_lock = new object(); diff --git a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/ILGen.cs b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/ILGen.cs index a87dccf..880f85b 100644 --- a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/ILGen.cs +++ b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/ILGen.cs @@ -34,32 +34,7 @@ namespace System.Linq.Expressions.Compiler Debug.Assert(index >= 0); Debug.Assert(index < ushort.MaxValue); - switch (index) - { - case 0: - il.Emit(OpCodes.Ldarg_0); - break; - case 1: - il.Emit(OpCodes.Ldarg_1); - break; - case 2: - il.Emit(OpCodes.Ldarg_2); - break; - case 3: - il.Emit(OpCodes.Ldarg_3); - break; - default: - if (index <= byte.MaxValue) - { - il.Emit(OpCodes.Ldarg_S, (byte)index); - } - else - { - // cast to short, result is correct ushort. - il.Emit(OpCodes.Ldarg, (short)index); - } - break; - } + il.Emit(OpCodes.Ldarg, index); } internal static void EmitLoadArgAddress(this ILGenerator il, int index) @@ -67,15 +42,7 @@ namespace System.Linq.Expressions.Compiler Debug.Assert(index >= 0); Debug.Assert(index < ushort.MaxValue); - if (index <= byte.MaxValue) - { - il.Emit(OpCodes.Ldarga_S, (byte)index); - } - else - { - // cast to short, result is correct ushort. - il.Emit(OpCodes.Ldarga, (short)index); - } + il.Emit(OpCodes.Ldarga, index); } internal static void EmitStoreArg(this ILGenerator il, int index) @@ -83,15 +50,7 @@ namespace System.Linq.Expressions.Compiler Debug.Assert(index >= 0); Debug.Assert(index < ushort.MaxValue); - if (index <= byte.MaxValue) - { - il.Emit(OpCodes.Starg_S, (byte)index); - } - else - { - // cast to short, result is correct ushort. - il.Emit(OpCodes.Starg, (short)index); - } + il.Emit(OpCodes.Starg, index); } /// @@ -354,51 +313,7 @@ namespace System.Linq.Expressions.Compiler internal static void EmitPrimitive(this ILGenerator il, int value) { - OpCode c; - switch (value) - { - case -1: - c = OpCodes.Ldc_I4_M1; - break; - case 0: - c = OpCodes.Ldc_I4_0; - break; - case 1: - c = OpCodes.Ldc_I4_1; - break; - case 2: - c = OpCodes.Ldc_I4_2; - break; - case 3: - c = OpCodes.Ldc_I4_3; - break; - case 4: - c = OpCodes.Ldc_I4_4; - break; - case 5: - c = OpCodes.Ldc_I4_5; - break; - case 6: - c = OpCodes.Ldc_I4_6; - break; - case 7: - c = OpCodes.Ldc_I4_7; - break; - case 8: - c = OpCodes.Ldc_I4_8; - break; - default: - if (value >= sbyte.MinValue && value <= sbyte.MaxValue) - { - il.Emit(OpCodes.Ldc_I4_S, (sbyte)value); - } - else - { - il.Emit(OpCodes.Ldc_I4, value); - } - return; - } - il.Emit(c); + il.Emit(OpCodes.Ldc_I4, value); } private static void EmitPrimitive(this ILGenerator il, uint value) diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/CodeGenerator.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/CodeGenerator.cs index 93a1841..5d94795 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/CodeGenerator.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/CodeGenerator.cs @@ -982,42 +982,7 @@ namespace System.Runtime.Serialization { if (_codeGenTrace != CodeGenTrace.None) EmitSourceInstruction("Ldc.i4 " + intVar); - switch (intVar) - { - case -1: - _ilGen.Emit(OpCodes.Ldc_I4_M1); - break; - case 0: - _ilGen.Emit(OpCodes.Ldc_I4_0); - break; - case 1: - _ilGen.Emit(OpCodes.Ldc_I4_1); - break; - case 2: - _ilGen.Emit(OpCodes.Ldc_I4_2); - break; - case 3: - _ilGen.Emit(OpCodes.Ldc_I4_3); - break; - case 4: - _ilGen.Emit(OpCodes.Ldc_I4_4); - break; - case 5: - _ilGen.Emit(OpCodes.Ldc_I4_5); - break; - case 6: - _ilGen.Emit(OpCodes.Ldc_I4_6); - break; - case 7: - _ilGen.Emit(OpCodes.Ldc_I4_7); - break; - case 8: - _ilGen.Emit(OpCodes.Ldc_I4_8); - break; - default: - _ilGen.Emit(OpCodes.Ldc_I4, intVar); - break; - } + _ilGen.Emit(OpCodes.Ldc_I4, intVar); } internal void Ldc(long l) @@ -1103,37 +1068,16 @@ namespace System.Runtime.Serialization { if (_codeGenTrace != CodeGenTrace.None) EmitSourceInstruction("Ldarg " + slot); - switch (slot) - { - case 0: - _ilGen.Emit(OpCodes.Ldarg_0); - break; - case 1: - _ilGen.Emit(OpCodes.Ldarg_1); - break; - case 2: - _ilGen.Emit(OpCodes.Ldarg_2); - break; - case 3: - _ilGen.Emit(OpCodes.Ldarg_3); - break; - default: - if (slot <= 255) - _ilGen.Emit(OpCodes.Ldarg_S, slot); - else - _ilGen.Emit(OpCodes.Ldarg, slot); - break; - } + + _ilGen.Emit(OpCodes.Ldarg, slot); } internal void Starg(int slot) { if (_codeGenTrace != CodeGenTrace.None) EmitSourceInstruction("Starg " + slot); - if (slot <= 255) - _ilGen.Emit(OpCodes.Starg_S, slot); - else - _ilGen.Emit(OpCodes.Starg, slot); + + _ilGen.Emit(OpCodes.Starg, slot); } internal void Ldarga(ArgBuilder argBuilder) @@ -1145,10 +1089,8 @@ namespace System.Runtime.Serialization { if (_codeGenTrace != CodeGenTrace.None) EmitSourceInstruction("Ldarga " + slot); - if (slot <= 255) - _ilGen.Emit(OpCodes.Ldarga_S, slot); - else - _ilGen.Emit(OpCodes.Ldarga, slot); + + _ilGen.Emit(OpCodes.Ldarga, slot); } internal void Ldlen() diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/CodeGenerator.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/CodeGenerator.cs index f6f3bdd..134812f 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/CodeGenerator.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/CodeGenerator.cs @@ -870,42 +870,7 @@ namespace System.Xml.Serialization internal void Ldc(int intVar) { - switch (intVar) - { - case -1: - _ilGen.Emit(OpCodes.Ldc_I4_M1); - break; - case 0: - _ilGen.Emit(OpCodes.Ldc_I4_0); - break; - case 1: - _ilGen.Emit(OpCodes.Ldc_I4_1); - break; - case 2: - _ilGen.Emit(OpCodes.Ldc_I4_2); - break; - case 3: - _ilGen.Emit(OpCodes.Ldc_I4_3); - break; - case 4: - _ilGen.Emit(OpCodes.Ldc_I4_4); - break; - case 5: - _ilGen.Emit(OpCodes.Ldc_I4_5); - break; - case 6: - _ilGen.Emit(OpCodes.Ldc_I4_6); - break; - case 7: - _ilGen.Emit(OpCodes.Ldc_I4_7); - break; - case 8: - _ilGen.Emit(OpCodes.Ldc_I4_8); - break; - default: - _ilGen.Emit(OpCodes.Ldc_I4, intVar); - break; - } + _ilGen.Emit(OpCodes.Ldc_I4, intVar); } internal void Ldc(long l) @@ -1000,27 +965,7 @@ namespace System.Xml.Serialization internal void Ldarg(int slot) { - switch (slot) - { - case 0: - _ilGen.Emit(OpCodes.Ldarg_0); - break; - case 1: - _ilGen.Emit(OpCodes.Ldarg_1); - break; - case 2: - _ilGen.Emit(OpCodes.Ldarg_2); - break; - case 3: - _ilGen.Emit(OpCodes.Ldarg_3); - break; - default: - if (slot <= 255) - _ilGen.Emit(OpCodes.Ldarg_S, slot); - else - _ilGen.Emit(OpCodes.Ldarg, slot); - break; - } + _ilGen.Emit(OpCodes.Ldarg, slot); } internal void Ldarga(ArgBuilder argBuilder) @@ -1030,10 +975,7 @@ namespace System.Xml.Serialization internal void Ldarga(int slot) { - if (slot <= 255) - _ilGen.Emit(OpCodes.Ldarga_S, slot); - else - _ilGen.Emit(OpCodes.Ldarga, slot); + _ilGen.Emit(OpCodes.Ldarga, slot); } internal void Ldlen() diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/GenerateHelper.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/GenerateHelper.cs index f8469b8..ab9890e 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/GenerateHelper.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/GenerateHelper.cs @@ -609,30 +609,7 @@ namespace System.Xml.Xsl.IlGen /// public void LoadInteger(int intVal) { - OpCode opcode; - - if (intVal >= -1 && intVal < 9) - { - switch (intVal) - { - case -1: opcode = OpCodes.Ldc_I4_M1; break; - case 0: opcode = OpCodes.Ldc_I4_0; break; - case 1: opcode = OpCodes.Ldc_I4_1; break; - case 2: opcode = OpCodes.Ldc_I4_2; break; - case 3: opcode = OpCodes.Ldc_I4_3; break; - case 4: opcode = OpCodes.Ldc_I4_4; break; - case 5: opcode = OpCodes.Ldc_I4_5; break; - case 6: opcode = OpCodes.Ldc_I4_6; break; - case 7: opcode = OpCodes.Ldc_I4_7; break; - case 8: opcode = OpCodes.Ldc_I4_8; break; - default: Debug.Fail($"Unexpected int val {intVal}"); return; - } - Emit(opcode); - } - else if (intVal >= -128 && intVal <= 127) - Emit(OpCodes.Ldc_I4_S, (sbyte)intVal); - else - Emit(OpCodes.Ldc_I4, intVal); + Emit(OpCodes.Ldc_I4, intVal); } public void LoadBoolean(bool boolVal) @@ -697,26 +674,13 @@ namespace System.Xml.Xsl.IlGen public void LoadParameter(int paramPos) { - switch (paramPos) + if (paramPos <= ushort.MaxValue) { - case 0: Emit(OpCodes.Ldarg_0); break; - case 1: Emit(OpCodes.Ldarg_1); break; - case 2: Emit(OpCodes.Ldarg_2); break; - case 3: Emit(OpCodes.Ldarg_3); break; - default: - if (paramPos <= 255) - { - Emit(OpCodes.Ldarg_S, (byte)paramPos); - } - else if (paramPos <= ushort.MaxValue) - { - Emit(OpCodes.Ldarg, paramPos); - } - else - { - throw new XslTransformException(SR.XmlIl_TooManyParameters); - } - break; + Emit(OpCodes.Ldarg, paramPos); + } + else + { + throw new XslTransformException(SR.XmlIl_TooManyParameters); } } @@ -724,11 +688,7 @@ namespace System.Xml.Xsl.IlGen { int paramPos = (int)paramId; - if (paramPos <= 255) - { - Emit(OpCodes.Starg_S, (byte)paramPos); - } - else if (paramPos <= ushort.MaxValue) + if (paramPos <= ushort.MaxValue) { Emit(OpCodes.Starg, (int)paramPos); } diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs index 13a28e1..685a951 100644 --- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs +++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs @@ -289,33 +289,7 @@ namespace System.Text.RegularExpressions protected void Ldstr(string str) => _ilg!.Emit(OpCodes.Ldstr, str); /// A macro for the various forms of Ldc. - protected void Ldc(int i) - { - Debug.Assert(_ilg != null); - - if ((uint)i < 8) - { - _ilg.Emit(i switch - { - 0 => OpCodes.Ldc_I4_0, - 1 => OpCodes.Ldc_I4_1, - 2 => OpCodes.Ldc_I4_2, - 3 => OpCodes.Ldc_I4_3, - 4 => OpCodes.Ldc_I4_4, - 5 => OpCodes.Ldc_I4_5, - 6 => OpCodes.Ldc_I4_6, - _ => OpCodes.Ldc_I4_7, - }); - } - else if (i <= 127 && i >= -128) - { - _ilg.Emit(OpCodes.Ldc_I4_S, (byte)i); - } - else - { - _ilg.Emit(OpCodes.Ldc_I4, i); - } - } + protected void Ldc(int i) => _ilg!.Emit(OpCodes.Ldc_I4, i); /// A macro for _ilg.Emit(OpCodes.Ldc_I8). protected void LdcI8(long i) => _ilg!.Emit(OpCodes.Ldc_I8, i); -- 2.7.4