Coverage Report

Created: 2017-04-15 07:07

/home/travis/build/MoarVM/MoarVM/src/gc/gen2.c
Line
Count
Source (jump to first uncovered line)
1
#include "moar.h"
2
3
/* Creates a new second generation allocator. */
4
130
MVMGen2Allocator * MVM_gc_gen2_create(MVMInstance *i) {
5
130
    /* Create allocator data structure. */
6
130
    MVMGen2Allocator *al = MVM_malloc(sizeof(MVMGen2Allocator));
7
130
8
130
    /* Create empty size classes array data structure. */
9
130
    al->size_classes = (MVMGen2SizeClass *)MVM_calloc(MVM_GEN2_BINS, sizeof(MVMGen2SizeClass));
10
130
11
130
    /* Set up overflows area. */
12
130
    al->alloc_overflows = MVM_GEN2_OVERFLOWS;
13
130
    al->num_overflows = 0;
14
130
    al->overflows = MVM_malloc(al->alloc_overflows * sizeof(MVMCollectable *));
15
130
16
130
    return al;
17
130
}
18
19
/* Sets up a size class bin in the second generation. */
20
1.56k
static void setup_bin(MVMGen2Allocator *al, MVMuint32 bin) {
21
1.56k
    /* Work out page size we want. */
22
1.56k
    MVMuint32 page_size = MVM_GEN2_PAGE_ITEMS * ((bin + 1) << MVM_GEN2_BIN_BITS);
23
1.56k
24
1.56k
    /* We'll just allocate a single page to start off with. */
25
1.56k
    al->size_classes[bin].num_pages = 1;
26
1.56k
    al->size_classes[bin].pages     = MVM_malloc(sizeof(void *) * al->size_classes[bin].num_pages);
27
1.56k
    al->size_classes[bin].pages[0]  = MVM_malloc(page_size);
28
1.56k
29
1.56k
    /* Set up allocation position and limit. */
30
1.56k
    al->size_classes[bin].alloc_pos = al->size_classes[bin].pages[0];
31
1.56k
    al->size_classes[bin].alloc_limit = al->size_classes[bin].alloc_pos + page_size;
32
1.56k
33
1.56k
    /* Free list is empty until GC run (and we just do page by page allocation). */
34
1.56k
    al->size_classes[bin].free_list = NULL;
35
1.56k
}
36
37
/* Adds a new page to a size class bin. */
38
15.6k
static void add_page(MVMGen2Allocator *al, MVMuint32 bin) {
39
15.6k
    /* Work out page size. */
40
15.6k
    MVMuint32 page_size = MVM_GEN2_PAGE_ITEMS * ((bin + 1) << MVM_GEN2_BIN_BITS);
41
15.6k
42
15.6k
    /* Add the extra page. */
43
15.6k
    MVMuint32 cur_page = al->size_classes[bin].num_pages;
44
15.6k
    al->size_classes[bin].num_pages++;
45
15.6k
    al->size_classes[bin].pages = MVM_realloc(al->size_classes[bin].pages,
46
15.6k
        sizeof(void *) * al->size_classes[bin].num_pages);
47
15.6k
    al->size_classes[bin].pages[cur_page] = MVM_malloc(page_size);
48
15.6k
49
15.6k
    /* Set up allocation position and limit. */
50
15.6k
    al->size_classes[bin].alloc_pos = al->size_classes[bin].pages[cur_page];
51
15.6k
    al->size_classes[bin].alloc_limit = al->size_classes[bin].alloc_pos + page_size;
52
15.6k
53
15.6k
    /* set the cur_page to a proper value */
54
15.6k
    al->size_classes[bin].cur_page = cur_page;
55
15.6k
}
56
57
/* Allocates space using the second generation allocator and returns
58
 * a pointer to the allocated space. Does not zero the space or set
59
 * it up in any way. */
60
4.20M
void * MVM_gc_gen2_allocate(MVMGen2Allocator *al, MVMuint32 size) {
61
4.20M
    void *result;
62
4.20M
63
4.20M
    /* Determine the bin. If we hit a bin exactly then it's off-by-one,
64
4.20M
     * since the bins list is base-0. Otherwise we've some extra bits,
65
4.20M
     * which round us up to the next bin, but that's a no-op. */
66
4.20M
    MVMuint32 bin = (size >> MVM_GEN2_BIN_BITS);
67
4.20M
    if ((size & MVM_GEN2_BIN_MASK) == 0)
68
4.20M
        bin--;
69
4.20M
70
4.20M
    /* If the selected bin is in range... */
71
4.20M
    if (bin < MVM_GEN2_BINS) {
72
4.20M
        /* If we've no pages yet, never encountered this bin; set it up. */
73
4.20M
        if (al->size_classes[bin].pages == NULL)
74
1.56k
            setup_bin(al, bin);
75
4.20M
76
4.20M
        /* If there's a free list entry, use that. */
77
4.20M
        if (al->size_classes[bin].free_list) {
78
0
            result = (void *)al->size_classes[bin].free_list;
79
0
            al->size_classes[bin].free_list = (char **)*(al->size_classes[bin].free_list);
80
0
        }
81
4.20M
        else {
82
4.20M
            /* If we're at the page limit, add a new page. */
83
4.20M
            if (al->size_classes[bin].alloc_pos == al->size_classes[bin].alloc_limit)
84
15.6k
                add_page(al, bin);
85
4.20M
86
4.20M
            /* Now we can allocate. */
87
4.20M
            result = al->size_classes[bin].alloc_pos;
88
4.20M
            al->size_classes[bin].alloc_pos += (bin + 1) << MVM_GEN2_BIN_BITS;
89
4.20M
        }
90
4.20M
    }
91
0
    else {
92
0
        /* We're beyond the size class bins, so resort to malloc. */
93
0
        result = MVM_malloc(size);
94
0
95
0
        /* Add to overflows list. */
96
0
        if (al->num_overflows == al->alloc_overflows) {
97
0
            al->alloc_overflows *= 2;
98
0
            al->overflows = MVM_realloc(al->overflows,
99
0
                al->alloc_overflows * sizeof(MVMCollectable *));
100
0
        }
101
0
        al->overflows[al->num_overflows++] = result;
102
0
    }
103
4.20M
104
4.20M
    return result;
105
4.20M
}
106
107
/* Allocates space using the second generation allocator and returns
108
 * a pointer to the allocated space. Promises the memory will be
109
 * zeroed, except that the MVMCollectable gen 2 flag will get set. */
110
3.63M
void * MVM_gc_gen2_allocate_zeroed(MVMGen2Allocator *al, MVMuint32 size) {
111
3.63M
    void *a = MVM_gc_gen2_allocate(al, size);
112
3.63M
    memset(a, 0, size);
113
3.63M
    ((MVMCollectable *)a)->flags = MVM_CF_SECOND_GEN;
114
3.63M
    return a;
115
3.63M
}
116
117
/* Frees all memory associated with the second generation. */
118
0
void MVM_gc_gen2_destroy(MVMInstance *i, MVMGen2Allocator *al) {
119
0
    MVMint32 j, k;
120
0
121
0
    /* Remove all pages. */
122
0
    for (j = 0; j < MVM_GEN2_BINS; j++) {
123
0
        for (k = 0; k < al->size_classes[j].num_pages; k++)
124
0
            MVM_free(al->size_classes[j].pages[k]);
125
0
        MVM_free(al->size_classes[j].pages);
126
0
    }
127
0
128
0
    /* Free any allocated overflows. */
129
0
    for (j = 0; j < al->num_overflows; j++)
130
0
        if (al->overflows[j])
131
0
            MVM_free(al->overflows[j]);
132
0
133
0
    /* Clean up allocator data structure. */
134
0
    MVM_free(al->size_classes);
135
0
    al->size_classes = NULL;
136
0
    MVM_free(al->overflows);
137
0
    al->overflows = NULL;
138
0
    MVM_free(al);
139
0
}
140
141
/* blindly move pages from one gen2 to another */
142
0
void MVM_gc_gen2_transfer(MVMThreadContext *src, MVMThreadContext *dest) {
143
0
    MVMGen2Allocator *gen2 = src->gen2, *dest_gen2 = dest->gen2;
144
0
    MVMuint32 bin, obj_size, page;
145
0
    char ***freelist_insert_pos;
146
0
147
0
    for (bin = 0; bin < MVM_GEN2_BINS; bin++) {
148
0
        MVMuint32 orig_dest_num_pages = dest_gen2->size_classes[bin].num_pages;
149
0
        char *cur_ptr, *end_ptr;
150
0
        /* If we've nothing allocated in this size class, skip it. */
151
0
        if (gen2->size_classes[bin].pages == NULL)
152
0
            continue;
153
0
154
0
        if (dest_gen2->size_classes[bin].pages == NULL)
155
0
            dest_gen2->size_classes[bin].free_list = NULL;
156
0
157
0
        /* Calculate object size for this bin. */
158
0
        obj_size = (bin + 1) << MVM_GEN2_BIN_BITS;
159
0
160
0
        /* freelist_insert_pos is a pointer to a memory location that
161
0
         * stores the address of the last traversed free list node (char **). */
162
0
        /* Initialize freelist insertion position to free list head. */
163
0
        freelist_insert_pos = &gen2->size_classes[bin].free_list;
164
0
165
0
        if (dest_gen2->size_classes[bin].pages == NULL) {
166
0
            dest_gen2->size_classes[bin].pages
167
0
                = MVM_malloc(sizeof(void *) * gen2->size_classes[bin].num_pages);
168
0
            dest_gen2->size_classes[bin].num_pages = gen2->size_classes[bin].num_pages;
169
0
        }
170
0
        else {
171
0
            dest_gen2->size_classes[bin].num_pages
172
0
                += gen2->size_classes[bin].num_pages;
173
0
            dest_gen2->size_classes[bin].pages
174
0
                = MVM_realloc(dest_gen2->size_classes[bin].pages,
175
0
                    sizeof(void *) * dest_gen2->size_classes[bin].num_pages);
176
0
        }
177
0
178
0
        /* Visit each page in the source. */
179
0
        for (page = 0; page < gen2->size_classes[bin].num_pages; page++) {
180
0
            /* Visit all the objects, looking for dead ones and swap the
181
0
             * owner for each of them. */
182
0
            cur_ptr = gen2->size_classes[bin].pages[page];
183
0
            end_ptr = page + 1 == gen2->size_classes[bin].num_pages
184
0
                ? gen2->size_classes[bin].alloc_pos
185
0
                : cur_ptr + obj_size * MVM_GEN2_PAGE_ITEMS;
186
0
            while (cur_ptr < end_ptr) {
187
0
                if (cur_ptr == (char *)freelist_insert_pos) {
188
0
                    /* skip */
189
0
                }
190
0
                else if (cur_ptr == (char *)*freelist_insert_pos) {
191
0
/*                    printf("found a free list slot in bin %d page %d: %d with value %d and start %d and limit %d\n",
192
0
                        bin, page, cur_ptr, *(void **)cur_ptr, gen2->size_classes[bin].pages[page],
193
0
                        dest_gen2->size_classes[bin].alloc_limit);*/
194
0
                    freelist_insert_pos = (char ***)cur_ptr;
195
0
                }
196
0
                else { /* note: we don't have tests that exercise this path yet. */
197
0
/*                    printf("updating an owner from %d to %d\n", ((MVMCollectable *)cur_ptr)->owner, dest->thread_id);*/
198
0
                    ((MVMCollectable *)cur_ptr)->owner = dest->thread_id;
199
0
                }
200
0
201
0
                /* Move to the next object. */
202
0
                cur_ptr += obj_size;
203
0
            }
204
0
            dest_gen2->size_classes[bin].pages[page + orig_dest_num_pages] = gen2->size_classes[bin].pages[page];
205
0
        }
206
0
207
0
        freelist_insert_pos = &dest_gen2->size_classes[bin].free_list;
208
0
        while (*freelist_insert_pos) {
209
0
            freelist_insert_pos = (char ***)*freelist_insert_pos;
210
0
        }
211
0
        /* chain the destination's freelist through any remaining unallocated area */
212
0
        if (dest_gen2->size_classes[bin].alloc_pos) {
213
0
            cur_ptr = dest_gen2->size_classes[bin].alloc_pos;
214
0
            end_ptr = dest_gen2->size_classes[bin].alloc_limit;
215
0
            while (cur_ptr < end_ptr) {
216
0
                *freelist_insert_pos = (char **)cur_ptr;
217
0
                freelist_insert_pos = (char ***)cur_ptr;
218
0
                cur_ptr += obj_size;
219
0
            }
220
0
        }
221
0
222
0
        /* link to the new pages, if any */
223
0
        *freelist_insert_pos = gen2->size_classes[bin].free_list;
224
0
225
0
        dest_gen2->size_classes[bin].alloc_pos = gen2->size_classes[bin].alloc_pos;
226
0
        dest_gen2->size_classes[bin].alloc_limit = gen2->size_classes[bin].alloc_limit;
227
0
228
0
        MVM_free(gen2->size_classes[bin].pages);
229
0
        gen2->size_classes[bin].pages = NULL;
230
0
        gen2->size_classes[bin].num_pages = 0;
231
0
    }
232
0
    { /* copy the roots... */
233
0
        MVMuint32 i, n = src->num_gen2roots;
234
0
        for ( i = 0; i < n; i++) {
235
0
            MVM_gc_root_gen2_add(dest, src->gen2roots[i]);
236
0
        }
237
0
        src->num_gen2roots = 0;
238
0
        src->alloc_gen2roots = 0;
239
0
        MVM_free(src->gen2roots);
240
0
        src->gen2roots = NULL;
241
0
    }
242
0
}
243
244
245
0
void MVM_gc_gen2_compact_overflows(MVMGen2Allocator *al) {
246
0
    /* compact the overflow list to prevent it from growing without bounds */
247
0
    MVMCollectable **overflows     = al->overflows;
248
0
    const MVMuint32  num_overflows = al->num_overflows;
249
0
    MVMuint32        cursor = 0;
250
0
    MVMuint32        live;
251
0
252
0
    /* Find the first NULL object. */
253
0
    while (cursor < num_overflows && overflows[cursor])
254
0
        cursor++;
255
0
    live = cursor;
256
0
257
0
    /* Slide others back so the alive ones are at the start of the list. */
258
0
    while (cursor < num_overflows) {
259
0
        if (overflows[cursor]) {
260
0
            overflows[live++] = overflows[cursor];
261
0
        }
262
0
        cursor++;
263
0
    }
264
0
265
0
    al->num_overflows = live;
266
0
}