/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

#if defined MN_LINUX
#define sigsetjmp   __sigsetjmp
#define CNAME(x)    x
#elif defined MN_OSX
#define sigsetjmp   sigsetjmp
#define CNAME(x)    _ ## x
#elif defined MN_FreeBSD
#define sigsetjmp   sigsetjmp
#define CNAME(x)    x
#else
#error "unsupported platform"
#endif

#if __SIZEOF_POINTER__ == 8
    .code64
#elif __SIZEOF_POINTER__ == 4
    .code32
#else
    #error "Void pointer should be either 4 or 8 bytes."
#endif
    .text
    .p2align 4, 0x90    /* align on 16-byte boundary and fill with NOPs */

    .globl CNAME(os_arch_frame_init)
    .globl _os_arch_frame_init
    /*
     * void os_arch_frame_init(struct stack_frame *sf)
     */
CNAME(os_arch_frame_init):
#if __SIZEOF_POINTER__ == 8
    pushq   %rbp                    /* function prologue for backtrace */
    movq    %rsp,%rbp

    movq    %rsp,(%rdi) /* sf->mainsp = %rsp */

    /*
     * Switch the stack so the stack pointer stored in 'sf->sf_jb' points
     * to the task stack. This is slightly complicated because OS X wants
     * the incoming stack pointer to be 16-byte aligned.
     *
     * ----------------
     * sf (other fields)
     * ----------------
     * sf (sf_jb)           0x8(%rdi)
     * ----------------
     * sf (sf_mainsp)       0x0(%rdi)
     * ----------------
     * alignment padding    variable (0 to 12 bytes)
     * ----------------
     */
    movq    %rdi,%rsp
    andq    $0xfffffffffffffff0,%rsp  /* align %rsp on 16-byte boundary */
    leaq    0x8(%rdi),%rax            /* %rax = &sf->sf_jb */
    pushq   %rdi
    movq    %rax, %rdi
    movq    $0, %rsi
    call    CNAME(sigsetjmp)          /* sigsetjmp(sf->sf_jb, 0) */
    popq    %rdi
    testq   %rax,%rax
    jne     1f
    movq    0x0(%rdi),%rsp            /* switch back to the main() stack */
    popq    %rbp
    ret                               /* return to os_arch_task_stack_init() */
1:
    leaq    2f(%rip),%rcx
    pushq   %rcx                      /* retaddr */
    pushq   $0                        /* frame pointer */
    movq    %rsp,%rbp                 /* handcrafted prologue for backtrace */
    pushq   %rax                      /* rc */
    pushq   %rsi                      /* sf */
    call    CNAME(os_arch_task_start) /* os_arch */
2:
    nop
#elif __SIZEOF_POINTER__ == 4
    push    %ebp                    /* function prologue for backtrace */
    mov     %esp,%ebp
    push    %esi                    /* save %esi before using it as a tmpreg */

    /*
     * At this point we are executing on the main() stack:
     * ----------------
     * stack_frame ptr      0xc(%esp)
     * ----------------
     * return address       0x8(%esp)
     * ----------------
     * saved ebp            0x4(%esp)
     * ----------------
     * saved esi            0x0(%esp)
     * ----------------
     */
    movl    0xc(%esp),%esi          /* %esi = 'sf' */
    movl    %esp,0x0(%esi)          /* sf->mainsp = %esp */

    /*
     * Switch the stack so the stack pointer stored in 'sf->sf_jb' points
     * to the task stack. This is slightly complicated because OS X wants
     * the incoming stack pointer to be 16-byte aligned.
     *
     * ----------------
     * sf (other fields)
     * ----------------
     * sf (sf_jb)           0x4(%esi)
     * ----------------
     * sf (sf_mainsp)       0x0(%esi)
     * ----------------
     * alignment padding    variable (0 to 12 bytes)
     * ----------------
     * savemask (0)         0x4(%esp)
     * ----------------
     * pointer to sf_jb     0x0(%esp)
     * ----------------
     */
    movl    %esi,%esp
    subl    $0x8,%esp               /* make room for sigsetjmp() arguments */
    andl    $0xfffffff0,%esp        /* align %esp on 16-byte boundary */
    leal    0x4(%esi),%eax          /* %eax = &sf->sf_jb */
    movl    %eax,0x0(%esp)
    movl    $0, 0x4(%esp)
    call    CNAME(sigsetjmp)        /* sigsetjmp(sf->sf_jb, 0) */
    test    %eax,%eax
    jne     1f
    movl    0x0(%esi),%esp          /* switch back to the main() stack */
    pop     %esi
    pop     %ebp
    ret                             /* return to os_arch_task_stack_init() */
1:
    lea     2f,%ecx
    push    %ecx                    /* retaddr */
    push    $0                      /* frame pointer */
    movl    %esp,%ebp               /* handcrafted prologue for backtrace */
    push    %eax                    /* rc */
    push    %esi                    /* sf */
    call    CNAME(os_arch_task_start) /* os_arch_task_start(sf, rc) */
    /* never returns */
2:
    nop
#endif
