PageRenderTime 51ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/libguile/gsubr.c

#
C | 341 lines | 177 code | 50 blank | 114 comment | 7 complexity | c6f29c01464adafd31e8cc4be6fe321e MD5 | raw file
Possible License(s): GPL-3.0, LGPL-3.0, GPL-2.0
  1. /* Copyright (C) 1995,1996,1997,1998,1999,2000,2001, 2006, 2008, 2009, 2010, 2011, 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. #ifdef HAVE_CONFIG_H
  19. # include <config.h>
  20. #endif
  21. #include <stdio.h>
  22. #include <stdarg.h>
  23. #include "libguile/_scm.h"
  24. #include "libguile/gsubr.h"
  25. #include "libguile/foreign.h"
  26. #include "libguile/instructions.h"
  27. #include "libguile/srfi-4.h"
  28. #include "libguile/programs.h"
  29. #include "libguile/private-options.h"
  30. /*
  31. * gsubr.c
  32. * Provide `gsubrs' -- subrs taking a prescribed number of required, optional,
  33. * and rest arguments.
  34. */
  35. /* OK here goes nothing: we're going to define VM assembly trampolines for
  36. invoking subrs. Ready? Right! */
  37. /* There's a maximum of 10 args, so the number of possible combinations is:
  38. (REQ-OPT-REST)
  39. for 0 args: 1 (000) (1 + 0)
  40. for 1 arg: 3 (100, 010, 001) (2 + 1)
  41. for 2 args: 5 (200, 110, 020, 101, 011) (3 + 2)
  42. for 3 args: 7 (300, 210, 120, 030, 201, 111, 021) (4 + 3)
  43. for N args: 2N+1
  44. and the index at which N args starts:
  45. for 0 args: 0
  46. for 1 args: 1
  47. for 2 args: 4
  48. for 3 args: 9
  49. for N args: N^2
  50. One can prove this:
  51. (1 + 3 + 5 + ... + (2N+1))
  52. = ((2N+1)+1)/2 * (N+1)
  53. = 2(N+1)/2 * (N+1)
  54. = (N+1)^2
  55. Thus the total sum is 11^2 = 121. Let's just generate all of them as
  56. read-only data.
  57. */
  58. /* A: req; B: opt; C: rest */
  59. #define A(nreq) \
  60. SCM_PACK_OP_24 (assert_nargs_ee, nreq + 1), \
  61. SCM_PACK_OP_24 (subr_call, 0), \
  62. 0, \
  63. 0
  64. #define B(nopt) \
  65. SCM_PACK_OP_24 (assert_nargs_le, nopt + 1), \
  66. SCM_PACK_OP_24 (alloc_frame, nopt + 1), \
  67. SCM_PACK_OP_24 (subr_call, 0), \
  68. 0
  69. #define C() \
  70. SCM_PACK_OP_24 (bind_rest, 1), \
  71. SCM_PACK_OP_24 (subr_call, 0), \
  72. 0, \
  73. 0
  74. #define AB(nreq, nopt) \
  75. SCM_PACK_OP_24 (assert_nargs_ge, nreq + 1), \
  76. SCM_PACK_OP_24 (assert_nargs_le, nreq + nopt + 1), \
  77. SCM_PACK_OP_24 (alloc_frame, nreq + nopt + 1), \
  78. SCM_PACK_OP_24 (subr_call, 0)
  79. #define AC(nreq) \
  80. SCM_PACK_OP_24 (assert_nargs_ge, nreq + 1), \
  81. SCM_PACK_OP_24 (bind_rest, nreq + 1), \
  82. SCM_PACK_OP_24 (subr_call, 0), \
  83. 0
  84. #define BC(nopt) \
  85. SCM_PACK_OP_24 (bind_rest, nopt + 1), \
  86. SCM_PACK_OP_24 (subr_call, 0), \
  87. 0, \
  88. 0
  89. #define ABC(nreq, nopt) \
  90. SCM_PACK_OP_24 (assert_nargs_ge, nreq + 1), \
  91. SCM_PACK_OP_24 (bind_rest, nreq + nopt + 1), \
  92. SCM_PACK_OP_24 (subr_call, 0), \
  93. 0
  94. /*
  95. (defun generate-bytecode (n)
  96. "Generate bytecode for N arguments"
  97. (interactive "p")
  98. (insert (format "/\* %d arguments *\/\n " n))
  99. (let ((nreq n))
  100. (while (<= 0 nreq)
  101. (let ((nopt (- n nreq)))
  102. (insert
  103. (if (< 0 nreq)
  104. (if (< 0 nopt)
  105. (format " AB(%d,%d)," nreq nopt)
  106. (format " A(%d)," nreq))
  107. (if (< 0 nopt)
  108. (format " B(%d)," nopt)
  109. (format " A(0),"))))
  110. (setq nreq (1- nreq))))
  111. (insert "\n ")
  112. (setq nreq (1- n))
  113. (while (<= 0 nreq)
  114. (let ((nopt (- n nreq 1)))
  115. (insert
  116. (if (< 0 nreq)
  117. (if (< 0 nopt)
  118. (format " ABC(%d,%d)," nreq nopt)
  119. (format " AC(%d)," nreq))
  120. (if (< 0 nopt)
  121. (format " BC(%d)," nopt)
  122. (format " C(),"))))
  123. (setq nreq (1- nreq))))
  124. (insert "\n\n ")))
  125. (defun generate-bytecodes (n)
  126. "Generate bytecodes for up to N arguments"
  127. (interactive "p")
  128. (let ((i 0))
  129. (while (<= i n)
  130. (generate-bytecode i)
  131. (setq i (1+ i)))))
  132. */
  133. static const scm_t_uint32 subr_stub_code[] = {
  134. /* C-u 1 0 M-x generate-bytecodes RET */
  135. /* 0 arguments */
  136. A(0),
  137. /* 1 arguments */
  138. A(1), B(1),
  139. C(),
  140. /* 2 arguments */
  141. A(2), AB(1,1), B(2),
  142. AC(1), BC(1),
  143. /* 3 arguments */
  144. A(3), AB(2,1), AB(1,2), B(3),
  145. AC(2), ABC(1,1), BC(2),
  146. /* 4 arguments */
  147. A(4), AB(3,1), AB(2,2), AB(1,3), B(4),
  148. AC(3), ABC(2,1), ABC(1,2), BC(3),
  149. /* 5 arguments */
  150. A(5), AB(4,1), AB(3,2), AB(2,3), AB(1,4), B(5),
  151. AC(4), ABC(3,1), ABC(2,2), ABC(1,3), BC(4),
  152. /* 6 arguments */
  153. A(6), AB(5,1), AB(4,2), AB(3,3), AB(2,4), AB(1,5), B(6),
  154. AC(5), ABC(4,1), ABC(3,2), ABC(2,3), ABC(1,4), BC(5),
  155. /* 7 arguments */
  156. A(7), AB(6,1), AB(5,2), AB(4,3), AB(3,4), AB(2,5), AB(1,6), B(7),
  157. AC(6), ABC(5,1), ABC(4,2), ABC(3,3), ABC(2,4), ABC(1,5), BC(6),
  158. /* 8 arguments */
  159. A(8), AB(7,1), AB(6,2), AB(5,3), AB(4,4), AB(3,5), AB(2,6), AB(1,7), B(8),
  160. AC(7), ABC(6,1), ABC(5,2), ABC(4,3), ABC(3,4), ABC(2,5), ABC(1,6), BC(7),
  161. /* 9 arguments */
  162. A(9), AB(8,1), AB(7,2), AB(6,3), AB(5,4), AB(4,5), AB(3,6), AB(2,7), AB(1,8), B(9),
  163. AC(8), ABC(7,1), ABC(6,2), ABC(5,3), ABC(4,4), ABC(3,5), ABC(2,6), ABC(1,7), BC(8),
  164. /* 10 arguments */
  165. A(10), AB(9,1), AB(8,2), AB(7,3), AB(6,4), AB(5,5), AB(4,6), AB(3,7), AB(2,8), AB(1,9), B(10),
  166. AC(9), ABC(8,1), ABC(7,2), ABC(6,3), ABC(5,4), ABC(4,5), ABC(3,6), ABC(2,7), ABC(1,8), BC(9),
  167. };
  168. #undef A
  169. #undef B
  170. #undef C
  171. #undef AB
  172. #undef AC
  173. #undef BC
  174. #undef ABC
  175. /* (nargs * nargs) + nopt + rest * (nargs + 1) */
  176. #define SUBR_STUB_CODE(nreq,nopt,rest) \
  177. &subr_stub_code[((nreq + nopt + rest) * (nreq + nopt + rest) \
  178. + nopt + rest * (nreq + nopt + rest + 1)) * 4]
  179. static const scm_t_uint32*
  180. get_subr_stub_code (unsigned int nreq, unsigned int nopt, unsigned int rest)
  181. {
  182. if (SCM_UNLIKELY (rest > 1 || nreq + nopt + rest > 10))
  183. scm_out_of_range ("make-subr", scm_from_uint (nreq + nopt + rest));
  184. return SUBR_STUB_CODE (nreq, nopt, rest);
  185. }
  186. static SCM
  187. create_subr (int define, const char *name,
  188. unsigned int nreq, unsigned int nopt, unsigned int rest,
  189. SCM (*fcn) (), SCM *generic_loc)
  190. {
  191. SCM ret, sname;
  192. scm_t_bits flags;
  193. scm_t_bits nfree = generic_loc ? 3 : 2;
  194. sname = scm_from_utf8_symbol (name);
  195. flags = SCM_F_PROGRAM_IS_PRIMITIVE;
  196. flags |= generic_loc ? SCM_F_PROGRAM_IS_PRIMITIVE_GENERIC : 0;
  197. ret = scm_words (scm_tc7_program | (nfree << 16) | flags, nfree + 2);
  198. SCM_SET_CELL_WORD_1 (ret, get_subr_stub_code (nreq, nopt, rest));
  199. SCM_PROGRAM_FREE_VARIABLE_SET (ret, 0, scm_from_pointer (fcn, NULL));
  200. SCM_PROGRAM_FREE_VARIABLE_SET (ret, 1, sname);
  201. if (generic_loc)
  202. SCM_PROGRAM_FREE_VARIABLE_SET (ret, 2,
  203. scm_from_pointer (generic_loc, NULL));
  204. if (define)
  205. scm_define (sname, ret);
  206. return ret;
  207. }
  208. /* Given a program that is a primitive, determine its minimum arity.
  209. This is possible because each primitive's code is 4 32-bit words
  210. long, and they are laid out contiguously in an ordered pattern. */
  211. int
  212. scm_i_primitive_arity (SCM prim, int *req, int *opt, int *rest)
  213. {
  214. const scm_t_uint32 *code = SCM_PROGRAM_CODE (prim);
  215. unsigned idx, nargs, base, next;
  216. if (code < subr_stub_code)
  217. return 0;
  218. if (code > subr_stub_code + (sizeof(subr_stub_code) / sizeof(scm_t_uint32)))
  219. return 0;
  220. idx = (code - subr_stub_code) / 4;
  221. nargs = -1;
  222. next = 0;
  223. do
  224. {
  225. base = next;
  226. nargs++;
  227. next = (nargs + 1) * (nargs + 1);
  228. }
  229. while (idx >= next);
  230. *rest = (next - idx) < (idx - base);
  231. *req = *rest ? (next - 1) - idx : (base + nargs) - idx;
  232. *opt = *rest ? idx - (next - nargs) : idx - base;
  233. return 1;
  234. }
  235. scm_t_uintptr
  236. scm_i_primitive_call_ip (SCM subr)
  237. {
  238. const scm_t_uint32 *code = SCM_PROGRAM_CODE (subr);
  239. /* A stub is 4 32-bit words long, or 16 bytes. The call will be one
  240. instruction, in either the fourth, third, or second word. Return a
  241. byte offset from the entry. */
  242. return (scm_t_uintptr)(code + (code[3] ? 3 : code[2] ? 2 : 1));
  243. }
  244. SCM
  245. scm_c_make_gsubr (const char *name, int req, int opt, int rst, SCM (*fcn)())
  246. {
  247. return create_subr (0, name, req, opt, rst, fcn, NULL);
  248. }
  249. SCM
  250. scm_c_define_gsubr (const char *name, int req, int opt, int rst, SCM (*fcn)())
  251. {
  252. return create_subr (1, name, req, opt, rst, fcn, NULL);
  253. }
  254. SCM
  255. scm_c_make_gsubr_with_generic (const char *name,
  256. int req,
  257. int opt,
  258. int rst,
  259. SCM (*fcn)(),
  260. SCM *gf)
  261. {
  262. return create_subr (0, name, req, opt, rst, fcn, gf);
  263. }
  264. SCM
  265. scm_c_define_gsubr_with_generic (const char *name,
  266. int req,
  267. int opt,
  268. int rst,
  269. SCM (*fcn)(),
  270. SCM *gf)
  271. {
  272. return create_subr (1, name, req, opt, rst, fcn, gf);
  273. }
  274. void
  275. scm_init_gsubr()
  276. {
  277. #include "libguile/gsubr.x"
  278. }
  279. /*
  280. Local Variables:
  281. c-file-style: "gnu"
  282. End:
  283. */