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