summaryrefslogtreecommitdiff
path: root/rts/adjustor/NativeIA64.c
blob: f9556249453d0bf3b18dd9e5d9af87bfa05b6e47 (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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
/* -----------------------------------------------------------------------------
 * IA64 architecture adjustor thunk logic.
 * ---------------------------------------------------------------------------*/

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

#include "RtsUtils.h"
#include "StablePtr.h"

/* Layout of a function descriptor */
typedef struct _IA64FunDesc {
    StgWord64 ip;
    StgWord64 gp;
} IA64FunDesc;

static void *
stgAllocStable(size_t size_in_bytes, StgStablePtr *stable)
{
    StgArrBytes* arr;
    uint32_t data_size_in_words, total_size_in_words;

    /* round up to a whole number of words */
    data_size_in_words  = ROUNDUP_BYTES_TO_WDS(size_in_bytes);
    total_size_in_words = sizeofW(StgArrBytes) + data_size_in_words;

    /* allocate and fill it in */
    arr = (StgArrBytes *)allocate(total_size_in_words);
    SET_ARR_HDR(arr, &stg_ARR_WORDS_info, CCCS, size_in_bytes);

    /* obtain a stable ptr */
    *stable = getStablePtr((StgPtr)arr);

    /* and return a ptr to the goods inside the array */
    return(&(arr->payload));
}

void initAdjustors() { }

void*
createAdjustor(int cconv, StgStablePtr hptr,
               StgFunPtr wptr,
               char *typeString
#if !defined(powerpc_HOST_ARCH) && !defined(powerpc64_HOST_ARCH) && !defined(x86_64_HOST_ARCH)
               STG_UNUSED
#endif
    )
{
    void *adjustor = NULL;
    void *code = NULL;

    switch (cconv)
    {
    case 1: /* _ccall */
/*
    Up to 8 inputs are passed in registers.  We flush the last two inputs to
    the stack, initially into the 16-byte scratch region left by the caller.
    We then shuffle the others along by 4 (taking 2 registers for ourselves
    to save return address and previous function state - we need to come back
    here on the way out to restore the stack, so this is a real function
    rather than just a trampoline).

    The function descriptor we create contains the gp of the target function
    so gp is already loaded correctly.

        [MLX]       alloc r16=ar.pfs,10,2,0
                    movl r17=wptr
        [MII]       st8.spill [r12]=r38,8               // spill in6 (out4)
                    mov r41=r37                         // out7 = in5 (out3)
                    mov r40=r36;;                       // out6 = in4 (out2)
        [MII]       st8.spill [r12]=r39                 // spill in7 (out5)
                    mov.sptk b6=r17,50
                    mov r38=r34;;                       // out4 = in2 (out0)
        [MII]       mov r39=r35                         // out5 = in3 (out1)
                    mov r37=r33                         // out3 = in1 (loc1)
                    mov r36=r32                         // out2 = in0 (loc0)
        [MLX]       adds r12=-24,r12                    // update sp
                    movl r34=hptr;;                     // out0 = hptr
        [MIB]       mov r33=r16                         // loc1 = ar.pfs
                    mov r32=b0                          // loc0 = retaddr
                    br.call.sptk.many b0=b6;;

        [MII]       adds r12=-16,r12
                    mov b0=r32
                    mov.i ar.pfs=r33
        [MFB]       nop.m 0x0
                    nop.f 0x0
                    br.ret.sptk.many b0;;
*/

/* These macros distribute a long constant into the two words of an MLX bundle */
#define BITS(val,start,count)   (((val) >> (start)) & ((1 << (count))-1))
#define MOVL_LOWORD(val)        (BITS(val,22,18) << 46)
#define MOVL_HIWORD(val)        ( (BITS(val,0,7)    << 36)      \
                                | (BITS(val,7,9)    << 50)      \
                                | (BITS(val,16,5)   << 45)      \
                                | (BITS(val,21,1)   << 44)      \
                                | (BITS(val,40,23))             \
                                | (BITS(val,63,1)    << 59))

    {
        StgStablePtr stable;
        IA64FunDesc *wdesc = (IA64FunDesc *)wptr;
        StgWord64 wcode = wdesc->ip;
        IA64FunDesc *fdesc;
        StgWord64 *code;

        /* we allocate on the Haskell heap since malloc'd memory isn't
         * executable - argh */
        /* Allocated memory is word-aligned (8 bytes) but functions on ia64
         * must be aligned to 16 bytes.  We allocate an extra 8 bytes of
         * wiggle room so that we can put the code on a 16 byte boundary. */
        adjustor = stgAllocStable(sizeof(IA64FunDesc)+18*8+8, &stable);

        fdesc = (IA64FunDesc *)adjustor;
        code = (StgWord64 *)(fdesc + 1);
        /* add 8 bytes to code if needed to align to a 16-byte boundary */
        if ((StgWord64)code & 15) code++;
        fdesc->ip = (StgWord64)code;
        fdesc->gp = wdesc->gp;

        code[0]  = 0x0000058004288004 | MOVL_LOWORD(wcode);
        code[1]  = 0x6000000220000000 | MOVL_HIWORD(wcode);
        code[2]  = 0x029015d818984001;
        code[3]  = 0x8401200500420094;
        code[4]  = 0x886011d8189c0001;
        code[5]  = 0x84011004c00380c0;
        code[6]  = 0x0250210046013800;
        code[7]  = 0x8401000480420084;
        code[8]  = 0x0000233f19a06005 | MOVL_LOWORD((StgWord64)hptr);
        code[9]  = 0x6000000440000000 | MOVL_HIWORD((StgWord64)hptr);
        code[10] = 0x0200210020010811;
        code[11] = 0x1080006800006200;
        code[12] = 0x0000210018406000;
        code[13] = 0x00aa021000038005;
        code[14] = 0x000000010000001d;
        code[15] = 0x0084000880000200;

        /* save stable pointers in convenient form */
        code[16] = (StgWord64)hptr;
        code[17] = (StgWord64)stable;
    }
#else
    barf("adjustor creation not supported on this platform");
#endif
    break;

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

    return code;
}

void
freeHaskellFunctionPtr(void* ptr)
{
    IA64FunDesc *fdesc = (IA64FunDesc *)ptr;
    StgWord64 *code = (StgWord64 *)(fdesc+1);

    if (fdesc->ip != (StgWord64)code) {
        errorBelch("freeHaskellFunctionPtr: not for me, guv! %p\n", ptr);
        return;
    }
    freeStablePtr((StgStablePtr)code[16]);
    freeStablePtr((StgStablePtr)code[17]);
}