//
// Copyright (c) Microsoft Corporation.   All rights reserved.
//

/*******************************************************************/
/*                           WARNING                               */
/* This file should be identical in the Bartok and Singularity     */
/* depots. Master copy resides in Bartok Depot. Changes should be  */
/* made to Bartok Depot and propagated to Singularity Depot.       */
/*******************************************************************/


namespace System.GCs {

    using Microsoft.Bartok.Options;

    using System.Runtime.CompilerServices;

#if SINGULARITY
    using Microsoft.Singularity;
#endif

    [MixinConditional("X86")]
    [MixinConditional("ISA_IX86")]
    [Mixin(typeof(System.GCs.CalleeSaveRegisters))]
    [AccessedByRuntime("referenced from halforgc.asm")]
    internal struct CalleeSaveRegistersX86 /* : CalleeSaveRegisters */
    {

        [MixinOverride]
        internal void DebugTrace() {
            Trace.Log(Trace.Area.Stack,
                      "Registers:  EBX={0:x}, EDI={1:x}, ESI={2:x}, EBP={3:x}",
                      __arglist(this.EBX, this.EDI, this.ESI, this.EBP));
        }

        [MixinOverride]
        internal UIntPtr GetFramePointer()
        {
            return this.EBP;
        }

        [AccessedByRuntime("referenced from halforgc.asm")]
        internal UIntPtr EBX;
        [AccessedByRuntime("referenced from halforgc.asm")]
        internal UIntPtr EDI;
        [AccessedByRuntime("referenced from halforgc.asm")]
        internal UIntPtr ESI;
        [AccessedByRuntime("referenced from halforgc.asm")]
        internal UIntPtr EBP;
    }

    [MixinConditional("X86")]
    [MixinConditional("ISA_IX86")]
    [Mixin(typeof(System.GCs.CalleeSaveLocations))]
    internal unsafe struct CalleeSaveLocationsX86 /* : CalleeSaveLocations */
    {

#if SINGULARITY_KERNEL
#if false
        [MixinOverride]
        internal void SetCalleeSaves(SpillContext *context) {
            EBX.SetCalleeReg(&context->ebx);
            EDI.SetCalleeReg(&context->edi);
            ESI.SetCalleeReg(&context->esi);
            EBP.SetCalleeReg(&context->ebp);
        }
#endif
#endif

        [MixinOverride]
        internal
        void SetCalleeSaves(CalleeSaveRegistersX86 *calleeSaveRegisters)
        {
            EBX.SetCalleeReg(&calleeSaveRegisters->EBX);
            EDI.SetCalleeReg(&calleeSaveRegisters->EDI);
            ESI.SetCalleeReg(&calleeSaveRegisters->ESI);
            EBP.SetCalleeReg(&calleeSaveRegisters->EBP);
        }

        [MixinOverride]
        internal void ClearCalleeSaves() {
            EBX.ClearCalleeReg();
            ESI.ClearCalleeReg();
            EDI.ClearCalleeReg();
            EBP.ClearCalleeReg();
        }

        [MixinOverride]
        internal void ScanLiveRegs(UIntPtr mask,
                                   NonNullReferenceVisitor referenceVisitor)
        {
            EDI.ScanLiveReg((mask >> 2) & 0x3, referenceVisitor);
            ESI.ScanLiveReg((mask >> 4) & 0x3, referenceVisitor);
            EBX.ScanLiveReg((mask >> 0) & 0x3, referenceVisitor);
            EBP.ScanLiveReg((mask >> 6) & 0x3, referenceVisitor);
        }

        [MixinOverride]
        internal void PopFrame(UIntPtr *framePointer,
                               UIntPtr calleeSaveMask,
                               bool framePointerOmitted,
                               bool hasTransitionRecord)
        {
            UIntPtr *calleeSaveStart;
            if (framePointerOmitted) {
                calleeSaveStart = framePointer - 1;
            } else {
                VTable.Assert((calleeSaveMask & 0x10) == 0,
                              "EBP should not be callee saved");
                calleeSaveStart = framePointer;
                EBP.PopFrameReg(ref calleeSaveStart);
            }
            if (hasTransitionRecord) {
                calleeSaveStart -=
                    sizeof(CallStack.TransitionRecord) / sizeof(UIntPtr);
            }
            // Note: the order in which these appear is important!
            if ((calleeSaveMask & 0x1) != 0) {
                EBX.PopFrameReg(ref calleeSaveStart);
            }
            if ((calleeSaveMask & 0x8) != 0) {
                EBP.PopFrameReg(ref calleeSaveStart);
            }
            if ((calleeSaveMask & 0x4) != 0) {
                ESI.PopFrameReg(ref calleeSaveStart);
            }
            if ((calleeSaveMask & 0x2) != 0) {
                EDI.PopFrameReg(ref calleeSaveStart);
            }
        }

        [MixinOverride]
        internal void ClearFrame(UIntPtr calleeSaveMask,
                                 bool framePointerOmitted)
        {
            if (!framePointerOmitted) {
                VTable.Assert((calleeSaveMask & 0x10) == 0,
                              "EBP should not be callee saved");
                EBP.ClearFrameReg();
            }
            if ((calleeSaveMask & 0x1) != 0) {
                EBX.ClearFrameReg();
            }
            if ((calleeSaveMask & 0x8) != 0) {
                EBP.ClearFrameReg();
            }
            if ((calleeSaveMask & 0x4) != 0) {
                ESI.ClearFrameReg();
            }
            if ((calleeSaveMask & 0x2) != 0) {
                EDI.ClearFrameReg();
            }
        }

        [MixinOverride]
        internal UIntPtr GetFramePointer()
        {
            return EBP.value;
        }

        CalleeSaveLocations.RegLocation EBX;
        CalleeSaveLocations.RegLocation EDI;
        CalleeSaveLocations.RegLocation ESI;
        CalleeSaveLocations.RegLocation EBP;

    }

}
