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