/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, ¤t->hash_handle.key); |
52 | 1.09M | MVM_gc_worklist_add(tc, worklist, ¤t->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 | | }; |