Browse Source

Keep ref iterator loops as while when live after loop

The loop-shape decision can use ILVariable use lists before AST lowering, so avoid creating a for-loop when its iterator updates a byref local that must remain usable after the loop.

Assisted-by: OpenAI:openai/gpt-5.5:OpenCode
pull/3863/head
Siegfried Pammer 3 days ago committed by Siegfried Pammer
parent
commit
2a3279d627
  1. 23
      ICSharpCode.Decompiler/IL/Transforms/HighLevelLoopTransform.cs

23
ICSharpCode.Decompiler/IL/Transforms/HighLevelLoopTransform.cs

@ -395,6 +395,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -395,6 +395,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// - increment block
if (incrementBlock.Instructions.Count <= 1 || loop.Blocks.Count < 3)
return false;
if (StoresRefLocalUsedAfterLoop(loop, incrementBlock))
return false;
context.Step("Transform to for loop: " + loop.EntryPoint.Label, loop);
// move the block to the end of the loop:
loop.Blocks.MoveElementToEnd(incrementBlock);
@ -467,6 +469,27 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -467,6 +469,27 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return true;
}
static bool StoresRefLocalUsedAfterLoop(BlockContainer loop, Block incrementBlock)
{
foreach (var store in incrementBlock.Instructions.SkipLast(1).SelectMany(inst => inst.Descendants).OfType<StLoc>())
{
var variable = store.Variable;
if (variable.Type.IsByRefLike && IsVariableUsedAfterLoop(loop, variable))
return true;
}
return false;
}
static bool IsVariableUsedAfterLoop(BlockContainer loop, ILVariable variable)
{
foreach (var use in variable.LoadInstructions.Concat<ILInstruction>(variable.AddressInstructions).Concat(variable.StoreInstructions.Cast<ILInstruction>()))
{
if (loop.GetCommonParent(use) is Block { Kind: BlockKind.ControlFlow } && loop.IsBefore(use))
return true;
}
return false;
}
bool IsAssignment(ILInstruction inst)
{
if (inst is StLoc)

Loading…
Cancel
Save