Browse Source

Fix re-pinning getting confused about which variant of the pinned local to use when the new re-pinned reference depends on the old pinned reference.

pull/2113/head
Daniel Grunwald 5 years ago
parent
commit
f5409ebe31
  1. 16
      ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Unsafe.cs
  2. 47
      ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Unsafe.il
  3. 15
      ICSharpCode.Decompiler/IL/ControlFlow/DetectPinnedRegions.cs

16
ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Unsafe.cs

@ -50,6 +50,22 @@ internal sealed class ExtraUnsafeTests @@ -50,6 +50,22 @@ internal sealed class ExtraUnsafeTests
}
return arr;
}
public unsafe void pin_ptr_test(int[] a, int[] b)
{
//The blocks IL_0016 are reachable both inside and outside the pinned region starting at IL_0007. ILSpy has duplicated these blocks in order to place them both within and outside the `fixed` statement.
ref int reference;
fixed (int* ptr = &a[0]) {
if (*ptr <= 0) {
ptr[4 * 0] = 1;
return;
}
reference = ref *ptr;
}
fixed (int* ptr = &b[reference]) {
ptr[4 * 0] = 1;
}
}
}
namespace System.Runtime.CompilerServices

47
ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Unsafe.il

@ -546,4 +546,51 @@ lbl: @@ -546,4 +546,51 @@ lbl:
IL_0020: ldloc.0
IL_0021: ret
} // end of method Issue1292
.method /* 06000066 */ public hidebysig
instance void pin_ptr_test (
int32[] a,
int32[] b
) cil managed
{
/* From C++/CLI:
void pin_ptr_test(array<int>^ a, array<int>^ b)
{
pin_ptr<int> p = &a[0];
if (*p > 0)
{
p = &b[*p];
}
p[0] = 1;
}
*/
.maxstack 3
.locals /* 11000004 */ (
[0] int32& pinned modopt([mscorlib]System.Runtime.CompilerServices.IsExplicitlyDereferenced) p
)
IL_0000: ldarg.1
IL_0001: ldc.i4.0
IL_0002: ldelema [mscorlib]System.Int32 /* 01000016 */
IL_0007: stloc.0
IL_0008: ldloc.0
IL_0009: ldind.i4
IL_000a: ldc.i4.0
IL_000b: ble.s IL_0016
IL_000d: ldarg.2
IL_000e: ldloc.0
IL_000f: ldind.i4
IL_0010: ldelema [mscorlib]System.Int32 /* 01000016 */
IL_0015: stloc.0
IL_0016: ldloc.0
IL_0017: ldc.i4.4
IL_0018: ldc.i4.0
IL_0019: mul
IL_001a: add
IL_001b: ldc.i4.1
IL_001c: stind.i4
IL_001d: ret
} // end of method pin_ptr_test
}

15
ICSharpCode.Decompiler/IL/ControlFlow/DetectPinnedRegions.cs

@ -94,7 +94,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -94,7 +94,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
var block = container.Blocks[i];
for (int j = 0; j < block.Instructions.Count - 1; j++) {
var inst = block.Instructions[j];
if (inst.MatchStLoc(out ILVariable v) && v.Kind == VariableKind.PinnedLocal) {
if (inst.MatchStLoc(out ILVariable v, out var value) && v.Kind == VariableKind.PinnedLocal) {
if (block.Instructions[j + 1].OpCode != OpCode.Branch) {
// split block after j:
context.Step("Split block after pinned local write", inst);
@ -107,6 +107,19 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -107,6 +107,19 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
block.Instructions.Add(new Branch(newBlock));
container.Blocks.Insert(i + 1, newBlock);
}
// in case of re-pinning (e.g. C++/CLI assignment to pin_ptr variable),
// it's possible for the new value to be dependent on the old.
if (v.IsUsedWithin(value)) {
// In this case, we need to un-inline the uses of the pinned local
// so that they are split off into the block prior to the pinned local write
var temp = context.Function.RegisterVariable(VariableKind.StackSlot, v.Type);
block.Instructions.Insert(j++, new StLoc(temp, new LdLoc(v)));
foreach (var descendant in value.Descendants) {
if (descendant.MatchLdLoc(v)) {
descendant.ReplaceWith(new LdLoc(temp).WithILRange(descendant));
}
}
}
if (j > 0) {
// split block before j:
context.Step("Split block before pinned local write", inst);

Loading…
Cancel
Save