Skip to content

Commit 38e7ab4

Browse files
committed
Fix #3110: Add support for MCS 2.6.4 pinned region with array variable
* Added additional code to remove the conv instruction present in the initialization part of the pinned region. * Extended the code responsible for removing the unpin stloc to correctly match the inverted condition found in MCS 2.6.4 compiled code. * Enabled already present correctness test to run for MCS 2.6.4. This is a more generalized version of the fix on PR #3110 proposed by @ElektroKill.
1 parent 67eade3 commit 38e7ab4

File tree

4 files changed

+36
-14
lines changed

4 files changed

+36
-14
lines changed

ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs

-4
Original file line numberDiff line numberDiff line change
@@ -336,10 +336,6 @@ public async Task StackTypes([Values(false, true)] bool force32Bit)
336336
[Test]
337337
public async Task UnsafeCode([ValueSource(nameof(defaultOptions))] CompilerOptions options)
338338
{
339-
if (options.HasFlag(CompilerOptions.UseMcs2_6_4))
340-
{
341-
Assert.Ignore("Decompiler bug with mono!");
342-
}
343339
await RunCS(options: options);
344340
}
345341

ICSharpCode.Decompiler/IL/ControlFlow/DetectPinnedRegions.cs

+31-10
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
using System.Collections.Generic;
2020
using System.Diagnostics;
2121
using System.Linq;
22+
using System.Runtime.InteropServices.ComTypes;
2223

2324
using ICSharpCode.Decompiler.IL.Transforms;
2425
using ICSharpCode.Decompiler.TypeSystem;
@@ -612,18 +613,16 @@ bool CreatePinnedRegion(Block block, StLoc stLoc)
612613
innerBlock = (Block)innerBlock.Clone();
613614
clonedBlocks[i] = innerBlock;
614615
}
615-
Branch br = innerBlock.Instructions.LastOrDefault() as Branch;
616-
if (br != null && br.TargetBlock.IncomingEdgeCount == 1
617-
&& br.TargetContainer == sourceContainer && reachedEdgesPerBlock[br.TargetBlock.ChildIndex] == 0)
616+
if (innerBlock.MatchIfAtEndOfBlock(out _, out var trueInst, out var falseInst))
618617
{
619-
// branch that leaves body.
620-
// The target block should have an instruction that resets the pin; delete that instruction:
621-
StLoc unpin = br.TargetBlock.Instructions.First() as StLoc;
622-
if (unpin != null && unpin.Variable == stLoc.Variable && IsNullOrZero(unpin.Value))
623-
{
624-
br.TargetBlock.Instructions.RemoveAt(0);
625-
}
618+
HandleBranchLeavingPinnedRegion(trueInst, reachedEdgesPerBlock, sourceContainer, stLoc.Variable);
619+
HandleBranchLeavingPinnedRegion(falseInst, reachedEdgesPerBlock, sourceContainer, stLoc.Variable);
620+
}
621+
else
622+
{
623+
HandleBranchLeavingPinnedRegion(innerBlock.Instructions.LastOrDefault(), reachedEdgesPerBlock, sourceContainer, stLoc.Variable);
626624
}
625+
627626
// move block into body
628627
if (sourceContainer.Blocks[i] == entryBlock)
629628
{
@@ -698,6 +697,21 @@ bool CreatePinnedRegion(Block block, StLoc stLoc)
698697
return true;
699698
}
700699

700+
static void HandleBranchLeavingPinnedRegion(ILInstruction potentialBranch, int[] reachedEdgesPerBlock, BlockContainer sourceContainer, ILVariable pinnedRegionVar)
701+
{
702+
if (potentialBranch is Branch branch && branch.TargetBlock.IncomingEdgeCount == 1
703+
&& branch.TargetContainer == sourceContainer && reachedEdgesPerBlock[branch.TargetBlock.ChildIndex] == 0)
704+
{
705+
// branch that leaves body.
706+
// The target block should have an instruction that resets the pin; delete that instruction:
707+
StLoc unpin = branch.TargetBlock.Instructions.First() as StLoc;
708+
if (unpin != null && unpin.Variable == pinnedRegionVar && IsNullOrZero(unpin.Value))
709+
{
710+
branch.TargetBlock.Instructions.RemoveAt(0);
711+
}
712+
}
713+
}
714+
701715
static bool IsNullOrZero(ILInstruction inst)
702716
{
703717
while (inst is Conv conv)
@@ -750,6 +764,13 @@ void ProcessPinnedRegion(PinnedRegion pinnedRegion)
750764
// fixing a string
751765
HandleStringToPointer(pinnedRegion);
752766
}
767+
else if (pinnedRegion.Init is Conv { Kind: ConversionKind.StopGCTracking, Argument: var convArg })
768+
{
769+
// If pinnedRegion.Variable was already a pointer type, the input IL has a StopGCTracking conversion.
770+
// We can simply remove this conversion, as it is not needed.
771+
context.Step("Remove StopGCTracking conversion", pinnedRegion);
772+
pinnedRegion.Init = convArg;
773+
}
753774
// Detect nested pinned regions:
754775
BlockContainer body = (BlockContainer)pinnedRegion.Body;
755776
foreach (var block in body.Blocks)

ICSharpCode.Decompiler/IL/Transforms/IILTransform.cs

+2
Original file line numberDiff line numberDiff line change
@@ -88,12 +88,14 @@ internal ILReader CreateILReader()
8888
/// Unlike <c>context.Stepper.Step()</c>, calls to this method are only compiled in debug builds.
8989
/// </summary>
9090
[Conditional("STEP")]
91+
[DebuggerStepThrough]
9192
internal void Step(string description, ILInstruction? near)
9293
{
9394
Stepper.Step(description, near);
9495
}
9596

9697
[Conditional("STEP")]
98+
[DebuggerStepThrough]
9799
internal void StepStartGroup(string description, ILInstruction? near = null)
98100
{
99101
Stepper.StartGroup(description, near);

ICSharpCode.Decompiler/IL/Transforms/Stepper.cs

+3
Original file line numberDiff line numberDiff line change
@@ -95,11 +95,13 @@ public Stepper()
9595
///
9696
/// May throw <see cref="StepLimitReachedException"/> in debug mode.
9797
/// </summary>
98+
[DebuggerStepThrough]
9899
public void Step(string description, ILInstruction? near = null)
99100
{
100101
StepInternal(description, near);
101102
}
102103

104+
[DebuggerStepThrough]
103105
private Node StepInternal(string description, ILInstruction? near)
104106
{
105107
if (step == StepLimit)
@@ -123,6 +125,7 @@ private Node StepInternal(string description, ILInstruction? near)
123125
return stepNode;
124126
}
125127

128+
[DebuggerStepThrough]
126129
public void StartGroup(string description, ILInstruction? near = null)
127130
{
128131
groups.Push(StepInternal(description, near));

0 commit comments

Comments
 (0)