summaryrefslogtreecommitdiff
path: root/rts/adjustor/NativeAmd64Mingw.c
blob: 5acde07b1f997f4b8861e51c7cb4e06fee452096 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
/* -----------------------------------------------------------------------------
 * AMD64/Windows architecture adjustor thunk logic.
 * ---------------------------------------------------------------------------*/

#include "rts/PosixSource.h"
#include "Rts.h"

#include "RtsUtils.h"
#include "StablePtr.h"
#include "Adjustor.h"
#include "AdjustorPool.h"

#define DECLARE_ADJUSTOR_TEMPLATE(NAME) \
    extern uint8_t NAME ## _adjustor; \
    extern uint8_t NAME ## _adjustor_context; \
    extern uint8_t NAME ## _adjustor_end; \
    const struct AdjustorTemplate NAME ## _adjustor_template = { \
        .code_start = (uint8_t *) &NAME ## _adjustor, \
        .code_end = (uint8_t *) &NAME ## _adjustor_end, \
        .context_ptr = (const struct AdjustorContext **) &NAME ## _adjustor_context, \
    };

/* adjustors to handle calls with less than 6 integer arguments */
DECLARE_ADJUSTOR_TEMPLATE(simple_ccall);
static struct AdjustorPool *simple_ccall_pool;

/* adjustors to handle calls with 6 or more integer arguments where the fourth
 * argument is a float */
DECLARE_ADJUSTOR_TEMPLATE(complex_float_ccall);
static struct AdjustorPool *complex_float_ccall_pool;

/* adjustors to handle calls with 6 or more integer arguments where the fourth
 * argument is not a float */
DECLARE_ADJUSTOR_TEMPLATE(complex_nofloat_ccall);
static struct AdjustorPool *complex_nofloat_ccall_pool;

void initAdjustors()
{
    simple_ccall_pool = new_adjustor_pool_from_template(&simple_ccall_adjustor_template);
    complex_float_ccall_pool = new_adjustor_pool_from_template(&complex_float_ccall_adjustor_template);
    complex_nofloat_ccall_pool = new_adjustor_pool_from_template(&complex_nofloat_ccall_adjustor_template);
}

void*
createAdjustor(int cconv, StgStablePtr hptr,
               StgFunPtr wptr,
               char *typeString
    )
{
    struct AdjustorContext context = {
        .hptr = hptr,
        .wptr = wptr,
    };

    switch (cconv)
    {
    case 1: /* _ccall */
    /*
      stack at call:
               argn
               ...
               arg5
               return address
               %rcx,%rdx,%r8,%r9 = arg1..arg4

      if there are <4 integer args, then we can just push the
      StablePtr into %rcx and shuffle the other args up.

      If there are >=4 integer args, then we have to flush one arg
      to the stack, and arrange to adjust the stack ptr on return.
      The stack will be rearranged to this:

             argn
             ...
             arg5
             return address  *** <-- dummy arg in stub fn.
             arg4
             obscure_ccall_ret_code

      This unfortunately means that the type of the stub function
      must have a dummy argument for the original return address
      pointer inserted just after the 4th integer argument.

      See NativeAmd64MingwAsm.S.
    */
    {
        // determine whether we have 4 or more integer arguments,
        // and therefore need to flush one to the stack.
        if ((typeString[0] == '\0') ||
            (typeString[1] == '\0') ||
            (typeString[2] == '\0') ||
            (typeString[3] == '\0'))
        {
            return alloc_adjustor(simple_ccall_pool, context);
        }
        else
        {
            bool fourthFloating = (typeString[3] == 'f' || typeString[3] == 'd');
            if (fourthFloating) {
                return alloc_adjustor(complex_float_ccall_pool, context);
            } else {
                return alloc_adjustor(complex_nofloat_ccall_pool, context);
            }
        }
    }

    default:
        barf("createAdjustor: Unsupported calling convention");
        break;
    }
}

void freeHaskellFunctionPtr(void* ptr)
{
    struct AdjustorContext context = free_adjustor(ptr);
    freeStablePtr(context.hptr);
}