1/*
2   Title:  Assembly code routines for the poly system.
3   Author:    David Matthews
4   Copyright (c) David C. J. Matthews 2000-2020
5
6   This library is free software; you can redistribute it and/or
7   modify it under the terms of the GNU Lesser General Public
8   License version 2.1 as published by the Free Software Foundation.
9
10   This library is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13   Lesser General Public License for more details.
14
15   You should have received a copy of the GNU Lesser General Public
16   License along with this library; if not, write to the Free Software
17   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18*/
19
20/*
21   This is the 32-bit Unix version of the assembly code file.
22   There are separate versions of 32/64 and Windows (Intel syntax)
23   and Unix (gas syntax).
24*/
25
26/*
27 Registers used :-
28
29  %%eax: First argument to function.  Result of function call.
30  %%ebx: Second argument to function.
31  %%ecx: General register
32  %%edx: Closure pointer in call.
33  %%ebp: Points to memory used for extra registers
34  %%esi: General register.
35  %%edi: General register.
36  %%esp: Stack pointer.
37*/
38
39
40#include "config.h"
41#ifdef SYMBOLS_REQUIRE_UNDERSCORE
42#define EXTNAME(x)  _##x
43#else
44#define EXTNAME(x)  x
45#endif
46
47#
48# Macro to begin the hand-coded functions
49#
50
51#ifdef MACOSX
52#define GLOBAL .globl
53#else
54#define GLOBAL .global
55#endif
56
57#define INLINE_ROUTINE(id) \
58GLOBAL EXTNAME(id); \
59EXTNAME(id):
60
61#define Fr_Size                     16
62
63/* This is the argument vector passed in to X86AsmSwitchToPoly
64   It is used to initialise the frame.  A few values are updated
65   when ML returns. */
66#define Arg_LocalMpointer       0x0
67#define Arg_HandlerRegister     0x4
68#define Arg_LocalMbottom        0x8
69#define Arg_StackLimit          0xc
70#define Arg_ExceptionPacket     0x10  /* Address of packet to raise */
71#define Arg_RequestCode         0x14 /* Byte: Io function to call. */
72#define Arg_ReturnReason        0x16  /* Byte: Reason for returning from ML. */
73#define Arg_FullRestore         0x17  /* Byte: Full/partial restore */
74#define Arg_SaveCStack          0x18  /* Save C Stack pointer */
75#define Arg_ThreadId            0x1c  /* My thread id */
76#define Arg_StackPtr            0x20  /* Stack Pointer */
77#define Arg_TrapHandlerEntry    0x34
78#define Arg_SaveRAX             0x38
79#define Arg_SaveRBX             0x3c
80#define Arg_SaveRCX             0x40
81#define Arg_SaveRDX             0x44
82#define Arg_SaveRSI             0x48
83#define Arg_SaveRDI             0x4c
84#define Arg_SaveFP              0x50
85
86#define RETURN_HEAP_OVERFLOW        1
87#define RETURN_STACK_OVERFLOW       2
88#define RETURN_STACK_OVERFLOWEX     3
89
90# Mark the stack as non-executable when supported
91#if (defined(__linux__) && defined(__ELF__))
92.section .note.GNU-stack, "", @progbits
93#endif
94
95#
96# CODE STARTS HERE
97#
98    .text
99
100#define CALL_EXTRA(index) \
101        movb  $index,Arg_ReturnReason(%ebp); \
102        jmp   CallTrapHandler;
103
104/*  Enter ML code.  This is now only ever used to start a new thread.
105    It is probably unnecessary to save the callee-save regs or load the ML regs. */
106INLINE_ROUTINE(X86AsmSwitchToPoly)
107    pushl   %ebp                            # Standard entry sequence
108    movl    8(%esp),%ebp                    # Address of argument vector
109    pushl   %ebx
110    pushl   %edi
111    pushl   %esi                            # Push callee-save registers
112    subl    $(Fr_Size-12),%esp              # Allocate frame
113    movl    %esp,Arg_SaveCStack(%ebp)
114    movl    Arg_StackPtr(%ebp),%esp
115    FRSTOR  Arg_SaveFP(%ebp)
116    movl    Arg_SaveRAX(%ebp),%eax              # Load the registers
117    movl    Arg_SaveRBX(%ebp),%ebx              # Load the registers
118    movl    Arg_SaveRCX(%ebp),%ecx
119    movl    Arg_SaveRDX(%ebp),%edx
120    movl    Arg_SaveRSI(%ebp),%esi
121    movl    Arg_SaveRDI(%ebp),%edi
122    cld                                     # Clear this just in case
123    jmp     *(%edx)
124
125
126/* Save all the registers and enter the trap handler.
127   It is probably unnecessary to save the FP state now. */
128CallTrapHandler:
129    movl    %eax,Arg_SaveRAX(%ebp)
130    movl    %ebx,Arg_SaveRBX(%ebp)
131    movl    %ecx,Arg_SaveRCX(%ebp)
132    movl    %edx,Arg_SaveRDX(%ebp)
133    movl    %esi,Arg_SaveRSI(%ebp)
134    movl    %edi,Arg_SaveRDI(%ebp)
135    fnsave  Arg_SaveFP(%ebp)                # Save FP state.  Also resets the state so...
136    fldcw   Arg_SaveFP(%ebp)                # ...load because we need the same rounding mode in the RTS
137    movl    %esp,Arg_StackPtr(%ebp)         # Save ML stack pointer
138    movl    Arg_SaveCStack(%ebp),%esp       # Restore C stack pointer
139    subl    $12,%esp                        # Align stack ptr - GCC prefers this
140    pushl   Arg_ThreadId(%ebp)
141    calll   *Arg_TrapHandlerEntry(%ebp)
142    addl    $16,%esp
143    movl    Arg_StackPtr(%ebp),%esp
144    movl    Arg_ExceptionPacket(%ebp),%eax
145    cmpl    $1,%eax                             # Did we raise an exception?
146    jnz     raisexlocal
147    FRSTOR  Arg_SaveFP(%ebp)
148    movl    Arg_SaveRAX(%ebp),%eax              # Load the registers
149    movl    Arg_SaveRBX(%ebp),%ebx              # Load the registers
150    movl    Arg_SaveRCX(%ebp),%ecx
151    movl    Arg_SaveRDX(%ebp),%edx
152    movl    Arg_SaveRSI(%ebp),%esi
153    movl    Arg_SaveRDI(%ebp),%edi
154    cld                                     # Clear this just in case
155    ret
156
157raisexlocal:
158    movl    Arg_HandlerRegister(%ebp),%ecx    # Get next handler into %rcx
159    jmp     *(%ecx)
160
161INLINE_ROUTINE(X86AsmCallExtraRETURN_HEAP_OVERFLOW)
162    CALL_EXTRA(RETURN_HEAP_OVERFLOW)
163
164INLINE_ROUTINE(X86AsmCallExtraRETURN_STACK_OVERFLOW)
165    CALL_EXTRA(RETURN_STACK_OVERFLOW)
166
167INLINE_ROUTINE(X86AsmCallExtraRETURN_STACK_OVERFLOWEX)
168    CALL_EXTRA(RETURN_STACK_OVERFLOWEX)
169
170# Additional assembly code routines
171
172# This implements atomic addition in the same way as atomic_increment
173INLINE_ROUTINE(X86AsmAtomicDecrement)
174#ifndef HOSTARCHITECTURE_X86_64
175    movl    4(%esp),%eax
176#else
177    movl    %edi,%eax   # On X86_64 the argument is passed in %edi
178#endif
179# Use %ecx and %eax because they are volatile (unlike %ebx on X86/64/Unix)
180    movl    $-2,%ecx
181    lock; xaddl %ecx,(%eax)
182    subl    $2,%ecx
183    movl    %ecx,%eax
184    ret
185
186