|
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Collections.Immutable;
using System.Diagnostics;
using ILLink.Shared.DataFlow;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.FlowAnalysis;
using Microsoft.CodeAnalysis.Operations;
namespace ILLink.RoslynAnalyzer.DataFlow
{
// This class is responsible for the interprocedural analysis of local variables.
// It substitutes type arguments into the generic forward dataflow analysis,
// creating a simpler abstraction that can track the values of local variables using Roslyn APIs.
// The kinds of values tracked are still left as unspecified generic parameters TValue and TLattice.
public abstract class LocalDataFlowAnalysis<TValue, TContext, TLattice, TContextLattice, TTransfer, TConditionValue>
: ForwardDataFlowAnalysis<
LocalStateAndContext<TValue, TContext>,
LocalDataFlowState<TValue, TContext, TLattice, TContextLattice>,
LocalStateAndContextLattice<TValue, TContext, TLattice, TContextLattice>,
BlockProxy,
RegionProxy,
ControlFlowGraphProxy,
TTransfer,
TConditionValue
>
where TValue : struct, IEquatable<TValue>
where TContext : struct, IEquatable<TContext>
where TLattice : ILattice<TValue>
where TContextLattice : ILattice<TContext>
where TTransfer : LocalDataFlowVisitor<TValue, TContext, TLattice, TContextLattice, TConditionValue>
where TConditionValue : struct, INegate<TConditionValue>
{
protected readonly OperationBlockAnalysisContext Context;
private readonly IOperation OperationBlock;
private static LocalStateAndContextLattice<TValue, TContext, TLattice, TContextLattice> GetLatticeAndEntryValue(
TLattice lattice,
TContextLattice contextLattice,
TContext initialContext,
out LocalStateAndContext<TValue, TContext> entryValue)
{
LocalStateAndContextLattice<TValue, TContext, TLattice, TContextLattice> localStateAndContextLattice = new(new(lattice), contextLattice);
entryValue = new LocalStateAndContext<TValue, TContext>(default(LocalState<TValue>), initialContext);
return localStateAndContextLattice;
}
// The initial value of the local dataflow is the empty local state (no tracked assignments),
// with an initial context that must be specified by the derived class.
protected LocalDataFlowAnalysis(
OperationBlockAnalysisContext context,
IOperation operationBlock,
TLattice lattice,
TContextLattice contextLattice,
TContext initialContext)
: base(GetLatticeAndEntryValue(lattice, contextLattice, initialContext, out var entryValue), entryValue)
{
Context = context;
OperationBlock = operationBlock;
}
public void InterproceduralAnalyze()
{
ValueSetLattice<MethodBodyValue> methodGroupLattice = default;
DictionaryLattice<LocalKey, Maybe<TValue>, MaybeLattice<TValue, TLattice>> hoistedLocalLattice = default;
var interproceduralStateLattice = new InterproceduralStateLattice<TValue, TLattice>(
methodGroupLattice, hoistedLocalLattice);
var interproceduralState = interproceduralStateLattice.Top;
var oldInterproceduralState = interproceduralState.Clone();
if (OperationBlock is IAttributeOperation attribute)
{
AnalyzeAttribute(Context.OwningSymbol, attribute);
return;
}
Debug.Assert(Context.OwningSymbol is not IMethodSymbol methodSymbol ||
methodSymbol.MethodKind is not (MethodKind.LambdaMethod or MethodKind.LocalFunction));
var startMethod = new MethodBodyValue(Context.OwningSymbol, Context.GetControlFlowGraph(OperationBlock));
interproceduralState.TrackMethod(startMethod);
while (!interproceduralState.Equals(oldInterproceduralState))
{
oldInterproceduralState = interproceduralState.Clone();
Debug.Assert(!oldInterproceduralState.Methods.IsUnknown());
foreach (var method in oldInterproceduralState.Methods.GetKnownValues())
{
AnalyzeMethod(method, ref interproceduralState);
}
}
}
private void AnalyzeAttribute(ISymbol owningSymbol, IAttributeOperation attribute)
{
var cfg = Context.GetControlFlowGraph(attribute);
var lValueFlowCaptures = LValueFlowCapturesProvider.CreateLValueFlowCaptures(cfg);
var visitor = GetVisitor(owningSymbol, cfg, lValueFlowCaptures, default);
Fixpoint(new ControlFlowGraphProxy(cfg), visitor);
}
private void AnalyzeMethod(MethodBodyValue method, ref InterproceduralState<TValue, TLattice> interproceduralState)
{
var cfg = method.ControlFlowGraph;
var lValueFlowCaptures = LValueFlowCapturesProvider.CreateLValueFlowCaptures(cfg);
var visitor = GetVisitor(method.OwningSymbol, cfg, lValueFlowCaptures, interproceduralState);
Fixpoint(new ControlFlowGraphProxy(cfg), visitor);
// The interprocedural state struct is stored as a field of the visitor and modified
// in-place there, but we also need those modifications to be reflected here.
interproceduralState = visitor.InterproceduralState;
}
protected abstract TTransfer GetVisitor(
ISymbol owningSymbol,
ControlFlowGraph methodCFG,
ImmutableDictionary<CaptureId, FlowCaptureKind> lValueFlowCaptures,
InterproceduralState<TValue, TLattice> interproceduralState);
}
}
|