/home/travis/build/MoarVM/MoarVM/src/core/compunit.c
Line | Count | Source (jump to first uncovered line) |
1 | | #include "moar.h" |
2 | | #include "platform/mmap.h" |
3 | | |
4 | | #ifdef _WIN32 |
5 | | #include <fcntl.h> |
6 | | #define O_RDONLY _O_RDONLY |
7 | | #endif |
8 | | |
9 | | /* Creates a compilation unit from a byte array. */ |
10 | 2.61k | MVMCompUnit * MVM_cu_from_bytes(MVMThreadContext *tc, MVMuint8 *bytes, MVMuint32 size) { |
11 | 2.61k | /* Create compilation unit data structure. Allocate it in gen2 always, so |
12 | 2.61k | * it will never move (the JIT relies on this). */ |
13 | 2.61k | MVMCompUnit *cu; |
14 | 2.61k | MVM_gc_allocate_gen2_default_set(tc); |
15 | 2.61k | cu = (MVMCompUnit *)MVM_repr_alloc_init(tc, tc->instance->boot_types.BOOTCompUnit); |
16 | 2.61k | cu->body.data_start = bytes; |
17 | 2.61k | cu->body.data_size = size; |
18 | 2.61k | MVM_gc_allocate_gen2_default_clear(tc); |
19 | 2.61k | |
20 | 2.61k | /* Process the input. */ |
21 | 2.61k | MVMROOT(tc, cu, { |
22 | 2.61k | MVM_bytecode_unpack(tc, cu); |
23 | 2.61k | }); |
24 | 2.61k | |
25 | 2.61k | /* Resolve HLL config. It may contain nursery pointers, so fire write |
26 | 2.61k | * barrier on it. */ |
27 | 2.61k | cu->body.hll_config = MVM_hll_get_config_for(tc, cu->body.hll_name); |
28 | 2.61k | MVM_gc_write_barrier_hit(tc, (MVMCollectable *)cu); |
29 | 2.61k | |
30 | 2.61k | return cu; |
31 | 2.61k | } |
32 | | |
33 | | /* Loads a compilation unit from a bytecode file, mapping it into memory. */ |
34 | 1.43k | MVMCompUnit * MVM_cu_map_from_file(MVMThreadContext *tc, const char *filename) { |
35 | 1.43k | MVMCompUnit *cu = NULL; |
36 | 1.43k | void *block = NULL; |
37 | 1.43k | void *handle = NULL; |
38 | 1.43k | uv_file fd; |
39 | 1.43k | MVMuint64 size; |
40 | 1.43k | uv_fs_t req; |
41 | 1.43k | |
42 | 1.43k | /* Ensure the file exists, and get its size. */ |
43 | 1.43k | if (uv_fs_stat(tc->loop, &req, filename, NULL) < 0) { |
44 | 0 | MVM_exception_throw_adhoc(tc, "While looking for '%s': %s", filename, uv_strerror(req.result)); |
45 | 0 | } |
46 | 1.43k | |
47 | 1.43k | size = req.statbuf.st_size; |
48 | 1.43k | |
49 | 1.43k | /* Map the bytecode file into memory. */ |
50 | 1.43k | if ((fd = uv_fs_open(tc->loop, &req, filename, O_RDONLY, 0, NULL)) < 0) { |
51 | 0 | MVM_exception_throw_adhoc(tc, "While trying to open '%s': %s", filename, uv_strerror(req.result)); |
52 | 0 | } |
53 | 1.43k | |
54 | 1.43k | if ((block = MVM_platform_map_file(fd, &handle, (size_t)size, 0)) == NULL) { |
55 | 0 | /* FIXME: check errno or GetLastError() */ |
56 | 0 | MVM_exception_throw_adhoc(tc, "Could not map file '%s' into memory: %s", filename, "FIXME"); |
57 | 0 | } |
58 | 1.43k | |
59 | 1.43k | if (uv_fs_close(tc->loop, &req, fd, NULL) < 0) { |
60 | 0 | MVM_exception_throw_adhoc(tc, "Failed to close filehandle: %s", uv_strerror(req.result)); |
61 | 0 | } |
62 | 1.43k | |
63 | 1.43k | /* Turn it into a compilation unit. */ |
64 | 1.43k | cu = MVM_cu_from_bytes(tc, (MVMuint8 *)block, (MVMuint32)size); |
65 | 1.43k | cu->body.handle = handle; |
66 | 1.43k | cu->body.deallocate = MVM_DEALLOCATE_UNMAP; |
67 | 1.43k | return cu; |
68 | 1.43k | } |
69 | | |
70 | | /* Loads a compilation unit from a bytecode file handle, mapping it into memory. */ |
71 | 0 | MVMCompUnit * MVM_cu_map_from_file_handle(MVMThreadContext *tc, uv_file fd, MVMuint64 pos) { |
72 | 0 | MVMCompUnit *cu = NULL; |
73 | 0 | void *block = NULL; |
74 | 0 | void *handle = NULL; |
75 | 0 | MVMuint64 size; |
76 | 0 | uv_fs_t req; |
77 | 0 |
|
78 | 0 | /* Ensure the file exists, and get its size. */ |
79 | 0 | if (uv_fs_fstat(tc->loop, &req, fd, NULL) < 0) { |
80 | 0 | MVM_exception_throw_adhoc(tc, "Trying to stat: %s", uv_strerror(req.result)); |
81 | 0 | } |
82 | 0 |
|
83 | 0 | size = req.statbuf.st_size; |
84 | 0 |
|
85 | 0 | if ((block = MVM_platform_map_file(fd, &handle, (size_t)size, 0)) == NULL) { |
86 | 0 | /* FIXME: check errno or GetLastError() */ |
87 | 0 | MVM_exception_throw_adhoc(tc, "Could not map file into memory: %s", "FIXME"); |
88 | 0 | } |
89 | 0 |
|
90 | 0 | block = ((char*)block) + pos; |
91 | 0 |
|
92 | 0 | /* Turn it into a compilation unit. */ |
93 | 0 | cu = MVM_cu_from_bytes(tc, (MVMuint8 *)block, (MVMuint32)size); |
94 | 0 | cu->body.handle = handle; |
95 | 0 | cu->body.deallocate = MVM_DEALLOCATE_UNMAP; |
96 | 0 | return cu; |
97 | 0 | } |
98 | | |
99 | | /* Adds an extra callsite, needed due to an inlining, and returns its index. */ |
100 | 200 | MVMuint16 MVM_cu_callsite_add(MVMThreadContext *tc, MVMCompUnit *cu, MVMCallsite *cs) { |
101 | 200 | MVMuint16 found = 0; |
102 | 200 | MVMuint16 idx; |
103 | 200 | |
104 | 200 | uv_mutex_lock(cu->body.inline_tweak_mutex); |
105 | 200 | |
106 | 200 | /* See if we already know this callsite. */ |
107 | 3.11k | for (idx = 0; idx < cu->body.num_callsites; idx++) |
108 | 3.09k | if (cu->body.callsites[idx] == cs) { |
109 | 188 | found = 1; |
110 | 188 | break; |
111 | 188 | } |
112 | 200 | if (!found) { |
113 | 12 | /* Not known; let's add it. */ |
114 | 12 | size_t orig_size = cu->body.num_callsites * sizeof(MVMCallsite *); |
115 | 12 | size_t new_size = (cu->body.num_callsites + 1) * sizeof(MVMCallsite *); |
116 | 12 | MVMCallsite **new_callsites = MVM_fixed_size_alloc(tc, tc->instance->fsa, new_size); |
117 | 12 | memcpy(new_callsites, cu->body.callsites, orig_size); |
118 | 12 | idx = cu->body.num_callsites; |
119 | 12 | new_callsites[idx] = MVM_callsite_copy(tc, cs); |
120 | 12 | if (cu->body.callsites) |
121 | 12 | MVM_fixed_size_free_at_safepoint(tc, tc->instance->fsa, orig_size, |
122 | 12 | cu->body.callsites); |
123 | 12 | cu->body.callsites = new_callsites; |
124 | 12 | cu->body.num_callsites++; |
125 | 12 | } |
126 | 200 | |
127 | 200 | uv_mutex_unlock(cu->body.inline_tweak_mutex); |
128 | 200 | |
129 | 200 | return idx; |
130 | 200 | } |
131 | | |
132 | | /* Adds an extra string, needed due to an inlining, and returns its index. */ |
133 | 441 | MVMuint32 MVM_cu_string_add(MVMThreadContext *tc, MVMCompUnit *cu, MVMString *str) { |
134 | 441 | MVMuint32 found = 0; |
135 | 441 | MVMuint32 idx; |
136 | 441 | |
137 | 441 | uv_mutex_lock(cu->body.inline_tweak_mutex); |
138 | 441 | |
139 | 441 | /* See if we already know this string; only consider those added already by |
140 | 441 | * inline, since we don't intern and don't want this to be costly to hunt. */ |
141 | 596 | for (idx = cu->body.orig_strings; idx < cu->body.num_strings; idx++) |
142 | 493 | if (MVM_cu_string(tc, cu, idx) == str) { |
143 | 338 | found = 1; |
144 | 338 | break; |
145 | 338 | } |
146 | 441 | if (!found) { |
147 | 103 | /* Not known; let's add it. */ |
148 | 103 | size_t orig_size = cu->body.num_strings * sizeof(MVMString *); |
149 | 103 | size_t new_size = (cu->body.num_strings + 1) * sizeof(MVMString *); |
150 | 103 | MVMString **new_strings = MVM_fixed_size_alloc(tc, tc->instance->fsa, new_size); |
151 | 103 | memcpy(new_strings, cu->body.strings, orig_size); |
152 | 103 | idx = cu->body.num_strings; |
153 | 103 | new_strings[idx] = str; |
154 | 103 | if (cu->body.strings) |
155 | 103 | MVM_fixed_size_free_at_safepoint(tc, tc->instance->fsa, orig_size, |
156 | 103 | cu->body.strings); |
157 | 103 | cu->body.strings = new_strings; |
158 | 103 | cu->body.num_strings++; |
159 | 103 | } |
160 | 441 | |
161 | 441 | uv_mutex_unlock(cu->body.inline_tweak_mutex); |
162 | 441 | |
163 | 441 | return idx; |
164 | 441 | } |
165 | | |
166 | | /* Used when we try to read a string from the string heap, but it's not there. |
167 | | * Decodes it "on-demand" and stores it in the string heap. */ |
168 | 9.93M | static MVMuint32 read_uint32(MVMuint8 *src) { |
169 | 9.93M | #ifdef MVM_BIGENDIAN |
170 | | MVMuint32 value; |
171 | | size_t i; |
172 | | MVMuint8 *destbytes = (MVMuint8 *)&value; |
173 | | for (i = 0; i < 4; i++) |
174 | | destbytes[4 - i - 1] = src[i]; |
175 | | return value; |
176 | | #else |
177 | 9.93M | return *((MVMuint32 *)src); |
178 | 9.93M | #endif |
179 | 9.93M | } |
180 | 40.6k | static void compute_fast_table_upto(MVMThreadContext *tc, MVMCompUnit *cu, MVMuint32 end_bin) { |
181 | 40.6k | MVMuint32 cur_bin = cu->body.string_heap_fast_table_top; |
182 | 40.6k | MVMuint8 *cur_pos = cu->body.string_heap_start + cu->body.string_heap_fast_table[cur_bin]; |
183 | 40.6k | MVMuint8 *limit = cu->body.string_heap_read_limit; |
184 | 121k | while (cur_bin < end_bin) { |
185 | 81.1k | MVMuint32 i; |
186 | 1.37M | for (i = 0; i < MVM_STRING_FAST_TABLE_SPAN; i++) { |
187 | 1.29M | if (cur_pos + 4 < limit) { |
188 | 1.29M | MVMuint32 bytes = read_uint32(cur_pos) >> 1; |
189 | 1.03M | cur_pos += 4 + bytes + (bytes & 3 ? 4 - (bytes & 3) : 0); |
190 | 1.29M | } |
191 | 0 | else { |
192 | 0 | MVM_exception_throw_adhoc(tc, |
193 | 0 | "Attempt to read past end of string heap when locating string"); |
194 | 0 | } |
195 | 1.29M | } |
196 | 81.1k | cur_bin++; |
197 | 81.1k | cu->body.string_heap_fast_table[cur_bin] = (MVMuint32) |
198 | 81.1k | (cur_pos - cu->body.string_heap_start); |
199 | 81.1k | } |
200 | 40.6k | MVM_barrier(); |
201 | 40.6k | cu->body.string_heap_fast_table_top = end_bin; |
202 | 40.6k | } |
203 | 1.02M | MVMString * MVM_cu_obtain_string(MVMThreadContext *tc, MVMCompUnit *cu, MVMuint32 idx) { |
204 | 1.02M | MVMuint32 cur_idx; |
205 | 1.02M | MVMuint8 *cur_pos; |
206 | 1.02M | MVMuint8 *limit = cu->body.string_heap_read_limit; |
207 | 1.02M | |
208 | 1.02M | /* Make sure we've enough entries in the fast table to jump close to where |
209 | 1.02M | * the string will be. */ |
210 | 1.02M | MVMuint32 fast_bin = idx / MVM_STRING_FAST_TABLE_SPAN; |
211 | 1.02M | if (fast_bin > cu->body.string_heap_fast_table_top) |
212 | 40.6k | compute_fast_table_upto(tc, cu, fast_bin); |
213 | 1.02M | |
214 | 1.02M | /* Scan from that position to find the string we need. */ |
215 | 1.02M | cur_idx = fast_bin * MVM_STRING_FAST_TABLE_SPAN; |
216 | 1.02M | cur_pos = cu->body.string_heap_start + cu->body.string_heap_fast_table[fast_bin]; |
217 | 8.64M | while (cur_idx != idx) { |
218 | 7.61M | if (cur_pos + 4 < limit) { |
219 | 7.61M | MVMuint32 bytes = read_uint32(cur_pos) >> 1; |
220 | 6.03M | cur_pos += 4 + bytes + (bytes & 3 ? 4 - (bytes & 3) : 0); |
221 | 7.61M | } |
222 | 0 | else { |
223 | 0 | MVM_exception_throw_adhoc(tc, |
224 | 0 | "Attempt to read past end of string heap when locating string"); |
225 | 0 | } |
226 | 7.61M | cur_idx++; |
227 | 7.61M | } |
228 | 1.02M | |
229 | 1.02M | /* Read the string. */ |
230 | 1.02M | if (cur_pos + 4 < limit) { |
231 | 1.02M | MVMuint32 ss = read_uint32(cur_pos); |
232 | 1.02M | MVMuint32 bytes = ss >> 1; |
233 | 1.02M | MVMuint32 decode_utf8 = ss & 1; |
234 | 1.02M | cur_pos += 4; |
235 | 1.02M | if (cur_pos + bytes < limit) { |
236 | 1.02M | MVMString *s; |
237 | 1.02M | MVM_gc_allocate_gen2_default_set(tc); |
238 | 1.02M | s = decode_utf8 |
239 | 6.01k | ? MVM_string_utf8_decode(tc, tc->instance->VMString, (char *)cur_pos, bytes) |
240 | 1.02M | : MVM_string_latin1_decode(tc, tc->instance->VMString, (char *)cur_pos, bytes); |
241 | 1.02M | MVM_ASSIGN_REF(tc, &(cu->common.header), cu->body.strings[idx], s); |
242 | 1.02M | MVM_gc_allocate_gen2_default_clear(tc); |
243 | 1.02M | return s; |
244 | 1.02M | } |
245 | 0 | else { |
246 | 0 | MVM_exception_throw_adhoc(tc, |
247 | 0 | "Attempt to read past end of string heap when reading string"); |
248 | 0 | } |
249 | 1.02M | } |
250 | 0 | else { |
251 | 0 | MVM_exception_throw_adhoc(tc, |
252 | 0 | "Attempt to read past end of string heap when reading string length"); |
253 | 0 | } |
254 | 1.02M | } |