|
// 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.Generic;
using System.Diagnostics;
using ILLink.Shared;
using ILLink.Shared.DataFlow;
using Mono.Cecil;
using Mono.Cecil.Cil;
using HoistedLocalState = ILLink.Shared.DataFlow.DefaultValueDictionary<
Mono.Linker.Dataflow.HoistedLocalKey,
ILLink.Shared.DataFlow.ValueSet<ILLink.Shared.DataFlow.SingleValue>>;
using MultiValue = ILLink.Shared.DataFlow.ValueSet<ILLink.Shared.DataFlow.SingleValue>;
namespace Mono.Linker.Dataflow
{
// Tracks the set of methods which get analyzer together during interprocedural analysis,
// and the possible states of hoisted locals in state machine methods and lambdas/local functions.
struct InterproceduralState : IEquatable<InterproceduralState>
{
public ValueSet<MethodIL> MethodBodies;
public HoistedLocalState HoistedLocals;
readonly InterproceduralStateLattice lattice;
public InterproceduralState(ValueSet<MethodIL> methodBodies, HoistedLocalState hoistedLocals, InterproceduralStateLattice lattice)
=> (MethodBodies, HoistedLocals, this.lattice) = (methodBodies, hoistedLocals, lattice);
public bool Equals(InterproceduralState other)
=> MethodBodies.Equals(other.MethodBodies) && HoistedLocals.Equals(other.HoistedLocals);
public override bool Equals(object? obj)
=> obj is InterproceduralState state && Equals(state);
public override int GetHashCode() => HashUtils.Combine(MethodBodies.GetHashCode(), HoistedLocals.GetHashCode());
public InterproceduralState Clone()
=> new(MethodBodies.DeepCopy(), HoistedLocals.Clone(), lattice);
public void TrackMethod(MethodDefinition method)
{
if (method.Body is not MethodBody methodBody)
return;
TrackMethod(methodBody);
}
public void TrackMethod(MethodBody methodBody)
{
TrackMethod(lattice.Context.GetMethodIL(methodBody));
}
public void TrackMethod(MethodIL methodIL)
{
// Work around the fact that ValueSet is readonly
Debug.Assert(!MethodBodies.IsUnknown());
var methodsList = new List<MethodIL>(MethodBodies.GetKnownValues());
methodsList.Add(methodIL);
// For state machine methods, also scan the state machine members.
// Simplification: assume that all generated methods of the state machine type are
// reached at the point where the state machine method is reached.
if (CompilerGeneratedState.TryGetStateMachineType(methodIL.Method, out TypeDefinition? stateMachineType))
{
foreach (var stateMachineMethod in stateMachineType.Methods)
{
Debug.Assert(!CompilerGeneratedNames.IsLambdaOrLocalFunction(stateMachineMethod.Name));
if (stateMachineMethod.Body is MethodBody stateMachineMethodBody)
methodsList.Add(lattice.Context.GetMethodIL(stateMachineMethodBody));
}
}
MethodBodies = new ValueSet<MethodIL>(methodsList);
}
public void SetHoistedLocal(HoistedLocalKey key, MultiValue value)
{
// For hoisted locals, we track the entire set of assigned values seen
// in the closure of a method, so setting a hoisted local value meets
// it with any existing value.
HoistedLocals.Set(key,
lattice.HoistedLocalsLattice.ValueLattice.Meet(
HoistedLocals.Get(key), value));
}
public MultiValue GetHoistedLocal(HoistedLocalKey key)
=> HoistedLocals.Get(key);
}
readonly struct InterproceduralStateLattice : ILattice<InterproceduralState>
{
public readonly ValueSetLattice<MethodIL> MethodBodyLattice;
public readonly DictionaryLattice<HoistedLocalKey, MultiValue, ValueSetLattice<SingleValue>> HoistedLocalsLattice;
public readonly LinkContext Context;
public InterproceduralStateLattice(
ValueSetLattice<MethodIL> methodBodyLattice,
DictionaryLattice<HoistedLocalKey, MultiValue, ValueSetLattice<SingleValue>> hoistedLocalsLattice,
LinkContext context)
=> (MethodBodyLattice, HoistedLocalsLattice, Context) = (methodBodyLattice, hoistedLocalsLattice, context);
public InterproceduralState Top => new InterproceduralState(MethodBodyLattice.Top, HoistedLocalsLattice.Top, this);
public InterproceduralState Meet(InterproceduralState left, InterproceduralState right)
=> new(
MethodBodyLattice.Meet(left.MethodBodies, right.MethodBodies),
HoistedLocalsLattice.Meet(left.HoistedLocals, right.HoistedLocals),
this);
}
}
|