1 /* pvm-program.c - PVM programs. */
2
3 /* Copyright (C) 2020, 2021 Jose E. Marchesi */
4
5 /* This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include <config.h>
20 #include <stdlib.h>
21 #include <assert.h>
22 #include <string.h>
23 #include <stdio.h> /* For stdout. */
24
25 #include "jitter/jitter-print.h"
26
27 #include "pk-utils.h"
28 #include "pkt.h"
29
30 #include "pvm.h"
31 #include "pvm-alloc.h"
32 #include "pvm-val.h"
33
34 #include "pvm-vm.h"
35
36 #define PVM_PROGRAM_MAX_POINTERS 16
37 #define PVM_PROGRAM_MAX_LABELS 8
38
39 struct pvm_program
40 {
41 /* Jitter routine corresponding to this PVM program. */
42 pvm_routine routine;
43
44 /* Jitter labels used in the program. */
45 jitter_label *labels;
46
47 /* Next available label in LABELS. */
48 int next_label;
49
50 /* Pointers to the boxed PVM values referenced in PROGRAM. It is
51 necessary to provide the GC visibility into the routine. */
52 void **pointers;
53
54 /* Next available slot in POINTERS. */
55 int next_pointer;
56 };
57
58 /* Jitter print context to use when disassembling PVM programs. */
59 jitter_print_context_kind jitter_context_kind = NULL;
60 jitter_print_context jitter_context = NULL;
61
62 static void
collect_value_pointers(pvm_program program,pvm_val val)63 collect_value_pointers (pvm_program program, pvm_val val)
64 {
65 /* If the value is boxed, we need to store a pointer to it in the
66 pointers field of PROGRAM. */
67 if (PVM_VAL_BOXED_P (val))
68 {
69 if (program->next_pointer % PVM_PROGRAM_MAX_POINTERS == 0)
70 {
71 size_t size
72 = ((program->next_pointer + PVM_PROGRAM_MAX_POINTERS)
73 * sizeof (void*));
74
75 program->pointers = pvm_realloc (program->pointers, size);
76 assert (program->pointers != NULL);
77 memset (program->pointers + program->next_pointer, 0,
78 PVM_PROGRAM_MAX_POINTERS * sizeof (void*));
79 }
80
81 program->pointers[program->next_pointer++]
82 = PVM_VAL_BOX (val);
83 }
84 }
85
86 pvm_program
pvm_program_new(void)87 pvm_program_new (void)
88 {
89 pvm_program program = pvm_alloc (sizeof (struct pvm_program));
90
91 if (program)
92 {
93 program->routine = pvm_make_routine ();
94 program->pointers = NULL;
95 program->next_pointer = 0;
96 program->labels = NULL;
97 program->next_label = 0;
98 }
99
100 return program;
101 }
102
103 pvm_program_label
pvm_program_fresh_label(pvm_program program)104 pvm_program_fresh_label (pvm_program program)
105 {
106 if (program->next_label % PVM_PROGRAM_MAX_LABELS == 0)
107 {
108 size_t size
109 = ((program->next_label + PVM_PROGRAM_MAX_LABELS)
110 * sizeof (int*));
111
112 program->labels = pvm_realloc (program->labels, size);
113
114 }
115
116 program->labels[program->next_label]
117 = jitter_fresh_label (program->routine);
118 return program->next_label++;
119 }
120
121 int
pvm_program_append_label(pvm_program program,pvm_program_label label)122 pvm_program_append_label (pvm_program program,
123 pvm_program_label label)
124 {
125 if (label >= program->next_label)
126 return PVM_EINVAL;
127
128 pvm_routine_append_label (program->routine,
129 program->labels[label]);
130 return PVM_OK;
131 }
132
133 int
pvm_program_append_instruction(pvm_program program,const char * insn_name)134 pvm_program_append_instruction (pvm_program program,
135 const char *insn_name)
136 {
137 /* For push instructions, pvm_program_append_push_instruction shall
138 be used instead. That function is to remove when the causing
139 limitation in jitter gets fixed. */
140 assert (STRNEQ (insn_name, "push"));
141
142 /* XXX Jitter should provide error codes so we can return PVM_EINVAL
143 and PVM_EINSN properly. */
144 pvm_routine_append_instruction_name (program->routine,
145 insn_name);
146
147 return PVM_OK;
148 }
149
150 int
pvm_program_append_push_instruction(pvm_program program,pvm_val val)151 pvm_program_append_push_instruction (pvm_program program,
152 pvm_val val)
153 {
154 pvm_routine routine = program->routine;
155
156 collect_value_pointers (program, val);
157
158 /* Due to some jitter limitations, we have to do some additional
159 work. */
160
161 #if defined POKE_HOST_32BIT
162 /* Use the push-hi and push-lo instructions, to overcome jitter's
163 limitation of only accepting a jitter_uint value as a literal
164 argument, whose size is 32-bit in 32-bit hosts. */
165
166 if (val & ~0xffffffffLL)
167 {
168 PVM_ROUTINE_APPEND_INSTRUCTION (routine, pushhi);
169 pvm_routine_append_unsigned_literal_parameter (routine,
170 ((jitter_uint)
171 (val >> 32)));
172
173 PVM_ROUTINE_APPEND_INSTRUCTION (routine, pushlo);
174 pvm_routine_append_unsigned_literal_parameter (routine,
175 ((jitter_uint)
176 (val & 0xffffffff)));
177 }
178 else
179 {
180 PVM_ROUTINE_APPEND_INSTRUCTION (routine, push32);
181 pvm_routine_append_unsigned_literal_parameter (routine,
182 ((jitter_uint)
183 (val & 0xffffffff)));
184 }
185 #else
186 PVM_ROUTINE_APPEND_INSTRUCTION (routine, push);
187 pvm_routine_append_unsigned_literal_parameter (routine,
188 (jitter_uint) val);
189 #endif
190
191 return PVM_OK;
192 }
193
194 int
pvm_program_append_val_parameter(pvm_program program,pvm_val val)195 pvm_program_append_val_parameter (pvm_program program, pvm_val val)
196 {
197 collect_value_pointers (program, val);
198 pvm_routine_append_unsigned_literal_parameter (program->routine,
199 (jitter_uint) val);
200
201 return PVM_OK;
202 }
203
204 int
pvm_program_append_unsigned_parameter(pvm_program program,unsigned int n)205 pvm_program_append_unsigned_parameter (pvm_program program,
206 unsigned int n)
207 {
208 pvm_routine_append_unsigned_literal_parameter (program->routine,
209 (jitter_uint) n);
210
211 return PVM_OK;
212 }
213
214 int
pvm_program_append_register_parameter(pvm_program program,pvm_register reg)215 pvm_program_append_register_parameter (pvm_program program,
216 pvm_register reg)
217 {
218 /* XXX Jitter should return an error code here so we can return
219 PVM_EINVAL whenever appropriate. */
220 PVM_ROUTINE_APPEND_REGISTER_PARAMETER (program->routine, r, reg);
221
222 return PVM_OK;
223 }
224
225 int
pvm_program_append_label_parameter(pvm_program program,pvm_program_label label)226 pvm_program_append_label_parameter (pvm_program program,
227 pvm_program_label label)
228 {
229 /* XXX Jitter should return an error code here so we can return
230 PVM_EINVAL whenever appropriate. */
231 pvm_routine_append_label_parameter (program->routine,
232 program->labels[label]);
233
234 return PVM_OK;
235 }
236
237 pvm_program_program_point
pvm_program_beginning(pvm_program program)238 pvm_program_beginning (pvm_program program)
239 {
240 return (pvm_program_program_point) PVM_ROUTINE_BEGINNING (program->routine);
241 }
242
243 int
pvm_program_make_executable(pvm_program program)244 pvm_program_make_executable (pvm_program program)
245 {
246 /* XXX Jitter should return an error code here. */
247 jitter_routine_make_executable_if_needed (program->routine);
248
249 return PVM_OK;
250 }
251
252 void
pvm_destroy_program(pvm_program program)253 pvm_destroy_program (pvm_program program)
254 {
255 pvm_destroy_routine (program->routine);
256 }
257
258 pvm_routine
pvm_program_routine(pvm_program program)259 pvm_program_routine (pvm_program program)
260 {
261 return program->routine;
262 }
263
264 /* Jitter print context. */
265
266 static int
pvm_jitter_print_flush(jitter_print_context_data data)267 pvm_jitter_print_flush (jitter_print_context_data data)
268 {
269 pk_term_flush ();
270 return 0;
271 }
272
273 static int
pvm_jitter_print_char(jitter_print_context_data d,char c)274 pvm_jitter_print_char (jitter_print_context_data d, char c)
275 {
276 char str[2];
277
278 str[0] = c;
279 str[1] = '\0';
280 pk_puts ((const char*) &str);
281 return 0;
282 }
283
284 static int
pvm_jitter_begin_decoration(jitter_print_context_data d,const jitter_print_decoration_name name,enum jitter_print_decoration_type type,const union jitter_print_decoration_value * value)285 pvm_jitter_begin_decoration (jitter_print_context_data d,
286 const jitter_print_decoration_name name,
287 enum jitter_print_decoration_type type,
288 const union jitter_print_decoration_value *value)
289 {
290 if (STREQ (name, JITTER_PRINT_DECORATION_NAME_CLASS))
291 pk_term_class (value->string);
292 else
293 pk_term_hyperlink (value->string, NULL);
294
295 return 0;
296 }
297
298 static int
pvm_jitter_end_decoration(jitter_print_context_data data,const jitter_print_decoration_name name,enum jitter_print_decoration_type type,const union jitter_print_decoration_value * value)299 pvm_jitter_end_decoration (jitter_print_context_data data,
300 const jitter_print_decoration_name name,
301 enum jitter_print_decoration_type type,
302 const union jitter_print_decoration_value *value)
303 {
304 if (STREQ (name, JITTER_PRINT_DECORATION_NAME_CLASS))
305 pk_term_end_class (value->string);
306 else
307 pk_term_end_hyperlink ();
308
309 return 0;
310 }
311
312 void
pvm_disassemble_program_nat(pvm_program program)313 pvm_disassemble_program_nat (pvm_program program)
314 {
315 pvm_routine_disassemble (jitter_context, program->routine,
316 true, JITTER_OBJDUMP, NULL);
317 }
318
319 void
pvm_disassemble_program(pvm_program program)320 pvm_disassemble_program (pvm_program program)
321 {
322 pvm_routine_print (jitter_context, program->routine);
323 }
324
325
326 void
pvm_program_init()327 pvm_program_init ()
328 {
329 jitter_context_kind = jitter_print_context_kind_make_trivial ();
330
331 jitter_context_kind->print_char = pvm_jitter_print_char;
332 jitter_context_kind->flush = pvm_jitter_print_flush;
333 jitter_context_kind->begin_decoration = pvm_jitter_begin_decoration;
334 jitter_context_kind->end_decoration = pvm_jitter_end_decoration;
335
336 jitter_context = jitter_print_context_make (jitter_context_kind,
337 NULL);
338 }
339
340 void
pvm_program_fini()341 pvm_program_fini ()
342 {
343 jitter_print_context_destroy (jitter_context);
344 jitter_print_context_kind_destroy (jitter_context_kind);
345 }
346