c-base.scm 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  1. ;;; Ported from Scheme 48 1.9. See file COPYING for notices and license.
  2. ;;;
  3. ;;; Port Author: Andrew Whatson
  4. ;;;
  5. ;;; Original Authors: Richard Kelsey, Timo Harter, Mike Sperber
  6. ;;;
  7. ;;; scheme48-1.9.2/ps-compiler/prescheme/primop/c-base.scm
  8. (define-module (ps-compiler prescheme primop c-base)
  9. #:use-module (ice-9 format)
  10. #:use-module (ice-9 match)
  11. #:use-module (prescheme scheme48)
  12. #:use-module (ps-compiler node node)
  13. #:use-module (ps-compiler node node-util)
  14. #:use-module (ps-compiler node variable)
  15. #:use-module (ps-compiler prescheme c)
  16. #:use-module (ps-compiler prescheme c-call)
  17. #:use-module (ps-compiler prescheme c-decl)
  18. #:use-module (ps-compiler prescheme form)
  19. #:use-module ((ps-compiler prescheme infer-early) #:select (get-variable-type))
  20. #:use-module (ps-compiler prescheme primop c-primop)
  21. #:use-module (ps-compiler prescheme merge)
  22. #:use-module (ps-compiler prescheme spec)
  23. #:use-module (ps-compiler prescheme type)
  24. #:use-module (ps-compiler util util))
  25. (define-c-generator let #f
  26. (lambda (call port indent)
  27. (let ((args (call-args call))
  28. (vars (lambda-variables (call-arg call 0))))
  29. (do ((i 1 (+ i 1))
  30. (vars vars (cdr vars)))
  31. ((null? vars))
  32. (let ((val (vector-ref args i)))
  33. (if (not (lambda-node? val))
  34. (c-assignment (car vars) val port indent)))))))
  35. (define-c-generator letrec1 #f
  36. (lambda (call port indent)
  37. (values)))
  38. (define-c-generator letrec2 #f
  39. (lambda (call port indent)
  40. (values)))
  41. (define-c-generator jump #f
  42. (lambda (call port indent)
  43. (let ((proc (called-lambda call)))
  44. (assign-argument-vars (lambda-variables proc) call 1 port indent)
  45. (indent-to port indent)
  46. (display "goto " port)
  47. (writec port #\L)
  48. (display (lambda-id proc) port)
  49. (write-char #\; port)
  50. (note-jump-generated! proc)
  51. (values))))
  52. (define (assign-argument-vars vars call start port indent)
  53. (really-assign-argument-vars vars call start "arg" port indent))
  54. (define (assign-merged-argument-vars vars call start port indent)
  55. (really-assign-argument-vars vars call start "merged_arg" port indent))
  56. (define (assign-global-argument-vars vars call start port indent)
  57. (really-assign-argument-vars vars call start "goto_arg" port indent))
  58. (define (really-assign-argument-vars vars call start name port indent)
  59. (let ((args (call-args call)))
  60. (do ((i start (+ i 1))
  61. (vars vars (cdr vars)))
  62. ((>= i (vector-length args)))
  63. (if (not (or (undefined-value-node? (vector-ref args i))
  64. (eq? type/unit (get-variable-type (car vars)))))
  65. (c-assignment (c-argument-var name
  66. (get-variable-type (car vars))
  67. (- i start)
  68. port)
  69. (vector-ref args i)
  70. port indent)))))
  71. ;; Calls
  72. ;; Unknown calls have a first argument of 'goto if they are supposed to be
  73. ;; tail-recursive. For known calls the protocol field of the lambda node
  74. ;; is set to 'tail-called if any of the calls are supposed to be tail-recursive.
  75. ;;
  76. ;; Calls to non-tail-called procedures are just regular C calls. For tail-
  77. ;; called procedures there are two kinds of calls:
  78. ;; Tail-call from a tail-called procedure: proceed through the driver loop
  79. ;; All others: start a driver loop
  80. ;;
  81. ;; Known and unknown calls are handled identically, except that known calls
  82. ;; may be to merged procedures.
  83. ;;
  84. ;; Merged procedures with GOTO calls:
  85. ;; This works if we merge the return points as well. Possibly there should be
  86. ;; one return switch per C procedure. There do have to be separate return point
  87. ;; variables (and one global one for the switch).
  88. (define-c-generator call #f
  89. (lambda (call port indent)
  90. (cond ((merged-procedure-reference (call-arg call 1))
  91. => (lambda (form)
  92. (generate-merged-call call 2 form port indent)))
  93. (else
  94. (generate-c-call call 2 port indent)))))
  95. (define-c-generator tail-call #f
  96. (lambda (call port indent)
  97. (cond ((merged-procedure-reference (call-arg call 1))
  98. => (lambda (form)
  99. (generate-merged-goto-call call 2 form port indent)))
  100. (else
  101. (generate-c-tail-call call 2 port indent)))))
  102. (define-c-generator unknown-call #f
  103. (lambda (call port indent)
  104. (if (goto-protocol? (literal-value (call-arg call 2)))
  105. (user-warning "ignoring GOTO declaration for non-tail-recursive call to"
  106. (variable-name (reference-variable
  107. (call-arg call 1)))))
  108. (generate-c-call call 3 port indent)))
  109. (define-c-generator unknown-tail-call #f
  110. (lambda (call port indent)
  111. (generate-c-tail-call call 3 port indent)))
  112. (define (generate-merged-goto-call call start form port indent)
  113. (let ((proc (form-value form)))
  114. (assign-merged-argument-vars (cdr (lambda-variables proc))
  115. call start
  116. port indent)
  117. (indent-to port indent)
  118. (display "goto " port)
  119. (display (form-c-name form) port)
  120. (write-char #\; port)
  121. (values)))
  122. (define (generate-goto-call call start port indent)
  123. (let ((proc (call-arg call 1)))
  124. (if (not (global-reference? proc))
  125. (bug "incorrect procedure in goto call ~S" call))
  126. (assign-global-argument-vars (cdr (lambda-variables
  127. (global-lambda
  128. (reference-variable proc))))
  129. call start
  130. port indent)
  131. ;; T is the marker for the tail-call version of the procedure
  132. (indent-to port indent)
  133. (display "return((long)T" port)
  134. (c-value proc port)
  135. (display ");" port)))
  136. (define (global-lambda var)
  137. (let ((form (maybe-variable->form var)))
  138. (if (and form
  139. (or (eq? 'lambda (form-type form))
  140. (eq? 'merged (form-type form))))
  141. (form-value form)
  142. (bug "value of ~S, called using goto, is not a known procedure"
  143. var))))
  144. ;; C requires that we dereference all but calls to global functions.
  145. ;; Calls to literals are macros that must take care of themselves.
  146. (define (generate-c-call call start port indent)
  147. (let ((vars (lambda-variables (call-arg call 0)))
  148. (args (call-args call))
  149. (proc (call-arg call 1)))
  150. (if (and (global-reference? proc)
  151. (memq? 'tail-called (variable-flags (reference-variable proc))))
  152. (call-with-driver-loop call start port indent (car vars))
  153. (let ((deref? (or (and (reference-node? proc)
  154. (variable-binder (reference-variable proc)))
  155. (call-node? proc))))
  156. (if (used? (car vars))
  157. (c-assign-to-variable (car vars) port indent))
  158. (if deref?
  159. (display "(*" port))
  160. (c-value proc port)
  161. (if deref?
  162. (writec port #\)))
  163. (write-value+result-var-list args start (cdr vars) port)))
  164. (writec port #\;)
  165. (values)))
  166. (define (generate-c-tail-call call start port indent)
  167. (let ((proc (call-arg call 1))
  168. (args (call-args call))
  169. (cont (call-arg call 0)))
  170. (cond ((not (and (global-reference? proc)
  171. (memq? 'tail-called
  172. (variable-flags (reference-variable proc)))))
  173. (let* ((type (get-variable-type (reference-variable cont)))
  174. (void? (or (eq? type type/unit)
  175. (eq? type type/null))))
  176. (indent-to port indent)
  177. (if (not void?)
  178. (display "return " port))
  179. (c-value proc port)
  180. (write-value-list-with-extras args start *extra-tail-call-args* port)
  181. (if void?
  182. (begin
  183. (display ";" port)
  184. (indent-to port indent)
  185. (display "return" port)))))
  186. (*doing-tail-called-procedure?*
  187. (generate-goto-call call start port indent))
  188. (else
  189. (call-with-driver-loop call start port indent #f)))
  190. (writec port #\;)
  191. (values)))
  192. (define (global-reference? node)
  193. (and (reference-node? node)
  194. (global-variable? (reference-variable node))))
  195. (define (call-with-driver-loop call start port indent result-var)
  196. (let* ((proc-var (reference-variable (call-arg call 1)))
  197. (vars (lambda-variables (global-lambda proc-var))))
  198. (assign-global-argument-vars (cdr vars) call start port indent)
  199. (if result-var
  200. (c-assign-to-variable result-var port indent)
  201. (begin
  202. (indent-to port indent)
  203. (display "return " port)))
  204. (display "TTrun_machine((long)" port)
  205. (display "T" port)
  206. (write-c-identifier (variable-name proc-var) port)
  207. (display ")" port)))
  208. (define (generate-merged-call call start form port indent)
  209. (let ((return-index (form-return-count form))
  210. (name (form-c-name form))
  211. (res (lambda-variables (call-arg call 0))))
  212. (set-form-return-count! form (+ 1 return-index))
  213. (assign-merged-argument-vars (cdr (lambda-variables (form-value form)))
  214. call start port indent)
  215. (format port "~%#ifdef USE_DIRECT_THREADING~%")
  216. (indent-to port indent)
  217. (format port "~A_return_address = &&~A_return_~S;~%#else~%" name name return-index)
  218. (indent-to port indent)
  219. (format port "~A_return_tag = ~D;~%#endif~%" name return-index)
  220. (indent-to port indent)
  221. (format port "goto ~A;" name)
  222. (indent-to port (- indent 1))
  223. (format port "~A_return_~S:" name return-index)
  224. (do ((i 0 (+ i 1))
  225. (res res (cdr res)))
  226. ((null? res))
  227. (let ((var (car res)))
  228. (cond ((and (used? var)
  229. (let ((type (get-variable-type var)))
  230. (and (not (eq? type type/unit))
  231. (not (eq? type type/null)))))
  232. (c-assign-to-variable var port indent)
  233. (format port "~A~D_return_value;" name i)))))))
  234. ;; Returns
  235. (define-c-generator return #f
  236. (lambda (call port indent)
  237. (if *current-merged-procedure*
  238. (generate-return-from-merged-call call 1 port indent)
  239. (really-generate-c-return call 1 port indent))))
  240. (define-c-generator unknown-return #f
  241. (lambda (call port indent)
  242. (cond (*doing-tail-called-procedure?*
  243. (generate-return-from-tail-call call port indent))
  244. (*current-merged-procedure*
  245. (generate-return-from-merged-call call 1 port indent))
  246. (else
  247. (really-generate-c-return call 1 port indent)))))
  248. (define (generate-return-from-tail-call call port indent)
  249. (if (not (no-value-node? (call-arg call 1)))
  250. (c-assignment "TTreturn_value" (call-arg call 1) port indent))
  251. (indent-to port indent)
  252. (display "return(0L);" port))
  253. (define (generate-return-from-merged-call call start port indent)
  254. (let ((name *current-merged-procedure*))
  255. (do ((i start (+ i 1)))
  256. ((= i (call-arg-count call)))
  257. (let ((arg (call-arg call i)))
  258. (if (not (no-value-node? arg))
  259. (c-assignment (format #f "~A~D_return_value" name (- i start))
  260. arg port indent))))
  261. (format port "~%#ifdef USE_DIRECT_THREADING~%")
  262. (indent-to port indent)
  263. (format port "goto *~A_return_address;~%#else~%" name)
  264. (indent-to port indent)
  265. (format port "goto ~A_return;~%#endif~%" name)))
  266. (define (really-generate-c-return call start port indent)
  267. (do ((i (+ start 1) (+ i 1)))
  268. ((= i (call-arg-count call)))
  269. (let ((arg (call-arg call i)))
  270. (if (not (no-value-node? arg))
  271. (begin
  272. (indent-to port indent)
  273. (format port "*TT~D = " (- (- i start) 1))
  274. (c-value arg port)
  275. (write-char #\; port)))))
  276. (let ((result (call-arg call start)))
  277. (cond
  278. ((and (not (no-value-node? result))
  279. (let ((type (get-variable-type
  280. (reference-variable (call-arg call 0)))))
  281. (and (not (eq? type type/unit))
  282. (not (eq? type type/null)))))
  283. (indent-to port indent)
  284. (display "return" port)
  285. (write-char #\space port)
  286. (c-value result port)
  287. (display ";" port))
  288. (else
  289. (if (call-node? result)
  290. ;; emit for the side effects
  291. (begin
  292. (indent-to port indent)
  293. (primop-generate-c (call-primop result) result port 0)
  294. (display ";" port)
  295. (newline port)))
  296. (indent-to port indent)
  297. (display "return" port)
  298. (display ";" port))))
  299. (values))
  300. ;; Allocate
  301. ;;(define-c-generator allocate #f
  302. ;; (lambda (call port indent)
  303. ;; (let ((cont (call-arg call 0))
  304. ;; (size (call-arg call 1)))
  305. ;; (c-assign-to-variable (car (lambda-variables cont)) port indent)
  306. ;; (display "(long) malloc(" port)
  307. ;; (c-value size port)
  308. ;; (display "* sizeof(char));" port))))
  309. (define-c-generator global-ref #t
  310. (lambda (call port indent)
  311. (c-value (call-arg call 0) port)))
  312. (define-c-generator global-set! #f
  313. (lambda (call port indent)
  314. (let ((value (call-arg call 2)))
  315. (if (not (and (literal-node? value)
  316. (unspecific? (literal-value value))))
  317. (c-assignment (reference-variable (call-arg call 1))
  318. value
  319. port indent)))))
  320. ;; if (ARG1 OP ARG2) {
  321. ;; cont1 }
  322. ;; else {
  323. ;; cont2 }
  324. (define-c-generator test #f
  325. (lambda (call port indent)
  326. (match (call-args call)
  327. (#(cont1 cont2 value)
  328. (generate-c-conditional-prelude port indent)
  329. (c-value value port)
  330. (generate-c-conditional-jumps cont1 cont2 port indent)))))
  331. (define (generate-c-conditional-prelude port indent)
  332. (indent-to port indent)
  333. (display "if " port)
  334. (writec port #\())
  335. (define (generate-c-conditional-jumps cont1 cont2 port indent)
  336. (display ") {" port)
  337. (write-c-block (lambda-body cont1) port (+ indent 2))
  338. (newline port)
  339. (indent-to port indent)
  340. (display "else {" port)
  341. (write-c-block (lambda-body cont2) port (+ indent 2)))
  342. (define-c-generator unspecific #t
  343. (lambda (call port indent)
  344. (bug "generating code for undefined value ~S" call)))
  345. (define-c-generator uninitialized-value #t
  346. (lambda (call port indent)
  347. (bug "generating code for uninitialized value ~S" call)))
  348. (define-c-generator null-pointer? #t
  349. (lambda (call port indent)
  350. (display "NULL == " port)
  351. (c-value (call-arg call 0) port)))
  352. (define-c-generator null-pointer #t
  353. (lambda (call port indent)
  354. (display "NULL" port)))
  355. (define-c-generator eq? #t
  356. (lambda (call port indent)
  357. (simple-c-primop "==" call port)))