Browse Source

Variable splitting: support cases where a ref is passed through a ref-returning method, and then used.

From `IndexRangeTest.UseIndexForRef`:
```
	stloc V_4(call GetSpan())
	stloc S_19(ldloca V_4)
	stloc V_2(call get_Length(ldloc S_19))
	stloc V_3(call GetOffset(addressof System.Index(call GetIndex(ldc.i4 0)), ldloc V_2))
	call UseRef(call get_Item(ldloc S_19, ldloc V_3))
	stloc V_4(call GetSpan())
	stloc S_30(ldloca V_4)
	stloc V_2(binary.sub.i4(call get_Length(ldloc S_30), call GetInt(ldc.i4 0)))
	call UseRef(call get_Item(ldloc S_30, ldloc V_2))
```
Due to `Span.get_Item` being a ref-return, it's possible that `ref V_4` is returned and passed into the `UseRef` method (this can't actually happen given Span's implementation, but it's a possible implementation of the get_Item type signature).
But we still need to split `V_4` -- it's a compiler-generated variable and needs to be inlined.

I think we can do this relatively simply by continuing to go up the ancestor instructions when we hit a ref-returning call. The recursive `DetermineAddressUse` call will check that there are no stores to `V_4` between the `get_Item` call and the point where the returned reference is used. Thus we still ensure that we don't split a variable while there is a live reference to it.
pull/1986/head
Daniel Grunwald 5 years ago
parent
commit
060830dd64
  1. 15
      ICSharpCode.Decompiler/IL/Transforms/SplitVariables.cs

15
ICSharpCode.Decompiler/IL/Transforms/SplitVariables.cs

@ -114,7 +114,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -114,7 +114,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
value = ldFlda.Target;
}
if (value.OpCode != OpCode.LdLoca) {
// GroupStores.HandleLoad() only detects ref-locals when they are directly initialized with ldloca
// GroupStores only handles ref-locals correctly when they are supported by GetAddressLoadForRefLocalUse(),
// which only works for ldflda*(ldloca)
return AddressUse.Unknown;
}
foreach (var load in stloc.Variable.LoadInstructions) {
@ -132,14 +133,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -132,14 +133,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// Address is passed to method.
// We'll assume the method only uses the address locally,
// unless we can see an address being returned from the method:
if (call is NewObj) {
if (call.Method.DeclaringType.IsByRefLike) {
IType returnType = (call is NewObj) ? call.Method.DeclaringType : call.Method.ReturnType;
if (returnType.IsByRefLike) {
// If the address is returned from the method, it check whether it's consumed immediately.
// This can still be fine, as long as we also check the consumer's other arguments for 'stloc targetVar'.
if (DetermineAddressUse(call, targetVar) != AddressUse.Immediate)
return AddressUse.Unknown;
}
} else {
if (call.Method.ReturnType.IsByRefLike) {
return AddressUse.Unknown;
}
}
foreach (var p in call.Method.Parameters) {
// catch "out Span<int>" and similar

Loading…
Cancel
Save