File: Linker.Dataflow\InterproceduralState.cs
Web Access
Project: src\src\tools\illink\src\linker\Mono.Linker.csproj (illink)
// 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);
    }
}