1/*
2   Title:  Assembly code routines for the poly system.
3   Author:    David Matthews
4   Copyright (c) David C. J. Matthews 2000-2016
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                     56
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_SaveRAX             0x34
78#define Arg_SaveRBX             0x38
79#define Arg_SaveRCX             0x3c
80#define Arg_SaveRDX             0x40
81#define Arg_SaveRSI             0x44
82#define Arg_SaveRDI             0x48
83#define Arg_SaveFP              0x4c
84
85#define RETURN_HEAP_OVERFLOW        1
86#define RETURN_STACK_OVERFLOW       2
87#define RETURN_STACK_OVERFLOWEX     3
88#define RETURN_CALLBACK_RETURN      6
89#define RETURN_CALLBACK_EXCEPTION   7
90#define RETURN_KILL_SELF            9
91
92# Mark the stack as non-executable when supported
93#ifdef HAVE_GNU_STACK
94.section .note.GNU-stack, "", @progbits
95#endif
96
97#
98# CODE STARTS HERE
99#
100    .text
101
102#define CALL_EXTRA(index) \
103        pushl %ecx; \
104        movb  $index,Arg_ReturnReason(%ebp); \
105        popl  %ecx; \
106        jmp   SaveFullState;
107
108/* Load the registers from the ML stack and jump to the code.
109  This is used to start ML code.
110  The argument is the address of the MemRegisters struct and goes into %rbp.
111  This is the general code for switching control to ML.  There are a number of cases to consider:
112  1.  Initial entry to root function or a new thread.  Needs to load EDX at least.
113  2.  Normal return from an RTS call.  Could just do a simple return.
114  3.  Exception raised in RTS call.
115  4.  Callback from C to an ML function.  In effect this is a coroutine. Similar to 1.
116  5.  Return from "trap" i.e. Heap/Stack overflow.  Stack-overflow can result in an exception
117      either because the stack can't be grown or because Interrupt has been raised. */
118INLINE_ROUTINE(X86AsmSwitchToPoly)
119    pushl   %ebp                            # Standard entry sequence
120    movl    8(%esp),%ebp                    # Address of argument vector
121    movl    %esp,Arg_SaveCStack(%ebp)
122    pushl   %ebx
123    pushl   %edi
124    pushl   %esi                            # Push callee-save registers
125    subl    $(Fr_Size-12),%esp              # Allocate frame
126    movl    Arg_StackPtr(%ebp),%esp
127    movl    Arg_ExceptionPacket(%ebp),%eax
128    cmpl    $1,%eax                             # Did we raise an exception?
129    jnz     raisexlocal
130    FRSTOR  Arg_SaveFP(%ebp)
131    movl    Arg_SaveRAX(%ebp),%eax              # Load the registers
132    movl    Arg_SaveRBX(%ebp),%ebx              # Load the registers
133    movl    Arg_SaveRCX(%ebp),%ecx
134    movl    Arg_SaveRDX(%ebp),%edx
135    movl    Arg_SaveRSI(%ebp),%esi
136    movl    Arg_SaveRDI(%ebp),%edi
137    cld                                     # Clear this just in case
138    ret
139
140
141/* Code to save the state and switch to C
142   This saves the full register state. */
143SaveFullState:
144    movl    %eax,Arg_SaveRAX(%ebp)
145    movl    %ebx,Arg_SaveRBX(%ebp)
146    movl    %ecx,Arg_SaveRCX(%ebp)
147    movl    %edx,Arg_SaveRDX(%ebp)
148    movl    %esi,Arg_SaveRSI(%ebp)
149    movl    %edi,Arg_SaveRDI(%ebp)
150    fnsave  Arg_SaveFP(%ebp)                # Save FP state.  Also resets the state so...
151    fldcw   Arg_SaveFP(%ebp)                # ...load because we need the same rounding mode in the RTS
152    movl    %esp,Arg_StackPtr(%ebp)         # Save ML stack pointer
153    movl    Arg_SaveCStack(%ebp),%esp       # Restore C stack pointer
154    subl    $12,%esp                        # Sp is just before the registers
155    popl    %esi
156    popl    %edi
157    popl    %ebx
158    popl    %ebp
159    ret
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/* Used when entering new code.  The argument and closure are on the stack
171   in case there is a GC before we enter the code. */
172INLINE_ROUTINE(X86AsmPopArgAndClosure)
173    popl    %edx
174    popl    %eax
175    jmp     *(%edx)
176
177INLINE_ROUTINE(X86AsmRaiseException)
178raisexlocal:
179    movl    Arg_HandlerRegister(%ebp),%ecx    # Get next handler into %rcx
180    jmp     *(%ecx)
181
182# Additional assembly code routines
183
184# RTS call to kill the current thread.
185INLINE_ROUTINE(X86AsmKillSelf)
186    CALL_EXTRA(RETURN_KILL_SELF)
187
188INLINE_ROUTINE(X86AsmCallbackReturn)
189    CALL_EXTRA(RETURN_CALLBACK_RETURN)
190
191INLINE_ROUTINE(X86AsmCallbackException)
192    CALL_EXTRA(RETURN_CALLBACK_EXCEPTION)
193
194# This implements atomic addition in the same way as atomic_increment
195INLINE_ROUTINE(X86AsmAtomicIncrement)
196#ifndef HOSTARCHITECTURE_X86_64
197    movl    4(%esp),%eax
198#else
199    movl    %edi,%eax   # On X86_64 the argument is passed in %edi
200#endif
201# Use %ecx and %eax because they are volatile (unlike %ebx on X86/64/Unix)
202    movl    $2,%ecx
203    lock; xaddl %ecx,(%eax)
204    addl    $2,%ecx
205    movl    %ecx,%eax
206    ret
207
208