From f5409ebe313d8fa05b38629e2065b94021d38bfc Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Mon, 10 Aug 2020 21:34:35 +0200 Subject: [PATCH] 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. --- .../TestCases/ILPretty/Unsafe.cs | 16 +++++++ .../TestCases/ILPretty/Unsafe.il | 47 +++++++++++++++++++ .../IL/ControlFlow/DetectPinnedRegions.cs | 15 +++++- 3 files changed, 77 insertions(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Unsafe.cs b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Unsafe.cs index be41f47b0..fd288864c 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Unsafe.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Unsafe.cs @@ -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 diff --git a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Unsafe.il b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Unsafe.il index 7766f0c80..8e89b8adf 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Unsafe.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Unsafe.il @@ -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^ a, array^ b) + { + pin_ptr 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 } \ No newline at end of file diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/DetectPinnedRegions.cs b/ICSharpCode.Decompiler/IL/ControlFlow/DetectPinnedRegions.cs index a7a89d40e..793dedb3a 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/DetectPinnedRegions.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/DetectPinnedRegions.cs @@ -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 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);