Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,9 @@ protected LocalDataFlowAnalysis(
OperationBlock = operationBlock;
}

public void InterproceduralAnalyze()
public bool InterproceduralAnalyze()
{
bool succeeded = true;
ValueSetLattice<MethodBodyValue> methodGroupLattice = default;
DictionaryLattice<LocalKey, Maybe<TValue>, MaybeLattice<TValue, TLattice>> hoistedLocalLattice = default;
var interproceduralStateLattice = new InterproceduralStateLattice<TValue, TLattice>(
Expand All @@ -75,8 +76,8 @@ public void InterproceduralAnalyze()

if (OperationBlock is IAttributeOperation attribute)
{
AnalyzeAttribute(Context.OwningSymbol, attribute);
return;
succeeded &= AnalyzeAttribute(Context.OwningSymbol, attribute);
return succeeded;
}

Debug.Assert(Context.OwningSymbol is not IMethodSymbol methodSymbol ||
Expand All @@ -91,29 +92,31 @@ public void InterproceduralAnalyze()
Debug.Assert(!oldInterproceduralState.Methods.IsUnknown());
foreach (var method in oldInterproceduralState.Methods.GetKnownValues())
{
AnalyzeMethod(method, ref interproceduralState);
succeeded &= AnalyzeMethod(method, ref interproceduralState);
}
}
return succeeded;
}

private void AnalyzeAttribute(ISymbol owningSymbol, IAttributeOperation attribute)
private bool 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);
return Fixpoint(new ControlFlowGraphProxy(cfg), visitor);
}

private void AnalyzeMethod(MethodBodyValue method, ref InterproceduralState<TValue, TLattice> interproceduralState)
private bool 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);
bool succeeded = 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;
return succeeded;
}

protected abstract TTransfer GetVisitor(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ public static ImmutableArray<DiagnosticDescriptor> GetSupportedDiagnostics()
diagDescriptorsArrayBuilder.Add(DiagnosticDescriptors.GetDiagnosticDescriptor(DiagnosticId.ReturnValueDoesNotMatchFeatureGuards));
diagDescriptorsArrayBuilder.Add(DiagnosticDescriptors.GetDiagnosticDescriptor(DiagnosticId.InvalidFeatureGuard));
diagDescriptorsArrayBuilder.Add(DiagnosticDescriptors.GetDiagnosticDescriptor(DiagnosticId.TypeMapGroupTypeCannotBeStaticallyDetermined));
diagDescriptorsArrayBuilder.Add(DiagnosticDescriptors.GetDiagnosticDescriptor(DiagnosticId.DataflowAnalysisDidNotConverge));

foreach (var requiresAnalyzer in RequiresAnalyzers.Value)
{
Expand Down Expand Up @@ -110,8 +111,17 @@ public override void Initialize(AnalysisContext context)
foreach (var operationBlock in context.OperationBlocks)
{
TrimDataFlowAnalysis trimDataFlowAnalysis = new(context, dataFlowAnalyzerContext, operationBlock);
trimDataFlowAnalysis.InterproceduralAnalyze();
bool success = trimDataFlowAnalysis.InterproceduralAnalyze();
trimDataFlowAnalysis.ReportDiagnostics(context.ReportDiagnostic);
if (!success)
{
context.ReportDiagnostic(
Diagnostic.Create(DiagnosticDescriptors.GetDiagnosticDescriptor(
DiagnosticId.DataflowAnalysisDidNotConverge,
diagnosticSeverity: DiagnosticSeverity.Warning),
operationBlock.Syntax.GetLocation(),
operationBlock.FindContainingSymbol(context.OwningSymbol).GetDisplayName()));
}
}
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ successor.ConditionKind is ConditionKind.WhenTrue

// This just runs a dataflow algorithm until convergence. It doesn't cache any results,
// allowing each particular kind of analysis to decide what is worth saving.
public void Fixpoint(TControlFlowGraph cfg, TTransfer transfer)
public bool Fixpoint(TControlFlowGraph cfg, TTransfer transfer)
{
TraceStart(cfg);

Expand Down Expand Up @@ -244,11 +244,15 @@ public void Fixpoint(TControlFlowGraph cfg, TTransfer transfer)
};

bool changed = true;
while (changed)
const int MaxIterations = 10_000;
int iterations = 0;
while (changed && iterations < MaxIterations)
{
changed = false;
foreach (var block in cfg.Blocks)
{
if (++iterations >= MaxIterations)
break;

TraceVisitBlock(block);

Expand Down Expand Up @@ -417,6 +421,8 @@ public void Fixpoint(TControlFlowGraph cfg, TTransfer transfer)
}
}

return !changed || iterations >= MaxIterations;

void FlowStateThroughExitedFinallys(
IControlFlowGraph<TBlock, TRegion>.ControlFlowBranch predecessor,
ref TValue predecessorState)
Expand Down
1 change: 1 addition & 0 deletions src/tools/illink/src/ILLink.Shared/DiagnosticId.cs
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ public enum DiagnosticId
RequiresUnreferencedCodeOnEntryPoint = 2123,
TypeMapGroupTypeCannotBeStaticallyDetermined = 2124,
ReferenceNotMarkedIsTrimmable = 2125,
DataflowAnalysisDidNotConverge = 2126,
_EndTrimAnalysisWarningsSentinel,

// Single-file diagnostic ids.
Expand Down
6 changes: 6 additions & 0 deletions src/tools/illink/src/ILLink.Shared/SharedStrings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -1251,4 +1251,10 @@
<data name="ReferenceNotMarkedIsAotCompatibleMessage" xml:space="preserve">
<value>Referenced assembly '{0}' is not built with `<IsAotCompatible>true</IsAotCompatible>` and may not be compatible with AOT.</value>
</data>
<data name="DataflowAnalysisDidNotConvergeTitle" xml:space="preserve">
<value>Trim dataflow analysis took too long to complete. Trim safety cannot be guaranteed.</value>
</data>
<data name="DataflowAnalysisDidNotConvergeMessage" xml:space="preserve">
<value>Trim dataflow analysis of member '{0}' took too long to complete. Trim safety cannot be guaranteed.</value>
</data>
</root>
Loading