rtl: propagate OP_DST to scheme
[bpt/guile.git] / libguile / instructions.c
1 /* Copyright (C) 2001, 2009, 2010, 2011, 2012, 2013 Free Software Foundation, Inc.
2 *
3 * This library is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU Lesser General Public License
5 * as published by the Free Software Foundation; either version 3 of
6 * the License, or (at your option) any later version.
7 *
8 * This library is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with this library; if not, write to the Free Software
15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
16 * 02110-1301 USA
17 */
18
19 #if HAVE_CONFIG_H
20 # include <config.h>
21 #endif
22
23 #include <string.h>
24
25 #include "_scm.h"
26 #include "threads.h"
27 #include "instructions.h"
28
29
30 struct scm_instruction {
31 enum scm_opcode opcode; /* opcode */
32 const char *name; /* instruction name */
33 signed char len; /* Instruction length. This may be -1 for
34 the loader (see the `VM_LOADER'
35 macro). */
36 signed char npop; /* The number of values popped. This may be
37 -1 for insns like `call' which can take
38 any number of arguments. */
39 char npush; /* the number of values pushed */
40 SCM symname; /* filled in later */
41 };
42
43
44 SCM_SYMBOL (sym_left_arrow, "<-");
45 SCM_SYMBOL (sym_bang, "!");
46
47
48 #define OP_HAS_ARITY (1U << 0)
49
50 #define FOR_EACH_INSTRUCTION_WORD_TYPE(M) \
51 M(X32) \
52 M(U8_X24) \
53 M(U8_U24) \
54 M(U8_L24) \
55 M(U8_R24) \
56 M(U8_U8_I16) \
57 M(U8_U8_U8_U8) \
58 M(U8_U12_U12) \
59 M(U32) /* Unsigned. */ \
60 M(I32) /* Immediate. */ \
61 M(A32) /* Immediate, high bits. */ \
62 M(B32) /* Immediate, low bits. */ \
63 M(N32) /* Non-immediate. */ \
64 M(S32) /* Scheme value (indirected). */ \
65 M(L32) /* Label. */ \
66 M(LO32) /* Label with offset. */ \
67 M(X8_U24) \
68 M(X8_U12_U12) \
69 M(X8_R24) \
70 M(X8_L24) \
71 M(B1_X7_L24) \
72 M(B1_U7_L24)
73
74 #define TYPE_WIDTH 5
75
76 enum word_type
77 {
78 #define ENUM(type) type,
79 FOR_EACH_INSTRUCTION_WORD_TYPE (ENUM)
80 #undef ENUM
81 };
82
83 static SCM word_type_symbols[] =
84 {
85 #define FALSE(type) SCM_BOOL_F,
86 FOR_EACH_INSTRUCTION_WORD_TYPE (FALSE)
87 #undef FALSE
88 };
89
90 #define OP(n,type) ((type) << (n*TYPE_WIDTH))
91
92 /* The VM_DEFINE_OP macro uses a CPP-based DSL to describe what kinds of
93 arguments each RTL instruction takes. This piece of code is the only
94 bit that actually interprets that language. These macro definitions
95 encode the operand types into bits in a 32-bit integer.
96
97 (rtl-instruction-list) parses those encoded values into lists of
98 symbols, one for each 32-bit word that the operator takes. (system
99 vm rtl) uses those word types to generate assemblers and
100 disassemblers for the instructions. */
101
102 #define OP1(type0) \
103 (OP (0, type0))
104 #define OP2(type0, type1) \
105 (OP (0, type0) | OP (1, type1))
106 #define OP3(type0, type1, type2) \
107 (OP (0, type0) | OP (1, type1) | OP (2, type2))
108 #define OP4(type0, type1, type2, type3) \
109 (OP (0, type0) | OP (1, type1) | OP (2, type2) | OP (3, type3))
110
111 #define OP_DST (1 << (TYPE_WIDTH * 5))
112
113 #define WORD_TYPE(n, word) \
114 (((word) >> ((n) * TYPE_WIDTH)) & ((1 << TYPE_WIDTH) - 1))
115
116 struct scm_rtl_instruction {
117 enum scm_rtl_opcode opcode; /* opcode */
118 const char *name; /* instruction name */
119 scm_t_uint32 meta;
120 SCM symname; /* filled in later */
121 };
122
123
124 #define SCM_VALIDATE_LOOKUP_INSTRUCTION(pos, var, cvar) \
125 do { \
126 cvar = scm_lookup_instruction_by_name (var); \
127 SCM_ASSERT_TYPE (cvar, var, pos, FUNC_NAME, "INSTRUCTION_P"); \
128 } while (0)
129
130
131 static scm_i_pthread_mutex_t itable_lock = SCM_I_PTHREAD_MUTEX_INITIALIZER;
132
133
134 static const struct scm_instruction*
135 fetch_instruction_table ()
136 {
137 static struct scm_instruction *table = NULL;
138
139 scm_i_pthread_mutex_lock (&itable_lock);
140 if (SCM_UNLIKELY (!table))
141 {
142 size_t bytes = SCM_VM_NUM_INSTRUCTIONS * sizeof(struct scm_instruction);
143 int i;
144 table = malloc (bytes);
145 memset (table, 0, bytes);
146 #define VM_INSTRUCTION_TO_TABLE 1
147 #include <libguile/vm-expand.h>
148 #include <libguile/vm-i-system.i>
149 #include <libguile/vm-i-scheme.i>
150 #include <libguile/vm-i-loader.i>
151 #undef VM_INSTRUCTION_TO_TABLE
152 for (i = 0; i < SCM_VM_NUM_INSTRUCTIONS; i++)
153 {
154 table[i].opcode = i;
155 if (table[i].name)
156 table[i].symname = scm_from_utf8_symbol (table[i].name);
157 else
158 table[i].symname = SCM_BOOL_F;
159 }
160 }
161 scm_i_pthread_mutex_unlock (&itable_lock);
162
163 return table;
164 }
165
166 static const struct scm_rtl_instruction*
167 fetch_rtl_instruction_table ()
168 {
169 static struct scm_rtl_instruction *table = NULL;
170
171 scm_i_pthread_mutex_lock (&itable_lock);
172 if (SCM_UNLIKELY (!table))
173 {
174 size_t bytes = SCM_VM_NUM_INSTRUCTIONS * sizeof(struct scm_rtl_instruction);
175 int i;
176 table = malloc (bytes);
177 memset (table, 0, bytes);
178
179 #define INIT(opcode, tag, name_, meta_) table[opcode].name = name_; table[opcode].meta = meta_;
180 FOR_EACH_VM_OPERATION (INIT);
181 #undef INIT
182
183 for (i = 0; i < SCM_VM_NUM_INSTRUCTIONS; i++)
184 {
185 table[i].opcode = i;
186 if (table[i].name)
187 table[i].symname = scm_from_utf8_symbol (table[i].name);
188 else
189 table[i].symname = SCM_BOOL_F;
190 }
191 }
192 scm_i_pthread_mutex_unlock (&itable_lock);
193
194 return table;
195 }
196
197 static const struct scm_instruction *
198 scm_lookup_instruction_by_name (SCM name)
199 {
200 static SCM instructions_by_name = SCM_BOOL_F;
201 const struct scm_instruction *table = fetch_instruction_table ();
202 SCM op;
203
204 if (SCM_UNLIKELY (scm_is_false (instructions_by_name)))
205 {
206 unsigned int i;
207
208 instructions_by_name =
209 scm_make_hash_table (SCM_I_MAKINUM (SCM_VM_NUM_INSTRUCTIONS));
210
211 for (i = 0; i < SCM_VM_NUM_INSTRUCTIONS; i++)
212 if (scm_is_true (table[i].symname))
213 scm_hashq_set_x (instructions_by_name, table[i].symname,
214 SCM_I_MAKINUM (i));
215 }
216
217 op = scm_hashq_ref (instructions_by_name, name, SCM_UNDEFINED);
218 if (SCM_I_INUMP (op))
219 return &table[SCM_I_INUM (op)];
220
221 return NULL;
222 }
223
224
225 /* Scheme interface */
226
227 SCM_DEFINE (scm_instruction_list, "instruction-list", 0, 0, 0,
228 (void),
229 "")
230 #define FUNC_NAME s_scm_instruction_list
231 {
232 SCM list = SCM_EOL;
233 int i;
234 const struct scm_instruction *ip = fetch_instruction_table ();
235 for (i = 0; i < SCM_VM_NUM_INSTRUCTIONS; i++)
236 if (ip[i].name)
237 list = scm_cons (ip[i].symname, list);
238 return scm_reverse_x (list, SCM_EOL);
239 }
240 #undef FUNC_NAME
241
242 SCM_DEFINE (scm_rtl_instruction_list, "rtl-instruction-list", 0, 0, 0,
243 (void),
244 "")
245 #define FUNC_NAME s_scm_rtl_instruction_list
246 {
247 SCM list = SCM_EOL;
248 int i;
249 const struct scm_rtl_instruction *ip = fetch_rtl_instruction_table ();
250 for (i = 0; i < SCM_VM_NUM_INSTRUCTIONS; i++)
251 if (ip[i].name)
252 {
253 scm_t_uint32 meta = ip[i].meta;
254 SCM tail = SCM_EOL;
255 int len;
256
257 /* Format: (name opcode word0 word1 ...) */
258
259 if (WORD_TYPE (3, meta))
260 len = 4;
261 else if (WORD_TYPE (2, meta))
262 len = 3;
263 else if (WORD_TYPE (1, meta))
264 len = 2;
265 else if (WORD_TYPE (0, meta))
266 len = 1;
267 else
268 abort ();
269
270 switch (len)
271 {
272 case 4:
273 tail = scm_cons (word_type_symbols[WORD_TYPE (3, meta)], tail);
274 case 3:
275 tail = scm_cons (word_type_symbols[WORD_TYPE (2, meta)], tail);
276 case 2:
277 tail = scm_cons (word_type_symbols[WORD_TYPE (1, meta)], tail);
278 case 1:
279 tail = scm_cons (word_type_symbols[WORD_TYPE (0, meta)], tail);
280 default:
281 tail = scm_cons ((meta & OP_DST) ? sym_left_arrow : sym_bang, tail);
282 tail = scm_cons (scm_from_int (ip[i].opcode), tail);
283 tail = scm_cons (ip[i].symname, tail);
284 break;
285 }
286
287 list = scm_cons (tail, list);
288 }
289
290 return scm_reverse_x (list, SCM_EOL);
291 }
292 #undef FUNC_NAME
293
294 SCM_DEFINE (scm_instruction_p, "instruction?", 1, 0, 0,
295 (SCM obj),
296 "")
297 #define FUNC_NAME s_scm_instruction_p
298 {
299 return scm_from_bool (scm_lookup_instruction_by_name (obj) != NULL);
300 }
301 #undef FUNC_NAME
302
303 SCM_DEFINE (scm_instruction_length, "instruction-length", 1, 0, 0,
304 (SCM inst),
305 "")
306 #define FUNC_NAME s_scm_instruction_length
307 {
308 const struct scm_instruction *ip;
309 SCM_VALIDATE_LOOKUP_INSTRUCTION (1, inst, ip);
310 return SCM_I_MAKINUM (ip->len);
311 }
312 #undef FUNC_NAME
313
314 SCM_DEFINE (scm_instruction_pops, "instruction-pops", 1, 0, 0,
315 (SCM inst),
316 "")
317 #define FUNC_NAME s_scm_instruction_pops
318 {
319 const struct scm_instruction *ip;
320 SCM_VALIDATE_LOOKUP_INSTRUCTION (1, inst, ip);
321 return SCM_I_MAKINUM (ip->npop);
322 }
323 #undef FUNC_NAME
324
325 SCM_DEFINE (scm_instruction_pushes, "instruction-pushes", 1, 0, 0,
326 (SCM inst),
327 "")
328 #define FUNC_NAME s_scm_instruction_pushes
329 {
330 const struct scm_instruction *ip;
331 SCM_VALIDATE_LOOKUP_INSTRUCTION (1, inst, ip);
332 return SCM_I_MAKINUM (ip->npush);
333 }
334 #undef FUNC_NAME
335
336 SCM_DEFINE (scm_instruction_to_opcode, "instruction->opcode", 1, 0, 0,
337 (SCM inst),
338 "")
339 #define FUNC_NAME s_scm_instruction_to_opcode
340 {
341 const struct scm_instruction *ip;
342 SCM_VALIDATE_LOOKUP_INSTRUCTION (1, inst, ip);
343 return SCM_I_MAKINUM (ip->opcode);
344 }
345 #undef FUNC_NAME
346
347 SCM_DEFINE (scm_opcode_to_instruction, "opcode->instruction", 1, 0, 0,
348 (SCM op),
349 "")
350 #define FUNC_NAME s_scm_opcode_to_instruction
351 {
352 scm_t_signed_bits opcode;
353 SCM ret = SCM_BOOL_F;
354
355 SCM_MAKE_VALIDATE (1, op, I_INUMP);
356 opcode = SCM_I_INUM (op);
357
358 if (opcode >= 0 && opcode < SCM_VM_NUM_INSTRUCTIONS)
359 ret = fetch_instruction_table ()[opcode].symname;
360
361 if (scm_is_false (ret))
362 scm_wrong_type_arg_msg (FUNC_NAME, 1, op, "INSTRUCTION_P");
363
364 return ret;
365 }
366 #undef FUNC_NAME
367
368 void
369 scm_bootstrap_instructions (void)
370 {
371 scm_c_register_extension ("libguile-" SCM_EFFECTIVE_VERSION,
372 "scm_init_instructions",
373 (scm_t_extension_init_func)scm_init_instructions,
374 NULL);
375
376 #define INIT(type) \
377 word_type_symbols[type] = scm_from_utf8_symbol (#type);
378 FOR_EACH_INSTRUCTION_WORD_TYPE (INIT)
379 #undef INIT
380 }
381
382 void
383 scm_init_instructions (void)
384 {
385 #ifndef SCM_MAGIC_SNARFER
386 #include "libguile/instructions.x"
387 #endif
388 }
389
390 /*
391 Local Variables:
392 c-file-style: "gnu"
393 End:
394 */