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