2323
2424namespace ICSharpCode . Decompiler . IL . Transforms
2525{
26- /// <summary>
27- /// Block IL_0018 (incoming: *) {
28- /// stloc s(ldc.i4 1)
29- /// br IL_0019
30- /// }
31- ///
32- /// Block IL_0019 (incoming: > 1) {
33- /// if (logic.not(ldloc s)) br IL_0027
34- /// br IL_001d
35- /// }
36- ///
37- /// replace br IL_0019 with br IL_0027
38- /// </summary>
3926 class RemoveInfeasiblePathTransform : IILTransform
4027 {
4128 void IILTransform . Run ( ILFunction function , ILTransformContext context )
@@ -45,7 +32,8 @@ void IILTransform.Run(ILFunction function, ILTransformContext context)
4532 bool changed = false ;
4633 foreach ( var block in container . Blocks )
4734 {
48- changed |= DoTransform ( block , context ) ;
35+ changed |= RemoveInfeasiblePath ( block , context ) ;
36+ changed |= RemoveUnconstrainedGenericReferenceTypeCheck ( block , context ) ;
4937 }
5038
5139 if ( changed )
@@ -55,8 +43,20 @@ void IILTransform.Run(ILFunction function, ILTransformContext context)
5543 }
5644 }
5745
58-
59- private bool DoTransform ( Block block , ILTransformContext context )
46+ /// <summary>
47+ /// Block IL_0018 (incoming: *) {
48+ /// stloc s(ldc.i4 1)
49+ /// br IL_0019
50+ /// }
51+ ///
52+ /// Block IL_0019 (incoming: > 1) {
53+ /// if (logic.not(ldloc s)) br IL_0027
54+ /// br IL_001d
55+ /// }
56+ ///
57+ /// replace br IL_0019 with br IL_0027
58+ /// </summary>
59+ private bool RemoveInfeasiblePath ( Block block , ILTransformContext context )
6060 {
6161 if ( ! MatchBlock1 ( block , out var s , out int value , out var br ) )
6262 return false ;
@@ -112,5 +112,127 @@ bool MatchBlock2(Block block, ILVariable s, int constantValue, [NotNullWhen(true
112112 exitInst = constantValue != 0 ? trueInst : falseInst ;
113113 return exitInst is Branch or Leave { Value : Nop } ;
114114 }
115+
116+ /// <summary>
117+ /// Block entryPoint (incoming: _) {
118+ /// [...]
119+ /// stloc S_0(...)
120+ /// stobj ``0(ldloca V_0, default.value ``0)
121+ /// if (comp.o(box ``0(ldloc V_0) != ldnull)) br invocationBlock
122+ /// br dereferenceBlock
123+ /// }
124+ ///
125+ /// Block dereferenceBlock (incoming: 1) {
126+ /// stloc V_0(ldobj ``0(ldloc S_0))
127+ /// stloc S_0(ldloca V_0)
128+ /// br invocationBlock
129+ /// }
130+ ///
131+ /// Block invocationBlock (incoming: 2) {
132+ /// [...]
133+ /// ... (constrained[``0].callvirt Method(ldobj.if.ref ``0(ldloc S_0), ...))
134+ /// }
135+ /// </summary>
136+ private bool RemoveUnconstrainedGenericReferenceTypeCheck ( Block entryPoint , ILTransformContext context )
137+ {
138+ // if (comp.o(box ``0(ldloc temporary) != ldnull)) br invocationBlock
139+ // br dereferenceBlock
140+ if ( ! entryPoint . MatchIfAtEndOfBlock ( out var condition , out var invocationBlockBranch , out var dereferenceBlockBranch ) )
141+ {
142+ return false ;
143+ }
144+ if ( ! condition . MatchCompNotEqualsNull ( out var arg ) )
145+ {
146+ return false ;
147+ }
148+ if ( ! arg . MatchBox ( out arg , out var type ) )
149+ {
150+ return false ;
151+ }
152+ if ( ! arg . MatchLdLoc ( out var temp ) || temp . StoreCount != 2 || temp . AddressCount != 2 )
153+ {
154+ return false ;
155+ }
156+ // stobj ``0(ldloca V_0, default.value ``0)
157+ var store = entryPoint . Instructions . ElementAtOrDefault ( entryPoint . Instructions . Count - 3 ) ;
158+ if ( store == null || ! store . MatchStObj ( out var target , out var value , out var storeType ) )
159+ {
160+ return false ;
161+ }
162+ if ( ! target . MatchLdLoca ( temp ) || ! value . MatchDefaultValue ( out var defaultValueType ) )
163+ {
164+ return false ;
165+ }
166+ if ( ! defaultValueType . Equals ( storeType ) || ! storeType . Equals ( type ) )
167+ {
168+ return false ;
169+ }
170+ // stloc S_0(...)
171+ store = entryPoint . Instructions . ElementAtOrDefault ( entryPoint . Instructions . Count - 4 ) ;
172+ if ( store == null || ! store . MatchStLoc ( out var stackSlot , out var thisValue ) )
173+ {
174+ return false ;
175+ }
176+ // check dereferenceBlock
177+ if ( ! dereferenceBlockBranch . MatchBranch ( out var dereferenceBlock ) || ! invocationBlockBranch . MatchBranch ( out var invocationBlock ) )
178+ {
179+ return false ;
180+ }
181+ if ( invocationBlock . IncomingEdgeCount != 2 )
182+ {
183+ return false ;
184+ }
185+ if ( dereferenceBlock . IncomingEdgeCount != 1 )
186+ {
187+ return false ;
188+ }
189+
190+ // stloc V_0(ldobj ``0(ldloc S_0))
191+ // stloc S_0(ldloca V_0)
192+ // br invocationBlock
193+ if ( dereferenceBlock . Instructions is not [ StLoc deref , StLoc addressLoad , Branch br ] )
194+ {
195+ return false ;
196+ }
197+ if ( deref . Variable != temp || addressLoad . Variable != stackSlot )
198+ {
199+ return false ;
200+ }
201+ if ( ! deref . Value . MatchLdObj ( out var stackSlotTarget , out var loadType ) )
202+ {
203+ return false ;
204+ }
205+ if ( ! stackSlotTarget . MatchLdLoc ( stackSlot ) || ! loadType . Equals ( type ) )
206+ {
207+ return false ;
208+ }
209+ if ( ! addressLoad . Value . MatchLdLoca ( temp ) )
210+ {
211+ return false ;
212+ }
213+ if ( br ? . TargetBlock != invocationBlock )
214+ {
215+ return false ;
216+ }
217+ // ... (constrained[``0].callvirt Method(ldobj.if.ref ``0(ldloc S_0), ...))
218+ if ( stackSlot . StoreCount != 2 || stackSlot . LoadCount != 2 || stackSlot . AddressCount != 0 )
219+ {
220+ return false ;
221+ }
222+ var callTarget = stackSlot . LoadInstructions . SingleOrDefault ( l => stackSlotTarget != l ) ;
223+ if ( callTarget ? . Parent is not LdObjIfRef { Parent : CallVirt call } ldobjIfRef )
224+ {
225+ return false ;
226+ }
227+ if ( call . Arguments . Count == 0 || call . Arguments [ 0 ] != ldobjIfRef || ! storeType . Equals ( call . ConstrainedTo ) )
228+ {
229+ return false ;
230+ }
231+ context . Step ( "RemoveUnconstrainedGenericReferenceTypeCheck" , store ) ;
232+ ldobjIfRef . ImplicitDeference = true ;
233+ entryPoint . Instructions . RemoveRange ( entryPoint . Instructions . Count - 3 , 3 ) ;
234+ entryPoint . Instructions . Add ( invocationBlockBranch ) ;
235+ return true ;
236+ }
115237 }
116238}
0 commit comments