1 /* VM library: specializer.
2
3 Copyright (C) 2016, 2017, 2018, 2019, 2020 Luca Saiu
4 Written by Luca Saiu
5
6 This file is part of Jitter.
7
8 Jitter is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
12
13 Jitter is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with Jitter. If not, see <http://www.gnu.org/licenses/>. */
20
21
22 #include <assert.h>
23
24 #include <jitter/jitter-malloc.h>
25 #include <jitter/jitter-fatal.h>
26
27 #include <jitter/jitter.h>
28 #include <jitter/jitter-instruction.h>
29 #include <jitter/jitter-mutable-routine.h>
30 #include <jitter/jitter-dynamic-buffer.h>
31 #include <jitter/jitter-specialize.h>
32 #include <jitter/jitter-replicate.h>
33 #include <jitter/jitter-vm.h>
34 #include <jitter/jitter-mmap.h> /* For executable deallocation. */
35
36
37 /* Executable routines: initialization and finalization.
38 * ************************************************************************** */
39
40 /* Initialize the pointed executable routine from the pointed non-executable
41 routine, without filling in the actual routine data.
42 Fail fatally if the routine already has another executable routine. */
43 static void
jitter_initialize_executable_routine(struct jitter_executable_routine * er,struct jitter_mutable_routine * r)44 jitter_initialize_executable_routine (struct jitter_executable_routine *er,
45 struct jitter_mutable_routine *r)
46 {
47 //fprintf (stderr, "[Making an executable routine at %p]\n", er);
48 /* Fail if this executable routine is not the first for the routine. */
49 if (r->executable_routine != NULL)
50 jitter_fatal ("cannot generate an executable routine from %p twice", r);
51
52 /* Link the two routines together thru pointers, so that destroying one can
53 update the other. */
54 r->executable_routine = er;
55 er->routine = r;
56 er->vm = r->vm;
57
58 /* Initialize the other fields, where we already have enough information. */
59 er->reference_count = 1;
60 er->slow_register_per_class_no = r->slow_register_per_class_no;
61
62 /* The remaining fields are still uninitialized. */
63 }
64
65 void
jitter_destroy_executable_routine(struct jitter_executable_routine * er)66 jitter_destroy_executable_routine (struct jitter_executable_routine *er)
67 {
68 //fprintf (stderr, "[Destroying executable routine at %p]\n", er);
69 /* If the non-executable routine which was translated into *er still exists
70 then unlink this. */
71 struct jitter_mutable_routine *r = er->routine;
72 if (r != NULL)
73 r->executable_routine = NULL;
74
75 /* Check that the reference count is exactly one. It is sensible to require
76 that when this function is called by the user the reference count be
77 exactly one; when called by jitter_unpin_executable_routine this field is
78 automatically set to 1 before arriving here, just in order to pass this
79 check. */
80 if (er->reference_count != 1)
81 jitter_fatal ("destroying executable routine with reference count %lu",
82 er->reference_count);
83
84 /* Destroy heap-allocated fields reachable from the compiled routine. */
85 #if (defined(JITTER_DISPATCH_SWITCH) \
86 || defined(JITTER_DISPATCH_DIRECT_THREADING))
87 free (er->specialized_program);
88 #elif (defined(JITTER_DISPATCH_MINIMAL_THREADING) \
89 || defined(JITTER_DISPATCH_NO_THREADING))
90 jitter_executable_deallocate (er->native_code);
91 #else
92 # error "unknown dispatch: this should not happen"
93 #endif /* dispatch */
94
95 /* Release memory for the struct itself. */
96 free (er);
97 }
98
99
100
101
102 /* Reference counting.
103 * ************************************************************************** */
104
105 void
jitter_pin_executable_routine(struct jitter_executable_routine * er)106 jitter_pin_executable_routine (struct jitter_executable_routine *er)
107 {
108 /* Just increment the field. This operation is easy and fast, as it never
109 involves construction or destruction. */
110 er->reference_count ++;
111 }
112
113 void
jitter_unpin_executable_routine(struct jitter_executable_routine * er)114 jitter_unpin_executable_routine (struct jitter_executable_routine *er)
115 {
116 /* Decrement the field. If the new value does not reach zero then we are
117 done. */
118 er->reference_count --;
119 if (er->reference_count > 0)
120 return;
121
122 /* If we arrived here the reference count has reached zero. */
123
124 /* Destroy the mutable companion, if it still exists. */
125 struct jitter_mutable_routine *r = er->routine;
126 if (r != NULL)
127 jitter_destroy_mutable_routine (r);
128
129 /* Destroy the executable routine itself. Temporarily set the reference count
130 field to one, as the executable destruction function expects, then call the
131 executable destruction function. */
132 er->reference_count = 1;
133 jitter_destroy_executable_routine (er);
134 }
135
136
137
138
139 /* Specialization.
140 * ************************************************************************** */
141
142 void
jitter_add_specialized_instruction_opcode(struct jitter_mutable_routine * p,jitter_uint specialized_opcode)143 jitter_add_specialized_instruction_opcode
144 (struct jitter_mutable_routine *p,
145 /* This is actually an enum vmprefix_specialized_instruction_opcode , but
146 the type is VM-dependent. */
147 jitter_uint specialized_opcode)
148 {
149 // FIXME: this comment is probably obsolete.
150 /* Without replication p->specialized_program holds direct-threaded code (each
151 VM thread followed by its zero or more residual arguments); without
152 replication p->specialized_program does *not* hold threads, but only
153 residual arguments.
154
155 In either case we push a new element onto p->replicated_blocks , which is
156 useful in one case at specialization time, and in the other only for
157 disassembling. */
158 struct jitter_replicated_block replicated_block = {specialized_opcode, NULL, 0};
159 jitter_dynamic_buffer_push (& p->replicated_blocks,
160 & replicated_block,
161 sizeof (struct jitter_replicated_block));
162 #ifndef JITTER_REPLICATE
163 # if defined(JITTER_DISPATCH_SWITCH)
164 union jitter_word w = {.fixnum = specialized_opcode};
165 # elif defined(JITTER_DISPATCH_DIRECT_THREADING)
166 union jitter_word w = {.thread = p->vm->threads [specialized_opcode]};
167 # else
168 # error "replication enabled, but not switch nor direct-threading"
169 # endif// #if defined(JITTER_DISPATCH_SWITCH)...
170 jitter_dynamic_buffer_push (& p->specialized_program, & w, sizeof (w));
171 #endif // #ifndef JITTER_REPLICATE
172 }
173
174 void
jitter_add_specialized_instruction_literal(struct jitter_mutable_routine * p,jitter_uint literal)175 jitter_add_specialized_instruction_literal (struct jitter_mutable_routine *p,
176 jitter_uint literal)
177 {
178 // fprintf (stderr, "Adding specialized instruction literal %i\n", (int)literal);
179 // FIXME: this will need generalization once more literal kinds are supported.
180 union jitter_word w = {.ufixnum = literal};
181 jitter_dynamic_buffer_push (& p->specialized_program, & w, sizeof (w));
182 }
183
184 void
jitter_add_specialized_instruction_label_index(struct jitter_mutable_routine * p,jitter_label_as_index unspecialized_instruction_index)185 jitter_add_specialized_instruction_label_index (struct jitter_mutable_routine *p,
186 jitter_label_as_index
187 unspecialized_instruction_index)
188 {
189 // fprintf (stderr, "Adding specialized instruction label_index %i\n", (int)unspecialized_instruction_index);
190 jitter_int next_word_index
191 = jitter_dynamic_buffer_size (& p->specialized_program)
192 / sizeof (jitter_int);
193 union jitter_word w
194 = {.ufixnum = unspecialized_instruction_index};
195 jitter_dynamic_buffer_push (& p->specialized_program, & w, sizeof (w));
196 jitter_dynamic_buffer_push (& p->specialized_label_indices,
197 & next_word_index, sizeof (jitter_int));
198 }
199
200 static void
jitter_backpatch_labels_in_specialized_routine(struct jitter_mutable_routine * p)201 jitter_backpatch_labels_in_specialized_routine (struct jitter_mutable_routine *p)
202 {
203 union jitter_word *specialized_program
204 = jitter_dynamic_buffer_to_pointer (& p->specialized_program);
205 const jitter_int *specialized_label_indices
206 = jitter_dynamic_buffer_to_pointer (& p->specialized_label_indices);
207 const jitter_int * const instruction_index_to_specialized_instruction_offset
208 = p->instruction_index_to_specialized_instruction_offset;
209 const int specialized_label_indices_no
210 = jitter_dynamic_buffer_size (& p->specialized_label_indices)
211 / sizeof (jitter_int);
212
213 int i;
214 for (i = 0; i < specialized_label_indices_no; i ++)
215 {
216 union jitter_word *argument
217 = specialized_program + specialized_label_indices[i];
218 argument->pointer
219 = (union jitter_word *)
220 ((char*)specialized_program
221 + instruction_index_to_specialized_instruction_offset
222 [argument->ufixnum]);
223 }
224 }
225
226 /* Add implicit instructions at the end of an unspecialized program. */
227 static void
jitter_mutable_routine_add_epilog(struct jitter_mutable_routine * p)228 jitter_mutable_routine_add_epilog (struct jitter_mutable_routine *p)
229 {
230 /* Add the final instructions which are supposed to close every VM routine.
231 Having each label, including the ones at the very end of the program when
232 they exist, associated to an actual unspecialized instruction makes
233 replication easier. */
234 if (p->options.add_final_exitvm)
235 jitter_mutable_routine_append_meta_instruction
236 (p, p->vm->exitvm_meta_instruction);
237 else
238 jitter_mutable_routine_append_meta_instruction
239 (p, p->vm->unreachable_meta_instruction);
240 }
241
242
243
244
245 /* Making an executable routine.
246 * ************************************************************************** */
247
248 struct jitter_executable_routine*
jitter_make_executable_routine(struct jitter_mutable_routine * p)249 jitter_make_executable_routine (struct jitter_mutable_routine *p)
250 {
251 if (p->stage != jitter_routine_stage_unspecialized)
252 jitter_fatal ("specializing non-unspecialized program");
253 if (p->expected_parameter_no != 0)
254 jitter_fatal ("specializing program with last instruction incomplete");
255 if (p->native_code != NULL)
256 jitter_fatal ("specializing program with native code already defined");
257
258 /* Add epilog instructions. This way we can be sure that the program
259 ends with an "exitvm" or "unreachable" instruction. */
260 jitter_mutable_routine_add_epilog (p);
261
262 /* Resolve label arguments in unspecialized instruction parameters. */
263 jitter_mutable_routine_resolve_labels (p);
264 /* Now label arguments refer unspecialized instruction indices. */
265
266 /* Compute jump targets. */
267 assert (p->jump_targets == NULL);
268 p->jump_targets = jitter_mutable_routine_jump_targets (p);
269
270 /* Now that we know how many instructions there are we can allocate
271 p->instruction_index_to_specialized_instruction_offset once and for all.
272 Its content will still be uninitialized. */
273 const int instruction_no = jitter_mutable_routine_instruction_no (p);
274 assert (p->instruction_index_to_specialized_instruction_offset == NULL);
275 p->instruction_index_to_specialized_instruction_offset
276 = jitter_xmalloc (sizeof (jitter_int) * instruction_no);
277
278 #ifdef JITTER_REPLICATE
279 /* Whenever some specialized instruction is the first one, or is reachable by
280 a jump, we need to insert a BEGINBASICBLOCK specialized instruction
281 before its specialization. This is needed for VM branches in minimal-threaded
282 code: there are no real threads in the thread array, only residual arguments;
283 however for every jump target we have to introduce a thread as the residual
284 argument of a BEGINBASICBLOCK special specialized instruction, which does
285 nothing but advancing the thread pointer past the argument.
286
287 We need this special case out of the loop to support empty programs, whose
288 specialization must still begin by a BEGINBASICBLOCK. Dealing with
289 this case within a loop is messy, because if the first instruction is
290 also a jump target then we need to set
291 p->instruction_index_to_specialized_instruction_offset [0]
292 before adding BEGINBASICBLOCK ; except that
293 p->instruction_index_to_specialized_instruction_offset [0]
294 it out of bounds if the program is empty.
295
296 In the no-threading case BEGINBASICBLOCK specialized instructions are
297 redundant but they cost nothing at run time, since they expand to zero
298 assembly instructions. FIXME: can I just not generate them in the
299 no-threading case by changing CPP conditionals? Test. */
300 if (instruction_no == 0)
301 jitter_insert_beginbasicblock (p);
302 #endif // #ifdef JITTER_REPLICATE
303
304 /* Specialize instructions, filling
305 p->instruction_index_to_specialized_instruction_offset at the same time. */
306 const struct jitter_instruction **instructions
307 = (const struct jitter_instruction **)
308 jitter_dynamic_buffer_to_pointer (& p->instructions);
309 int (* const specialize_instruction) (struct jitter_mutable_routine *p,
310 const struct jitter_instruction *ins)
311 = p->vm->specialize_instruction;
312 int instruction_index = 0;
313 while (instruction_index < instruction_no)
314 {
315 const struct jitter_instruction *next_instruction
316 = instructions [instruction_index];
317
318 /* The next specialized instruction will begin where the (current) free
319 space in specialized program begins. Notice that this may leave
320 uninitialized holes in
321 p->instruction_index_to_specialized_instruction_offset , at the indices
322 corresponding to some instructions specialized into
323 superinstructions. Also notice that the next generated native instructions
324 might belong to a translation of BEGINBASICBLOCK . */
325 p->instruction_index_to_specialized_instruction_offset [instruction_index]
326 = jitter_dynamic_buffer_size (& p->specialized_program);
327
328 #ifdef JITTER_REPLICATE
329 /* See the comment above about BEGINBASICBLOCK . */
330 if (instruction_index == 0 || p->jump_targets [instruction_index])
331 jitter_insert_beginbasicblock (p);
332 #endif // #ifdef JITTER_REPLICATE
333
334 /* Specialize the next instruction, obtaining as result the number of
335 unspecialized instructions covered by the one specialized instruction
336 they are translated into. This adds as many words as needed to
337 p->specialized_program . */
338 instruction_index += specialize_instruction (p, next_instruction);
339 }
340
341 /* Notice that p->instruction_index_to_specialized_instruction_offset is
342 accessed only when needed to patch labels, and it's harmless to leave its
343 content undefined in places where no labels are involved. */
344
345 /* Now that p->instruction_index_to_specialized_instruction_offset is filled
346 we have enough information to resolve label literals. */
347 jitter_backpatch_labels_in_specialized_routine (p);
348
349 /* The program is now specialized. FIXME: shall I free p->jump_targets
350 and set it to NULL now? */
351 p->stage = jitter_routine_stage_specialized;
352
353 #ifdef JITTER_REPLICATE
354 /* If replication is enabled then build the native code; this will change the
355 program stage again. */
356 jitter_replicate_program (p);
357 #endif // #ifdef JITTER_REPLICATE
358
359 /* Make an executable routine containing what we need to actually run the
360 code. */
361 struct jitter_executable_routine *res
362 = jitter_xmalloc (sizeof (struct jitter_executable_routine));
363 jitter_initialize_executable_routine (res, p);
364
365 /* Transfer the relevant fields from the non-executable routine to the
366 executable routine, invalidating the originals to avoid double freeing. */
367
368 /* Set the specialized_program field, where it exists. */
369 #if (defined(JITTER_DISPATCH_SWITCH) \
370 || defined(JITTER_DISPATCH_DIRECT_THREADING) \
371 || defined(JITTER_DISPATCH_MINIMAL_THREADING))
372 res->specialized_program
373 = jitter_dynamic_buffer_extract (& p->specialized_program);
374 #elif defined(JITTER_DISPATCH_NO_THREADING)
375 /* Nothing. */
376 #else
377 # error "unknown dispatch: this should not happen"
378 #endif /* dispatch */
379
380 /* Set the native_code and native_code_size fields, where they exist. */
381 #if (defined(JITTER_DISPATCH_SWITCH) \
382 || defined(JITTER_DISPATCH_DIRECT_THREADING)) \
383 /* Nothing. */
384 #elif (defined(JITTER_DISPATCH_MINIMAL_THREADING) \
385 || defined(JITTER_DISPATCH_NO_THREADING))
386 res->native_code = p->native_code;
387 res->native_code_size = p->native_code_size;
388 p->native_code = NULL;
389 #else
390 # error "unknown dispatch: this should not happen"
391 #endif /* dispatch */
392
393 /* Return a pointer to the executable routine. */
394 return res;
395 }
396