// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

.intel_syntax noprefix
#include <AsmOffsets.inc>         // generated by the build from AsmOffsets.cpp
#include <unixasmmacros.inc>

// Allocate non-array, non-finalizable object. If the allocation doesn't fit into the current thread's
// allocation context then automatically fallback to the slow allocation path.
//  RDI == MethodTable
NESTED_ENTRY RhpNewFast, _TEXT, NoHandler
        push_nonvol_reg rbx
        mov         rbx, rdi

        // rax = GetThread()
        INLINE_GETTHREAD

        //
        // rbx contains MethodTable pointer
        //
        mov         edx, [rbx + OFFSETOF__MethodTable__m_uBaseSize]

        //
        // rax: Thread pointer
        // rbx: MethodTable pointer
        // rdx: base size
        //

        mov         rsi, [rax + OFFSETOF__Thread__m_alloc_context__alloc_ptr]
        add         rdx, rsi
        cmp         rdx, [rax + OFFSETOF__Thread__m_alloc_context__alloc_limit]
        ja          LOCAL_LABEL(RhpNewFast_RarePath)

        // set the new alloc pointer
        mov         [rax + OFFSETOF__Thread__m_alloc_context__alloc_ptr], rdx

        mov         rax, rsi

        // set the new object's MethodTable pointer
        mov         [rsi], rbx

        .cfi_remember_state
        pop_nonvol_reg rbx
        ret

        .cfi_restore_state
        .cfi_def_cfa_offset 16          // workaround cfi_restore_state bug
LOCAL_LABEL(RhpNewFast_RarePath):
        mov         rdi, rbx            // restore MethodTable
        xor         esi, esi
        pop_nonvol_reg rbx
        jmp         C_FUNC(RhpNewObject)

NESTED_END RhpNewFast, _TEXT



// Allocate non-array object with finalizer
//  RDI == MethodTable
LEAF_ENTRY RhpNewFinalizable, _TEXT
        mov         esi, GC_ALLOC_FINALIZE
        jmp         C_FUNC(RhpNewObject)
LEAF_END RhpNewFinalizable, _TEXT



// Allocate non-array object
//  RDI == MethodTable
//  ESI == alloc flags
NESTED_ENTRY RhpNewObject, _TEXT, NoHandler

        PUSH_COOP_PINVOKE_FRAME rcx
        END_PROLOGUE

        // RCX: transition frame

        // Preserve the MethodTable in RBX
        mov         rbx, rdi

        xor         edx, edx    // numElements

        // Call the rest of the allocation helper.
        // void* RhpGcAlloc(MethodTable *pEEType, uint32_t uFlags, uintptr_t numElements, void * pTransitionFrame)
        call        C_FUNC(RhpGcAlloc)

        test        rax, rax
        jz          LOCAL_LABEL(NewOutOfMemory)

        .cfi_remember_state
        POP_COOP_PINVOKE_FRAME
        ret

        .cfi_restore_state
        .cfi_def_cfa_offset 96          // workaround cfi_restore_state bug
LOCAL_LABEL(NewOutOfMemory):
        // This is the OOM failure path. We're going to tail-call to a managed helper that will throw
        // an out of memory exception that the caller of this allocator understands.

        mov         rdi, rbx            // MethodTable pointer
        xor         esi, esi            // Indicate that we should throw OOM.

        POP_COOP_PINVOKE_FRAME

        jmp         EXTERNAL_C_FUNC(RhExceptionHandling_FailedAllocation)
NESTED_END RhpNewObject, _TEXT


// Allocate a string.
//  RDI == MethodTable
//  ESI == character/element count
NESTED_ENTRY RhNewString, _TEXT, NoHandler
        // we want to limit the element count to the non-negative 32-bit int range
        cmp         rsi, MAX_STRING_LENGTH
        ja          LOCAL_LABEL(StringSizeOverflow)

        push_nonvol_reg rbx
        push_nonvol_reg r12
        push_register rcx       // padding

        mov         rbx, rdi    // save MethodTable
        mov         r12, rsi    // save element count

        // rax = GetThread()
        INLINE_GETTHREAD

        mov         rcx, rax    // rcx = Thread*

        // Compute overall allocation size (align(base size + (element size * elements), 8)).
        lea         rax, [r12 * STRING_COMPONENT_SIZE + STRING_BASE_SIZE + 7]
        and         rax, -8

        // rax == string size
        // rbx == MethodTable
        // rcx == Thread*
        // r12 == element count

        mov         rdx, rax
        add         rax, [rcx + OFFSETOF__Thread__m_alloc_context__alloc_ptr]
        jc          LOCAL_LABEL(RhNewString_RarePath)

        // rax == new alloc ptr
        // rbx == MethodTable
        // rcx == Thread*
        // rdx == string size
        // r12 == element count
        cmp         rax, [rcx + OFFSETOF__Thread__m_alloc_context__alloc_limit]
        ja          LOCAL_LABEL(RhNewString_RarePath)

        mov         [rcx + OFFSETOF__Thread__m_alloc_context__alloc_ptr], rax

        // calc the new object pointer
        sub         rax, rdx

        mov         [rax + OFFSETOF__Object__m_pEEType], rbx
        mov         [rax + OFFSETOF__String__m_Length], r12d

        .cfi_remember_state
        pop_register rcx       // padding
        pop_nonvol_reg r12
        pop_nonvol_reg rbx
        ret

        .cfi_restore_state
        .cfi_def_cfa_offset 32  // workaround cfi_restore_state bug
LOCAL_LABEL(RhNewString_RarePath):
        mov         rdi, rbx    // restore MethodTable
        mov         rsi, r12    // restore element count
        // passing string size in rdx

        pop_register rcx        // padding
        pop_nonvol_reg r12
        pop_nonvol_reg rbx
        jmp C_FUNC(RhpNewArrayRare)

LOCAL_LABEL(StringSizeOverflow):
        // We get here if the size of the final string object can't be represented as an unsigned
        // 32-bit value. We're going to tail-call to a managed helper that will throw
        // an OOM exception that the caller of this allocator understands.

        // rdi holds MethodTable pointer already
        xor         esi, esi            // Indicate that we should throw OOM.
        jmp         EXTERNAL_C_FUNC(RhExceptionHandling_FailedAllocation)

NESTED_END RhNewString, _TEXT


// Allocate one dimensional, zero based array (SZARRAY).
//  RDI == MethodTable
//  ESI == element count
NESTED_ENTRY RhpNewArray, _TEXT, NoHandler
        // we want to limit the element count to the non-negative 32-bit int range
        cmp         rsi, 0x07fffffff
        ja          LOCAL_LABEL(ArraySizeOverflow)

        push_nonvol_reg rbx
        push_nonvol_reg r12
        push_register rcx       // padding

        mov         rbx, rdi    // save MethodTable
        mov         r12, rsi    // save element count

        // rax = GetThread()
        INLINE_GETTHREAD

        mov         rcx, rax    // rcx = Thread*

        // Compute overall allocation size (align(base size + (element size * elements), 8)).
        movzx       eax, word ptr [rbx + OFFSETOF__MethodTable__m_usComponentSize]
        mul         r12
        mov         edx, [rbx + OFFSETOF__MethodTable__m_uBaseSize]
        add         rax, rdx
        add         rax, 7
        and         rax, -8

        // rax == array size
        // rbx == MethodTable
        // rcx == Thread*
        // r12 == element count

        mov         rdx, rax
        add         rax, [rcx + OFFSETOF__Thread__m_alloc_context__alloc_ptr]
        jc          LOCAL_LABEL(RhpNewArray_RarePath)

        // rax == new alloc ptr
        // rbx == MethodTable
        // rcx == Thread*
        // rdx == array size
        // r12 == element count
        cmp         rax, [rcx + OFFSETOF__Thread__m_alloc_context__alloc_limit]
        ja          LOCAL_LABEL(RhpNewArray_RarePath)

        mov         [rcx + OFFSETOF__Thread__m_alloc_context__alloc_ptr], rax

        // calc the new object pointer
        sub         rax, rdx

        mov         [rax + OFFSETOF__Object__m_pEEType], rbx
        mov         [rax + OFFSETOF__Array__m_Length], r12d

        .cfi_remember_state
        pop_register rcx       // padding
        pop_nonvol_reg r12
        pop_nonvol_reg rbx
        ret

        .cfi_restore_state
        .cfi_def_cfa_offset 32  // workaround cfi_restore_state bug
LOCAL_LABEL(RhpNewArray_RarePath):
        mov         rdi, rbx    // restore MethodTable
        mov         rsi, r12    // restore element count
        // passing array size in rdx

        pop_register rcx        // padding
        pop_nonvol_reg r12
        pop_nonvol_reg rbx
        jmp C_FUNC(RhpNewArrayRare)

LOCAL_LABEL(ArraySizeOverflow):
        // We get here if the size of the final array object can't be represented as an unsigned
        // 32-bit value. We're going to tail-call to a managed helper that will throw
        // an overflow exception that the caller of this allocator understands.

        // rdi holds MethodTable pointer already
        mov         esi, 1              // Indicate that we should throw OverflowException
        jmp         EXTERNAL_C_FUNC(RhExceptionHandling_FailedAllocation)

NESTED_END RhpNewArray, _TEXT

NESTED_ENTRY RhpNewArrayRare, _TEXT, NoHandler

        // rdi == MethodTable
        // rsi == element count

        PUSH_COOP_PINVOKE_FRAME rcx
        END_PROLOGUE

        // rcx: transition frame

        // Preserve the MethodTable in RBX
        mov         rbx, rdi

        mov         rdx, rsi        // numElements

        // passing MethodTable in rdi
        xor         rsi, rsi        // uFlags
        // passing pTransitionFrame in rcx

        // Call the rest of the allocation helper.
        // void* RhpGcAlloc(MethodTable *pEEType, uint32_t uFlags, uintptr_t numElements, void * pTransitionFrame)
        call        C_FUNC(RhpGcAlloc)

        test        rax, rax
        jz          LOCAL_LABEL(ArrayOutOfMemory)

        .cfi_remember_state
        POP_COOP_PINVOKE_FRAME
        ret

        .cfi_restore_state
        .cfi_def_cfa_offset 96          // workaround cfi_restore_state bug
LOCAL_LABEL(ArrayOutOfMemory):
        // This is the OOM failure path. We're going to tail-call to a managed helper that will throw
        // an out of memory exception that the caller of this allocator understands.

        mov         rdi, rbx            // MethodTable pointer
        xor         esi, esi            // Indicate that we should throw OOM.

        POP_COOP_PINVOKE_FRAME

        jmp         EXTERNAL_C_FUNC(RhExceptionHandling_FailedAllocation)

NESTED_END RhpNewArrayRare, _TEXT
