Skip to content

Commit

Permalink
LVA: Coding style cleanup (#9424)
Browse files Browse the repository at this point in the history
  • Loading branch information
mary-georgiou-sonarsource authored Jun 14, 2024
1 parent f15169a commit 2cc6d48
Show file tree
Hide file tree
Showing 7 changed files with 3,413 additions and 3,293 deletions.
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

0 comments on commit 2cc6d48

Please sign in to comment.