Merge remote-tracking branch 'origin/stable-2.0'
[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_U8_I16) \
56 M(U8_U8_U8_U8) \
57 M(U8_U12_U12) \
58 M(U32) /* Unsigned. */ \
59 M(I32) /* Immediate. */ \
60 M(A32) /* Immediate, high bits. */ \
61 M(B32) /* Immediate, low bits. */ \
62 M(N32) /* Non-immediate. */ \
63 M(S32) /* Scheme value (indirected). */ \
64 M(L32) /* Label. */ \
65 M(LO32) /* Label with offset. */ \
66 M(X8_U24) \
67 M(X8_U12_U12) \
68 M(X8_L24) \
69 M(B1_X7_L24) \
70 M(B1_U7_L24) \
71 M(B1_X7_U24) \
72 M(B1_X31)
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 #define OP5(type0, type1, type2, type3, type4) \
111 (OP (0, type0) | OP (1, type1) | OP (2, type2) | OP (3, type3) | OP (4, type4))
112
113 #define OP_DST (1 << (TYPE_WIDTH * 5))
114
115 #define WORD_TYPE(n, word) \
116 (((word) >> ((n) * TYPE_WIDTH)) & ((1 << TYPE_WIDTH) - 1))
117
118 struct scm_rtl_instruction {
119 enum scm_rtl_opcode opcode; /* opcode */
120 const char *name; /* instruction name */
121 scm_t_uint32 meta;
122 SCM symname; /* filled in later */
123 };
124
125
126 #define SCM_VALIDATE_LOOKUP_INSTRUCTION(pos, var, cvar) \
127 do { \
128 cvar = scm_lookup_instruction_by_name (var); \
129 SCM_ASSERT_TYPE (cvar, var, pos, FUNC_NAME, "INSTRUCTION_P"); \
130 } while (0)
131
132
133 static scm_i_pthread_mutex_t itable_lock = SCM_I_PTHREAD_MUTEX_INITIALIZER;
134
135
136 static const struct scm_instruction*
137 fetch_instruction_table ()
138 {
139 static struct scm_instruction *table = NULL;
140
141 scm_i_pthread_mutex_lock (&itable_lock);
142 if (SCM_UNLIKELY (!table))
143 {
144 size_t bytes = SCM_VM_NUM_INSTRUCTIONS * sizeof(struct scm_instruction);
145 int i;
146 table = malloc (bytes);
147 memset (table, 0, bytes);
148 #define VM_INSTRUCTION_TO_TABLE 1
149 #include <libguile/vm-expand.h>
150 #include <libguile/vm-i-system.i>
151 #include <libguile/vm-i-scheme.i>
152 #include <libguile/vm-i-loader.i>
153 #undef VM_INSTRUCTION_TO_TABLE
154 for (i = 0; i < SCM_VM_NUM_INSTRUCTIONS; i++)
155 {
156 table[i].opcode = i;
157 if (table[i].name)
158 table[i].symname = scm_from_utf8_symbol (table[i].name);
159 else
160 table[i].symname = SCM_BOOL_F;
161 }
162 }
163 scm_i_pthread_mutex_unlock (&itable_lock);
164
165 return table;
166 }
167
168 static const struct scm_rtl_instruction*
169 fetch_rtl_instruction_table ()
170 {
171 static struct scm_rtl_instruction *table = NULL;
172
173 scm_i_pthread_mutex_lock (&itable_lock);
174 if (SCM_UNLIKELY (!table))
175 {
176 size_t bytes = SCM_VM_NUM_INSTRUCTIONS * sizeof(struct scm_rtl_instruction);
177 int i;
178 table = malloc (bytes);
179 memset (table, 0, bytes);
180
181 #define INIT(opcode, tag, name_, meta_) table[opcode].name = name_; table[opcode].meta = meta_;
182 FOR_EACH_VM_OPERATION (INIT);
183 #undef INIT
184
185 for (i = 0; i < SCM_VM_NUM_INSTRUCTIONS; i++)
186 {
187 table[i].opcode = i;
188 if (table[i].name)
189 table[i].symname = scm_from_utf8_symbol (table[i].name);
190 else
191 table[i].symname = SCM_BOOL_F;
192 }
193 }
194 scm_i_pthread_mutex_unlock (&itable_lock);
195
196 return table;
197 }
198
199 static const struct scm_instruction *
200 scm_lookup_instruction_by_name (SCM name)
201 {
202 static SCM instructions_by_name = SCM_BOOL_F;
203 const struct scm_instruction *table = fetch_instruction_table ();
204 SCM op;
205
206 if (SCM_UNLIKELY (scm_is_false (instructions_by_name)))
207 {
208 unsigned int i;
209
210 instructions_by_name =
211 scm_make_hash_table (SCM_I_MAKINUM (SCM_VM_NUM_INSTRUCTIONS));
212
213 for (i = 0; i < SCM_VM_NUM_INSTRUCTIONS; i++)
214 if (scm_is_true (table[i].symname))
215 scm_hashq_set_x (instructions_by_name, table[i].symname,
216 SCM_I_MAKINUM (i));
217 }
218
219 op = scm_hashq_ref (instructions_by_name, name, SCM_UNDEFINED);
220 if (SCM_I_INUMP (op))
221 return &table[SCM_I_INUM (op)];
222
223 return NULL;
224 }
225
226
227 /* Scheme interface */
228
229 SCM_DEFINE (scm_instruction_list, "instruction-list", 0, 0, 0,
230 (void),
231 "")
232 #define FUNC_NAME s_scm_instruction_list
233 {
234 SCM list = SCM_EOL;
235 int i;
236 const struct scm_instruction *ip = fetch_instruction_table ();
237 for (i = 0; i < SCM_VM_NUM_INSTRUCTIONS; i++)
238 if (ip[i].name)
239 list = scm_cons (ip[i].symname, list);
240 return scm_reverse_x (list, SCM_EOL);
241 }
242 #undef FUNC_NAME
243
244 SCM_DEFINE (scm_rtl_instruction_list, "rtl-instruction-list", 0, 0, 0,
245 (void),
246 "")
247 #define FUNC_NAME s_scm_rtl_instruction_list
248 {
249 SCM list = SCM_EOL;
250 int i;
251 const struct scm_rtl_instruction *ip = fetch_rtl_instruction_table ();
252 for (i = 0; i < SCM_VM_NUM_INSTRUCTIONS; i++)
253 if (ip[i].name)
254 {
255 scm_t_uint32 meta = ip[i].meta;
256 SCM tail = SCM_EOL;
257 int len;
258
259 /* Format: (name opcode word0 word1 ...) */
260
261 if (WORD_TYPE (4, meta))
262 len = 5;
263 else if (WORD_TYPE (3, meta))
264 len = 4;
265 else if (WORD_TYPE (2, meta))
266 len = 3;
267 else if (WORD_TYPE (1, meta))
268 len = 2;
269 else if (WORD_TYPE (0, meta))
270 len = 1;
271 else
272 abort ();
273
274 switch (len)
275 {
276 case 5:
277 tail = scm_cons (word_type_symbols[WORD_TYPE (4, meta)], tail);
278 case 4:
279 tail = scm_cons (word_type_symbols[WORD_TYPE (3, meta)], tail);
280 case 3:
281 tail = scm_cons (word_type_symbols[WORD_TYPE (2, meta)], tail);
282 case 2:
283 tail = scm_cons (word_type_symbols[WORD_TYPE (1, meta)], tail);
284 case 1:
285 tail = scm_cons (word_type_symbols[WORD_TYPE (0, meta)], tail);
286 default:
287 tail = scm_cons ((meta & OP_DST) ? sym_left_arrow : sym_bang, tail);
288 tail = scm_cons (scm_from_int (ip[i].opcode), tail);
289 tail = scm_cons (ip[i].symname, tail);
290 break;
291 }
292
293 list = scm_cons (tail, list);
294 }
295
296 return scm_reverse_x (list, SCM_EOL);
297 }
298 #undef FUNC_NAME
299
300 SCM_DEFINE (scm_instruction_p, "instruction?", 1, 0, 0,
301 (SCM obj),
302 "")
303 #define FUNC_NAME s_scm_instruction_p
304 {
305 return scm_from_bool (scm_lookup_instruction_by_name (obj) != NULL);
306 }
307 #undef FUNC_NAME
308
309 SCM_DEFINE (scm_instruction_length, "instruction-length", 1, 0, 0,
310 (SCM inst),
311 "")
312 #define FUNC_NAME s_scm_instruction_length
313 {
314 const struct scm_instruction *ip;
315 SCM_VALIDATE_LOOKUP_INSTRUCTION (1, inst, ip);
316 return SCM_I_MAKINUM (ip->len);
317 }
318 #undef FUNC_NAME
319
320 SCM_DEFINE (scm_instruction_pops, "instruction-pops", 1, 0, 0,
321 (SCM inst),
322 "")
323 #define FUNC_NAME s_scm_instruction_pops
324 {
325 const struct scm_instruction *ip;
326 SCM_VALIDATE_LOOKUP_INSTRUCTION (1, inst, ip);
327 return SCM_I_MAKINUM (ip->npop);
328 }
329 #undef FUNC_NAME
330
331 SCM_DEFINE (scm_instruction_pushes, "instruction-pushes", 1, 0, 0,
332 (SCM inst),
333 "")
334 #define FUNC_NAME s_scm_instruction_pushes
335 {
336 const struct scm_instruction *ip;
337 SCM_VALIDATE_LOOKUP_INSTRUCTION (1, inst, ip);
338 return SCM_I_MAKINUM (ip->npush);
339 }
340 #undef FUNC_NAME
341
342 SCM_DEFINE (scm_instruction_to_opcode, "instruction->opcode", 1, 0, 0,
343 (SCM inst),
344 "")
345 #define FUNC_NAME s_scm_instruction_to_opcode
346 {
347 const struct scm_instruction *ip;
348 SCM_VALIDATE_LOOKUP_INSTRUCTION (1, inst, ip);
349 return SCM_I_MAKINUM (ip->opcode);
350 }
351 #undef FUNC_NAME
352
353 SCM_DEFINE (scm_opcode_to_instruction, "opcode->instruction", 1, 0, 0,
354 (SCM op),
355 "")
356 #define FUNC_NAME s_scm_opcode_to_instruction
357 {
358 scm_t_signed_bits opcode;
359 SCM ret = SCM_BOOL_F;
360
361 SCM_MAKE_VALIDATE (1, op, I_INUMP);
362 opcode = SCM_I_INUM (op);
363
364 if (opcode >= 0 && opcode < SCM_VM_NUM_INSTRUCTIONS)
365 ret = fetch_instruction_table ()[opcode].symname;
366
367 if (scm_is_false (ret))
368 scm_wrong_type_arg_msg (FUNC_NAME, 1, op, "INSTRUCTION_P");
369
370 return ret;
371 }
372 #undef FUNC_NAME
373
374 void
375 scm_bootstrap_instructions (void)
376 {
377 scm_c_register_extension ("libguile-" SCM_EFFECTIVE_VERSION,
378 "scm_init_instructions",
379 (scm_t_extension_init_func)scm_init_instructions,
380 NULL);
381
382 #define INIT(type) \
383 word_type_symbols[type] = scm_from_utf8_symbol (#type);
384 FOR_EACH_INSTRUCTION_WORD_TYPE (INIT)
385 #undef INIT
386 }
387
388 void
389 scm_init_instructions (void)
390 {
391 #ifndef SCM_MAGIC_SNARFER
392 #include "libguile/instructions.x"
393 #endif
394 }
395
396 /*
397 Local Variables:
398 c-file-style: "gnu"
399 End:
400 */