@@ -252,13 +252,17 @@ public MethodIL GetMethodILWithInlinedSubstitutions(MethodIL method)
252
252
if ( ( flags [ offset ] & OpcodeFlags . Mark ) != 0 )
253
253
continue ;
254
254
255
+ TypeEqualityPatternAnalyzer typeEqualityAnalyzer = default ;
256
+
255
257
ILReader reader = new ILReader ( methodBytes , offset ) ;
256
258
while ( reader . HasNext )
257
259
{
258
260
offset = reader . Offset ;
259
261
flags [ offset ] |= OpcodeFlags . Mark ;
260
262
ILOpcode opcode = reader . ReadILOpcode ( ) ;
261
263
264
+ typeEqualityAnalyzer . Advance ( opcode , reader , method ) ;
265
+
262
266
// Mark any applicable EH blocks
263
267
foreach ( ILExceptionRegion ehRegion in ehRegions )
264
268
{
@@ -297,7 +301,8 @@ public MethodIL GetMethodILWithInlinedSubstitutions(MethodIL method)
297
301
|| opcode == ILOpcode . brtrue || opcode == ILOpcode . brtrue_s )
298
302
{
299
303
int destination = reader . ReadBranchDestination ( opcode ) ;
300
- if ( ! TryGetConstantArgument ( method , methodBytes , flags , offset , 0 , out int constant ) )
304
+ if ( ! TryGetConstantArgument ( method , methodBytes , flags , offset , 0 , out int constant )
305
+ && ! TryExpandTypeEquality ( typeEqualityAnalyzer , method , out constant ) )
301
306
{
302
307
// Can't get the constant - both branches are live.
303
308
offsetsToVisit . Push ( destination ) ;
@@ -681,13 +686,6 @@ private bool TryGetConstantArgument(MethodIL methodIL, byte[] body, OpcodeFlags[
681
686
constant = ( int ) substitution . Value ;
682
687
return true ;
683
688
}
684
- else if ( method . IsIntrinsic && method . Name is "op_Inequality" or "op_Equality"
685
- && method . OwningType is MetadataType mdType
686
- && mdType . Name == "Type" && mdType . Namespace == "System" && mdType . Module == mdType . Context . SystemModule
687
- && TryExpandTypeEquality ( methodIL , body , flags , currentOffset , method . Name , out constant ) )
688
- {
689
- return true ;
690
- }
691
689
else if ( method . IsIntrinsic && method . Name is "get_IsValueType" or "get_IsEnum"
692
690
&& method . OwningType is MetadataType mdt
693
691
&& mdt . Name == "Type" && mdt . Namespace == "System" && mdt . Module == mdt . Context . SystemModule
@@ -873,175 +871,63 @@ private static bool TryExpandTypeIs(MethodIL methodIL, byte[] body, OpcodeFlags[
873
871
return true ;
874
872
}
875
873
876
- private bool TryExpandTypeEquality ( MethodIL methodIL , byte [ ] body , OpcodeFlags [ ] flags , int offset , string op , out int constant )
877
- {
878
- if ( TryExpandTypeEquality_TokenToken ( methodIL , body , flags , offset , out constant )
879
- || TryExpandTypeEquality_TokenOther ( methodIL , body , flags , offset , 1 , expectGetType : false , out constant )
880
- || TryExpandTypeEquality_TokenOther ( methodIL , body , flags , offset , 2 , expectGetType : false , out constant )
881
- || TryExpandTypeEquality_TokenOther ( methodIL , body , flags , offset , 3 , expectGetType : false , out constant )
882
- || TryExpandTypeEquality_TokenOther ( methodIL , body , flags , offset , 1 , expectGetType : true , out constant )
883
- || TryExpandTypeEquality_TokenOther ( methodIL , body , flags , offset , 2 , expectGetType : true , out constant )
884
- || TryExpandTypeEquality_TokenOther ( methodIL , body , flags , offset , 3 , expectGetType : true , out constant ) )
885
- {
886
- if ( op == "op_Inequality" )
887
- constant ^= 1 ;
888
-
889
- return true ;
890
- }
891
-
892
- return false ;
893
- }
894
-
895
- private static bool TryExpandTypeEquality_TokenToken ( MethodIL methodIL , byte [ ] body , OpcodeFlags [ ] flags , int offset , out int constant )
874
+ private bool TryExpandTypeEquality ( in TypeEqualityPatternAnalyzer analyzer , MethodIL methodIL , out int constant )
896
875
{
897
- // We expect to see a sequence:
898
- // ldtoken Foo
899
- // call GetTypeFromHandle
900
- // ldtoken Bar
901
- // call GetTypeFromHandle
902
- // -> offset points here
903
876
constant = 0 ;
904
- const int SequenceLength = 20 ;
905
- if ( offset < SequenceLength )
906
- return false ;
907
-
908
- if ( ( flags [ offset - SequenceLength ] & OpcodeFlags . InstructionStart ) == 0 )
877
+ if ( ! analyzer . IsTypeEqualityBranch )
909
878
return false ;
910
879
911
- ILReader reader = new ILReader ( body , offset - SequenceLength ) ;
912
-
913
- TypeDesc type1 = ReadLdToken ( ref reader , methodIL , flags ) ;
914
- if ( type1 == null )
915
- return false ;
916
-
917
- if ( ! ReadGetTypeFromHandle ( ref reader , methodIL , flags ) )
918
- return false ;
919
-
920
- TypeDesc type2 = ReadLdToken ( ref reader , methodIL , flags ) ;
921
- if ( type2 == null )
922
- return false ;
923
-
924
- if ( ! ReadGetTypeFromHandle ( ref reader , methodIL , flags ) )
925
- return false ;
926
-
927
- // No value in making this work for definitions
928
- if ( type1 . IsGenericDefinition || type2 . IsGenericDefinition )
929
- return false ;
930
-
931
- // Dataflow runs on top of uninstantiated IL and we can't answer some questions there.
932
- // Unfortunately this means dataflow will still see code that the rest of the system
933
- // might have optimized away. It should not be a problem in practice.
934
- if ( type1 . ContainsSignatureVariables ( ) || type2 . ContainsSignatureVariables ( ) )
935
- return false ;
936
-
937
- bool ? equality = TypeExtensions . CompareTypesForEquality ( type1 , type2 ) ;
938
- if ( ! equality . HasValue )
939
- return false ;
940
-
941
- constant = equality . Value ? 1 : 0 ;
942
-
943
- return true ;
944
- }
945
-
946
- private bool TryExpandTypeEquality_TokenOther ( MethodIL methodIL , byte [ ] body , OpcodeFlags [ ] flags , int offset , int ldInstructionSize , bool expectGetType , out int constant )
947
- {
948
- // We expect to see a sequence:
949
- // ldtoken Foo
950
- // call GetTypeFromHandle
951
- // ldloc.X/ldloc_s X/ldarg.X/ldarg_s X
952
- // [optional] call Object.GetType
953
- // -> offset points here
954
- //
955
- // The ldtoken part can potentially be in the second argument position
956
-
957
- constant = 0 ;
958
- int sequenceLength = 5 + 5 + ldInstructionSize + ( expectGetType ? 5 : 0 ) ;
959
- if ( offset < sequenceLength )
960
- return false ;
961
-
962
- if ( ( flags [ offset - sequenceLength ] & OpcodeFlags . InstructionStart ) == 0 )
963
- return false ;
964
-
965
- ILReader reader = new ILReader ( body , offset - sequenceLength ) ;
880
+ if ( analyzer . IsTwoTokens )
881
+ {
882
+ var type1 = ( TypeDesc ) methodIL . GetObject ( analyzer . Token1 ) ;
883
+ var type2 = ( TypeDesc ) methodIL . GetObject ( analyzer . Token2 ) ;
966
884
967
- TypeDesc knownType = null ;
885
+ // No value in making this work for definitions
886
+ if ( type1 . IsGenericDefinition || type2 . IsGenericDefinition )
887
+ return false ;
968
888
969
- // Is the ldtoken in the first position?
970
- if ( reader . PeekILOpcode ( ) == ILOpcode . ldtoken )
971
- {
972
- knownType = ReadLdToken ( ref reader , methodIL , flags ) ;
973
- if ( knownType == null )
889
+ // Dataflow runs on top of uninstantiated IL and we can't answer some questions there.
890
+ // Unfortunately this means dataflow will still see code that the rest of the system
891
+ // might have optimized away. It should not be a problem in practice.
892
+ if ( type1 . ContainsSignatureVariables ( ) || type2 . ContainsSignatureVariables ( ) )
974
893
return false ;
975
894
976
- if ( ! ReadGetTypeFromHandle ( ref reader , methodIL , flags ) )
895
+ bool ? equality = TypeExtensions . CompareTypesForEquality ( type1 , type2 ) ;
896
+ if ( ! equality . HasValue )
977
897
return false ;
978
- }
979
898
980
- ILOpcode opcode = reader . ReadILOpcode ( ) ;
981
- if ( ldInstructionSize == 1 && opcode is ( >= ILOpcode . ldloc_0 and <= ILOpcode . ldloc_3 ) or ( >= ILOpcode . ldarg_0 and <= ILOpcode . ldarg_3 ) )
982
- {
983
- // Nothing to read
984
- }
985
- else if ( ldInstructionSize == 2 && opcode is ILOpcode . ldloc_s or ILOpcode . ldarg_s )
986
- {
987
- reader . ReadILByte ( ) ;
988
- }
989
- else if ( ldInstructionSize == 3 && opcode is ILOpcode . ldloc or ILOpcode . ldarg )
990
- {
991
- reader . ReadILUInt16 ( ) ;
899
+ constant = equality . Value ? 1 : 0 ;
992
900
}
993
901
else
994
902
{
995
- return false ;
996
- }
997
-
998
- if ( ( flags [ reader . Offset ] & OpcodeFlags . BasicBlockStart ) != 0 )
999
- return false ;
903
+ var knownType = ( TypeDesc ) methodIL . GetObject ( analyzer . Token1 ) ;
1000
904
1001
- if ( expectGetType )
1002
- {
1003
- if ( reader . ReadILOpcode ( ) is not ILOpcode . callvirt and not ILOpcode . call )
905
+ // No value in making this work for definitions
906
+ if ( knownType . IsGenericDefinition )
1004
907
return false ;
1005
908
1006
- // We don't actually mind if this is not Object.GetType
1007
- reader . ReadILToken ( ) ;
1008
-
1009
- if ( ( flags [ reader . Offset ] & OpcodeFlags . BasicBlockStart ) != 0 )
909
+ // Dataflow runs on top of uninstantiated IL and we can't answer some questions there.
910
+ // Unfortunately this means dataflow will still see code that the rest of the system
911
+ // might have optimized away. It should not be a problem in practice.
912
+ if ( knownType . ContainsSignatureVariables ( ) )
1010
913
return false ;
1011
- }
1012
914
1013
- // If the ldtoken wasn't in the first position, it must be in the other
1014
- if ( knownType == null )
1015
- {
1016
- knownType = ReadLdToken ( ref reader , methodIL , flags ) ;
1017
- if ( knownType == null )
915
+ if ( knownType . IsCanonicalDefinitionType ( CanonicalFormKind . Any ) )
1018
916
return false ;
1019
917
1020
- if ( ! ReadGetTypeFromHandle ( ref reader , methodIL , flags ) )
918
+ // We don't track types without a constructed MethodTable very well.
919
+ if ( ! ConstructedEETypeNode . CreationAllowed ( knownType ) )
1021
920
return false ;
1022
- }
1023
921
1024
- // No value in making this work for definitions
1025
- if ( knownType . IsGenericDefinition )
1026
- return false ;
1027
-
1028
- // Dataflow runs on top of uninstantiated IL and we can't answer some questions there.
1029
- // Unfortunately this means dataflow will still see code that the rest of the system
1030
- // might have optimized away. It should not be a problem in practice.
1031
- if ( knownType . ContainsSignatureVariables ( ) )
1032
- return false ;
1033
-
1034
- if ( knownType . IsCanonicalDefinitionType ( CanonicalFormKind . Any ) )
1035
- return false ;
922
+ if ( _devirtualizationManager . CanReferenceConstructedTypeOrCanonicalFormOfType ( knownType . NormalizeInstantiation ( ) ) )
923
+ return false ;
1036
924
1037
- // We don't track types without a constructed MethodTable very well.
1038
- if ( ! ConstructedEETypeNode . CreationAllowed ( knownType ) )
1039
- return false ;
925
+ constant = 0 ;
926
+ }
1040
927
1041
- if ( _devirtualizationManager . CanReferenceConstructedTypeOrCanonicalFormOfType ( knownType . NormalizeInstantiation ( ) ) )
1042
- return false ;
928
+ if ( analyzer . IsInequality )
929
+ constant ^= 1 ;
1043
930
1044
- constant = 0 ;
1045
931
return true ;
1046
932
}
1047
933
0 commit comments