Coverage Report

Created: 2018-07-03 15:31

/home/travis/build/MoarVM/MoarVM/src/6model/reprs/MVMHash.c
Line
Count
Source (jump to first uncovered line)
1
#include "moar.h"
2
3
/* This representation's function pointer table. */
4
static const MVMREPROps MVMHash_this_repr;
5
6
89.5M
MVM_STATIC_INLINE MVMString * get_string_key(MVMThreadContext *tc, MVMObject *key) {
7
89.5M
    if (!key || REPR(key)->ID != MVM_REPR_ID_MVMString || !IS_CONCRETE(key))
8
0
        MVM_exception_throw_adhoc(tc, "MVMHash representation requires MVMString keys");
9
89.5M
    return (MVMString *)key;
10
89.5M
}
11
12
/* Creates a new type object of this representation, and associates it with
13
 * the given HOW. */
14
146
static MVMObject * type_object_for(MVMThreadContext *tc, MVMObject *HOW) {
15
146
    MVMSTable *st = MVM_gc_allocate_stable(tc, &MVMHash_this_repr, HOW);
16
146
17
146
    MVMROOT(tc, st, {
18
146
        MVMObject *obj = MVM_gc_allocate_type_object(tc, st);
19
146
        MVM_ASSIGN_REF(tc, &(st->header), st->WHAT, obj);
20
146
        st->size = sizeof(MVMHash);
21
146
    });
22
146
23
146
    return st->WHAT;
24
146
}
25
26
/* Copies the body of one object to another. */
27
3.51k
static void copy_to(MVMThreadContext *tc, MVMSTable *st, void *src, MVMObject *dest_root, void *dest) {
28
3.51k
    MVMHashBody *src_body  = (MVMHashBody *)src;
29
3.51k
    MVMHashBody *dest_body = (MVMHashBody *)dest;
30
3.51k
    MVMHashEntry *current  = NULL, *tmp = NULL;
31
3.51k
    unsigned bucket_tmp;
32
3.51k
33
3.51k
    /* NOTE: if we really wanted to, we could avoid rehashing... */
34
6.53k
    HASH_ITER(hash_handle, src_body->hash_head, current, tmp, bucket_tmp) {
35
6.53k
        MVMHashEntry *new_entry = MVM_fixed_size_alloc(tc, tc->instance->fsa,
36
6.53k
            sizeof(MVMHashEntry));
37
6.53k
        MVMString *key = MVM_HASH_KEY(current);
38
6.53k
        MVM_ASSIGN_REF(tc, &(dest_root->header), new_entry->value, current->value);
39
6.53k
        MVM_HASH_BIND(tc, dest_body->hash_head, key, new_entry);
40
6.53k
        MVM_gc_write_barrier(tc, &(dest_root->header), &(key->common.header));
41
6.53k
    }
42
3.51k
}
43
44
/* Adds held objects to the GC worklist. */
45
707k
static void gc_mark(MVMThreadContext *tc, MVMSTable *st, void *data, MVMGCWorklist *worklist) {
46
707k
    MVMHashBody     *body = (MVMHashBody *)data;
47
707k
    MVMHashEntry *current = NULL, *tmp = NULL;
48
707k
    unsigned bucket_tmp;
49
707k
50
1.09M
    HASH_ITER(hash_handle, body->hash_head, current, tmp, bucket_tmp) {
51
1.09M
        MVM_gc_worklist_add(tc, worklist, &current->hash_handle.key);
52
1.09M
        MVM_gc_worklist_add(tc, worklist, &current->value);
53
1.09M
    }
54
707k
}
55
56
/* Called by the VM in order to free memory associated with this object. */
57
3.41M
static void gc_free(MVMThreadContext *tc, MVMObject *obj) {
58
3.41M
    MVMHash *h = (MVMHash *)obj;
59
3.41M
    MVMHashEntry *current = NULL, *tmp = NULL;
60
3.41M
    unsigned bucket_tmp;
61
3.41M
    HASH_ITER(hash_handle, h->body.hash_head, current, tmp, bucket_tmp) {
62
287k
        if (current != h->body.hash_head)
63
112k
            MVM_fixed_size_free(tc, tc->instance->fsa, sizeof(MVMHashEntry), current);
64
287k
    }
65
3.41M
    tmp = h->body.hash_head;
66
3.41M
    HASH_CLEAR(tc, hash_handle, h->body.hash_head);
67
3.41M
    if (tmp)
68
174k
        MVM_fixed_size_free(tc, tc->instance->fsa, sizeof(MVMHashEntry), tmp);
69
3.41M
}
70
71
17.2M
static void at_key(MVMThreadContext *tc, MVMSTable *st, MVMObject *root, void *data, MVMObject *key_obj, MVMRegister *result, MVMuint16 kind) {
72
17.2M
    MVMHashBody   *body = (MVMHashBody *)data;
73
17.2M
    MVMHashEntry *entry = NULL;
74
17.2M
    MVM_HASH_GET(tc, body->hash_head, get_string_key(tc, key_obj), entry);
75
17.2M
    if (kind == MVM_reg_obj)
76
17.2M
        result->o = entry != NULL ? entry->value : tc->instance->VMNull;
77
17.2M
    else
78
18.4E
        MVM_exception_throw_adhoc(tc,
79
18.4E
            "MVMHash representation does not support native type storage");
80
17.2M
}
81
12.7M
void MVMHash_at_key(MVMThreadContext *tc, MVMSTable *st, MVMObject *root, void *data, MVMObject *key_obj, MVMRegister *result, MVMuint16 kind) {
82
12.7M
    return at_key(tc, st, root, data, key_obj, result, kind);
83
12.7M
}
84
85
2.12M
static void bind_key(MVMThreadContext *tc, MVMSTable *st, MVMObject *root, void *data, MVMObject *key_obj, MVMRegister value, MVMuint16 kind) {
86
2.12M
    MVMHashBody   *body = (MVMHashBody *)data;
87
2.12M
    MVMHashEntry *entry = NULL;
88
2.12M
89
2.12M
    MVMString *key = get_string_key(tc, key_obj);
90
2.12M
    if (MVM_UNLIKELY(kind != MVM_reg_obj))
91
0
        MVM_exception_throw_adhoc(tc,
92
0
            "MVMHash representation does not support native type storage");
93
2.12M
94
2.12M
    /* first check whether we can must update the old entry. */
95
2.12M
    MVM_HASH_GET(tc, body->hash_head, key, entry);
96
2.12M
    if (!entry) {
97
2.07M
        entry = MVM_fixed_size_alloc(tc, tc->instance->fsa,
98
2.07M
            sizeof(MVMHashEntry));
99
2.07M
        MVM_ASSIGN_REF(tc, &(root->header), entry->value, value.o);
100
2.07M
        MVM_HASH_BIND(tc, body->hash_head, key, entry);
101
2.07M
        MVM_gc_write_barrier(tc, &(root->header), &(key->common.header));
102
2.07M
    }
103
46.1k
    else {
104
46.1k
        MVM_ASSIGN_REF(tc, &(root->header), entry->value, value.o);
105
46.1k
    }
106
2.12M
}
107
815k
void MVMHash_bind_key(MVMThreadContext *tc, MVMSTable *st, MVMObject *root, void *data, MVMObject *key_obj, MVMRegister value, MVMuint16 kind) {
108
815k
    return bind_key(tc, st, root, data, key_obj, value, kind);
109
815k
}
110
946k
static MVMuint64 elems(MVMThreadContext *tc, MVMSTable *st, MVMObject *root, void *data) {
111
946k
    MVMHashBody *body = (MVMHashBody *)data;
112
946k
    return HASH_CNT(hash_handle, body->hash_head);
113
946k
}
114
115
576k
static MVMint64 exists_key(MVMThreadContext *tc, MVMSTable *st, MVMObject *root, void *data, MVMObject *key_obj) {
116
576k
    MVMHashBody   *body = (MVMHashBody *)data;
117
576k
    MVMHashEntry *entry = NULL;
118
576k
    MVM_HASH_GET(tc, body->hash_head, get_string_key(tc, key_obj), entry);
119
576k
    return entry != NULL;
120
576k
}
121
122
154k
static void delete_key(MVMThreadContext *tc, MVMSTable *st, MVMObject *root, void *data, MVMObject *key_obj) {
123
154k
    MVMHashBody *body = (MVMHashBody *)data;
124
154k
    MVMString *key = get_string_key(tc, key_obj);
125
154k
    MVMHashEntry *old_entry = NULL;
126
154k
    MVM_HASH_GET(tc, body->hash_head, key, old_entry);
127
154k
    if (old_entry) {
128
152k
        HASH_DELETE(hash_handle, body->hash_head, old_entry);
129
152k
        MVM_fixed_size_free(tc, tc->instance->fsa,
130
152k
            sizeof(MVMHashEntry), old_entry);
131
152k
    }
132
154k
}
133
134
0
static MVMStorageSpec get_value_storage_spec(MVMThreadContext *tc, MVMSTable *st) {
135
0
    MVMStorageSpec spec;
136
0
    spec.inlineable      = MVM_STORAGE_SPEC_REFERENCE;
137
0
    spec.boxed_primitive = MVM_STORAGE_SPEC_BP_NONE;
138
0
    spec.can_box         = 0;
139
0
    spec.bits            = 0;
140
0
    spec.align           = 0;
141
0
    spec.is_unsigned     = 0;
142
0
    return spec;
143
0
}
144
145
static const MVMStorageSpec storage_spec = {
146
    MVM_STORAGE_SPEC_REFERENCE, /* inlineable */
147
    0,                          /* bits */
148
    0,                          /* align */
149
    MVM_STORAGE_SPEC_BP_NONE,   /* boxed_primitive */
150
    0,                          /* can_box */
151
    0,                          /* is_unsigned */
152
};
153
154
/* Gets the storage specification for this representation. */
155
8.50k
static const MVMStorageSpec * get_storage_spec(MVMThreadContext *tc, MVMSTable *st) {
156
8.50k
    return &storage_spec;
157
8.50k
}
158
159
/* Compose the representation. */
160
2
static void compose(MVMThreadContext *tc, MVMSTable *st, MVMObject *info) {
161
2
    /* XXX key and value types will be communicated here */
162
2
}
163
164
/* Deserialize the representation. */
165
1
static void deserialize(MVMThreadContext *tc, MVMSTable *st, MVMObject *root, void *data, MVMSerializationReader *reader) {
166
1
    MVMHashBody *body = (MVMHashBody *)data;
167
1
    MVMint64 elems = MVM_serialization_read_int(tc, reader);
168
1
    MVMint64 i;
169
4
    for (i = 0; i < elems; i++) {
170
3
        MVMString *key = MVM_serialization_read_str(tc, reader);
171
3
        MVMObject *value = MVM_serialization_read_ref(tc, reader);
172
3
        MVMHashEntry *entry = MVM_fixed_size_alloc(tc, tc->instance->fsa,
173
3
            sizeof(MVMHashEntry));
174
3
        MVM_ASSIGN_REF(tc, &(root->header), entry->value, value);
175
3
        MVM_HASH_BIND(tc, body->hash_head, key, entry);
176
3
    }
177
1
}
178
179
/* Serialize the representation. */
180
MVMThreadContext *cmp_tc;
181
2
static int cmp_strings(const void *s1, const void *s2) {
182
2
    return MVM_string_compare(cmp_tc, *(MVMString **)s1, *(MVMString **)s2);
183
2
}
184
1
static void serialize(MVMThreadContext *tc, MVMSTable *st, void *data, MVMSerializationWriter *writer) {
185
1
    MVMHashBody *body = (MVMHashBody *)data;
186
1
    MVMHashEntry *current = NULL, *tmp = NULL;
187
1
    MVMuint64 elems = HASH_CNT(hash_handle, body->hash_head);
188
1
    MVMString **keys = MVM_malloc(sizeof(MVMString *) * elems);
189
1
    unsigned bucket_tmp;
190
1
    MVMuint64 i = 0;
191
1
    MVM_serialization_write_int(tc, writer, elems);
192
3
    HASH_ITER(hash_handle, body->hash_head, current, tmp, bucket_tmp) {
193
3
        keys[i++] = MVM_HASH_KEY(current);
194
3
    }
195
1
    cmp_tc = tc;
196
1
    qsort(keys, elems, sizeof(MVMString*), cmp_strings);
197
4
    for (i = 0; i < elems; i++) {
198
3
        MVMHashEntry *entry;
199
3
        MVM_HASH_GET(tc, body->hash_head, keys[i], entry);
200
3
        MVM_serialization_write_str(tc, writer, keys[i]);
201
3
        MVM_serialization_write_ref(tc, writer, entry->value);
202
3
    }
203
1
    MVM_free(keys);
204
1
}
205
206
/* Set the size of the STable. */
207
0
static void deserialize_stable_size(MVMThreadContext *tc, MVMSTable *st, MVMSerializationReader *reader) {
208
0
    st->size = sizeof(MVMHash);
209
0
}
210
211
/* Bytecode specialization for this REPR. */
212
4.78k
static void spesh(MVMThreadContext *tc, MVMSTable *st, MVMSpeshGraph *g, MVMSpeshBB *bb, MVMSpeshIns *ins) {
213
4.78k
    switch (ins->info->opcode) {
214
1.28k
    case MVM_OP_create: {
215
1.28k
        if (!(st->mode_flags & MVM_FINALIZE_TYPE)) {
216
1.28k
            MVMSpeshOperand target   = ins->operands[0];
217
1.28k
            MVMSpeshOperand type     = ins->operands[1];
218
1.28k
            ins->info                = MVM_op_get_op(MVM_OP_sp_fastcreate);
219
1.28k
            ins->operands            = MVM_spesh_alloc(tc, g, 3 * sizeof(MVMSpeshOperand));
220
1.28k
            ins->operands[0]         = target;
221
1.28k
            ins->operands[1].lit_i16 = sizeof(MVMHash);
222
1.28k
            ins->operands[2].lit_i16 = MVM_spesh_add_spesh_slot(tc, g, (MVMCollectable *)st);
223
1.28k
            MVM_spesh_get_facts(tc, g, type)->usages--;
224
1.28k
        }
225
1.28k
        break;
226
1.28k
    }
227
4.78k
    }
228
4.78k
}
229
230
237k
static MVMuint64 unmanaged_size(MVMThreadContext *tc, MVMSTable *st, void *data) {
231
237k
    MVMHashBody *body = (MVMHashBody *)data;
232
237k
233
237k
    return sizeof(MVMHashEntry) * HASH_CNT(hash_handle, body->hash_head);
234
237k
}
235
236
/* Initializes the representation. */
237
144
const MVMREPROps * MVMHash_initialize(MVMThreadContext *tc) {
238
144
    return &MVMHash_this_repr;
239
144
}
240
241
static const MVMREPROps MVMHash_this_repr = {
242
    type_object_for,
243
    MVM_gc_allocate_object,
244
    NULL, /* initialize */
245
    copy_to,
246
    MVM_REPR_DEFAULT_ATTR_FUNCS,
247
    MVM_REPR_DEFAULT_BOX_FUNCS,
248
    MVM_REPR_DEFAULT_POS_FUNCS,
249
    {
250
        at_key,
251
        bind_key,
252
        exists_key,
253
        delete_key,
254
        get_value_storage_spec
255
    },    /* ass_funcs */
256
    elems,
257
    get_storage_spec,
258
    NULL, /* change_type */
259
    serialize,
260
    deserialize,
261
    NULL, /* serialize_repr_data */
262
    NULL, /* deserialize_repr_data */
263
    deserialize_stable_size,
264
    gc_mark,
265
    gc_free,
266
    NULL, /* gc_cleanup */
267
    NULL, /* gc_mark_repr_data */
268
    NULL, /* gc_free_repr_data */
269
    compose,
270
    spesh,
271
    "VMHash", /* name */
272
    MVM_REPR_ID_MVMHash,
273
    unmanaged_size, /* unmanaged_size */
274
    NULL, /* describe_refs */
275
};