File: Contracts\Debugger\StackPusher.cs
Web Access
Project: src\runtime\src\native\managed\cdac\Microsoft.Diagnostics.DataContractReader.Contracts\Microsoft.Diagnostics.DataContractReader.Contracts.csproj (Microsoft.Diagnostics.DataContractReader.Contracts)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;

namespace Microsoft.Diagnostics.DataContractReader.Contracts;

public static class StackPusher
{
    public static TargetPointer Push(Target target, ref TargetPointer sp, Span<byte> bytes, bool align)
    {
        if (align)
        {
            AlignStackPointer(target, ref sp);
        }

        sp = new TargetPointer(sp.Value - (ulong)bytes.Length);

        if (align)
        {
            AlignStackPointer(target, ref sp);
        }

        target.WriteBuffer(sp.Value, bytes);
        return sp;
    }

    public static TargetPointer PushSlot(Target target, ref TargetPointer sp, TargetNUInt value, bool align)
    {
        if (align)
        {
            AlignStackPointer(target, ref sp);
        }

        sp = new TargetPointer(sp.Value - (ulong)target.PointerSize);

        if (align)
        {
            AlignStackPointer(target, ref sp);
        }

        target.WriteNUInt(sp.Value, value);
        return sp;
    }

    private static uint GetStackAlignment(Target target)
    {
        RuntimeInfoArchitecture arch = target.Contracts.RuntimeInfo.GetTargetArchitecture();
        RuntimeInfoOperatingSystem os = target.Contracts.RuntimeInfo.GetTargetOperatingSystem();

        return arch switch
        {
            // Windows x86 (cdecl/stdcall) only requires 4-byte SP alignment.
            // System V i386 ABI requires 16-byte alignment at call sites.
            RuntimeInfoArchitecture.X86 => os == RuntimeInfoOperatingSystem.Windows ? 4u : 16u,
            RuntimeInfoArchitecture.X64 => 16,
            RuntimeInfoArchitecture.Arm => 8, // AAPCS32: 8-byte aligned at public interfaces
            RuntimeInfoArchitecture.Arm64 => 16,
            RuntimeInfoArchitecture.LoongArch64 => 16,
            RuntimeInfoArchitecture.RiscV64 => 16,
            _ => throw new NotSupportedException($"StackPusher does not know the stack alignment for architecture '{arch}'."),
        };
    }

    private static void AlignStackPointer(Target target, ref TargetPointer sp)
    {
        uint alignment = GetStackAlignment(target);
        ulong mask = ~((ulong)alignment - 1UL);
        sp = new TargetPointer(sp.Value & mask);
    }

}