Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LVA: Coding style cleanup #9424

Merged
merged 10 commits into from
Jun 14, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -20,95 +20,92 @@

using SonarAnalyzer.CFG.Helpers;

namespace SonarAnalyzer.CFG.LiveVariableAnalysis
namespace SonarAnalyzer.CFG.LiveVariableAnalysis;

public abstract class LiveVariableAnalysisBase<TCfg, TBlock>
{
public abstract class LiveVariableAnalysisBase<TCfg, TBlock>
{
protected readonly ISymbol originalDeclaration;
protected readonly CancellationToken Cancel;
private readonly IDictionary<TBlock, HashSet<ISymbol>> blockLiveOut = new Dictionary<TBlock, HashSet<ISymbol>>();
private readonly IDictionary<TBlock, HashSet<ISymbol>> blockLiveIn = new Dictionary<TBlock, HashSet<ISymbol>>();
private readonly HashSet<ISymbol> captured = new();
protected readonly ISymbol originalDeclaration;
protected readonly CancellationToken Cancel;
private readonly Dictionary<TBlock, HashSet<ISymbol>> blockLiveOut = new();
private readonly Dictionary<TBlock, HashSet<ISymbol>> blockLiveIn = new();
private readonly HashSet<ISymbol> captured = [];

public abstract bool IsLocal(ISymbol symbol);
protected abstract TBlock ExitBlock { get; }
protected abstract State ProcessBlock(TBlock block);
protected abstract IEnumerable<TBlock> ReversedBlocks();
protected abstract IEnumerable<TBlock> Successors(TBlock block);
protected abstract IEnumerable<TBlock> Predecessors(TBlock block);
public abstract bool IsLocal(ISymbol symbol);
protected abstract TBlock ExitBlock { get; }
protected abstract State ProcessBlock(TBlock block);
protected abstract IEnumerable<TBlock> ReversedBlocks();
protected abstract IEnumerable<TBlock> Successors(TBlock block);
protected abstract IEnumerable<TBlock> Predecessors(TBlock block);

public TCfg Cfg { get; }
public IReadOnlyCollection<ISymbol> CapturedVariables => captured;
public TCfg Cfg { get; }
public IReadOnlyCollection<ISymbol> CapturedVariables => captured;

protected LiveVariableAnalysisBase(TCfg cfg, ISymbol originalDeclaration, CancellationToken cancel)
{
Cfg = cfg;
this.originalDeclaration = originalDeclaration;
this.Cancel = cancel;
}
protected LiveVariableAnalysisBase(TCfg cfg, ISymbol originalDeclaration, CancellationToken cancel)
{
Cfg = cfg;
this.originalDeclaration = originalDeclaration;
Cancel = cancel;
}

/// <summary>
/// LiveIn variables are alive when entering block. They are read inside the block or any of it's successors.
/// </summary>
public IReadOnlyCollection<ISymbol> LiveIn(TBlock block) =>
blockLiveIn[block].Except(captured).ToImmutableArray();
/// <summary>
/// LiveIn variables are alive when entering block. They are read inside the block or any of it's successors.
/// </summary>
public IEnumerable<ISymbol> LiveIn(TBlock block) =>
blockLiveIn[block].Except(captured);

/// <summary>
/// LiveOut variables are alive when exiting block. They are read in any of it's successors.
/// </summary>
public IReadOnlyCollection<ISymbol> LiveOut(TBlock block) =>
blockLiveOut[block].Except(captured).ToImmutableArray();
/// <summary>
/// LiveOut variables are alive when exiting block. They are read in any of it's successors.
/// </summary>
public IEnumerable<ISymbol> LiveOut(TBlock block) =>
blockLiveOut[block].Except(captured);

protected void Analyze()
protected void Analyze()
{
var states = new Dictionary<TBlock, State>();
var queue = new UniqueQueue<TBlock>();
foreach (var block in ReversedBlocks())
{
var states = new Dictionary<TBlock, State>();
var queue = new UniqueQueue<TBlock>();
foreach (var block in ReversedBlocks())
var state = ProcessBlock(block);
captured.UnionWith(state.Captured);
states.Add(block, state);
blockLiveIn.Add(block, new HashSet<ISymbol>());
blockLiveOut.Add(block, new HashSet<ISymbol>());
queue.Enqueue(block);
}
while (queue.Any())
{
Cancel.ThrowIfCancellationRequested();

var block = queue.Dequeue();
var liveOut = blockLiveOut[block];
// note that on the PHP LVA impl, the `liveOut` gets cleared before being updated
foreach (var successorLiveIn in Successors(block).Select(x => blockLiveIn[x]).Where(x => x.Any()))
{
var state = ProcessBlock(block);
captured.UnionWith(state.Captured);
states.Add(block, state);
blockLiveIn.Add(block, new HashSet<ISymbol>());
blockLiveOut.Add(block, new HashSet<ISymbol>());
queue.Enqueue(block);
liveOut.UnionWith(successorLiveIn);
}
while (queue.Any())
// liveIn = UsedBeforeAssigned + (LiveOut - Assigned)
var liveIn = states[block].UsedBeforeAssigned.Concat(liveOut.Except(states[block].Assigned)).ToHashSet();
// Don't enqueue predecessors if nothing changed.
if (!liveIn.SetEquals(blockLiveIn[block]))
{
Cancel.ThrowIfCancellationRequested();

var block = queue.Dequeue();
var liveOut = blockLiveOut[block];
// note that on the PHP LVA impl, the `liveOut` gets cleared before being updated
foreach (var successorLiveIn in Successors(block).Select(x => blockLiveIn[x]).Where(x => x.Any()))
{
liveOut.UnionWith(successorLiveIn);
}
// liveIn = UsedBeforeAssigned + (LiveOut - Assigned)
var liveIn = states[block].UsedBeforeAssigned.Concat(liveOut.Except(states[block].Assigned)).ToHashSet();
// Don't enqueue predecessors if nothing changed.
if (!liveIn.SetEquals(blockLiveIn[block]))
blockLiveIn[block] = liveIn;
foreach (var predecessor in Predecessors(block))
{
blockLiveIn[block] = liveIn;
foreach (var predecessor in Predecessors(block))
{
queue.Enqueue(predecessor);
}
queue.Enqueue(predecessor);
}
}
if (blockLiveOut[ExitBlock].Any())
{
throw new InvalidOperationException("Out of exit block should be empty");
}
}

protected class State
if (blockLiveOut[ExitBlock].Any())
{
public ISet<ISymbol> Assigned { get; } = new HashSet<ISymbol>(); // Kill: The set of variables that are assigned a value.
public ISet<ISymbol> UsedBeforeAssigned { get; } = new HashSet<ISymbol>(); // Gen: The set of variables that are used before any assignment.
public ISet<ISymbol> ProcessedLocalFunctions { get; } = new HashSet<ISymbol>();
public ISet<ISymbol> Captured { get; } = new HashSet<ISymbol>();

protected State() { }
throw new InvalidOperationException("Out of exit block should be empty");
}
}

protected abstract class State
{
public HashSet<ISymbol> Assigned { get; } = []; // Kill: The set of variables that are assigned a value.
public HashSet<ISymbol> UsedBeforeAssigned { get; } = []; // Gen: The set of variables that are used before any assignment.
public HashSet<ISymbol> ProcessedLocalFunctions { get; } = [];
public HashSet<ISymbol> Captured { get; } = [];
}
}
Loading
Loading