PageRenderTime 51ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/fth/utils/clone.fth

https://github.com/cataska/pforth
Forth | 489 lines | 431 code | 58 blank | 0 comment | 10 complexity | 8d42b76162a2f1ef43b933c195535f30 MD5 | raw file
  1. \ @(#) clone.fth 97/12/10 1.1
  2. \ Clone for PForth
  3. \
  4. \ Create the smallest dictionary required to run an application.
  5. \
  6. \ Clone decompiles the Forth dictionary starting with the top
  7. \ word in the program. It then moves all referenced secondaries
  8. \ into a new dictionary.
  9. \
  10. \ This work was inspired by the CLONE feature that Mike Haas wrote
  11. \ for JForth. Mike's CLONE disassembled 68000 machine code then
  12. \ reassembled it which is much more difficult.
  13. \
  14. \ Copyright Phil Burk & 3DO 1994
  15. \
  16. \ O- trap custom 'C' calls
  17. \ O- investigate ALITERAL, XLITERAL, use XLITERAL in [']
  18. anew task-clone.fth
  19. decimal
  20. \ move to 'C'
  21. : PRIMITIVE? ( xt -- flag , true if primitive )
  22. ['] FIRST_COLON <
  23. ;
  24. : 'SELF ( -- xt , return xt of word being compiled )
  25. ?comp
  26. latest name>
  27. [compile] literal
  28. ; immediate
  29. :struct CL.REFERENCE
  30. long clr_OriginalXT \ original XT of word
  31. long clr_NewXT \ corresponding XT in cloned dictionary
  32. long clr_TotalSize \ size including data in body
  33. ;struct
  34. variable CL-INITIAL-REFS \ initial number of refs to allocate
  35. 100 cl-initial-refs !
  36. variable CL-REF-LEVEL \ level of threading while scanning
  37. variable CL-NUM-REFS \ number of secondaries referenced
  38. variable CL-MAX-REFS \ max number of secondaries allocated
  39. variable CL-LEVEL-MAX \ max level reached while scanning
  40. variable CL-LEVEL-ABORT \ max level before aborting
  41. 10 cl-level-abort !
  42. variable CL-REFERENCES \ pointer to cl.reference array
  43. variable CL-TRACE \ print debug stuff if true
  44. \ Cloned dictionary builds in allocated memory but XTs are relative
  45. \ to normal code-base, if CL-TEST-MODE true.
  46. variable CL-TEST-MODE
  47. variable CL-INITIAL-DICT \ initial size of dict to allocate
  48. 20 1024 * cl-initial-dict !
  49. variable CL-DICT-SIZE \ size of allocated cloned dictionary
  50. variable CL-DICT-BASE \ pointer to virtual base of cloned dictionary
  51. variable CL-DICT-ALLOC \ pointer to allocated dictionary memory
  52. variable CL-DICT-PTR \ rel pointer index into cloned dictionary
  53. 0 cl-dict-base !
  54. : CL.INDENT ( -- )
  55. cl-ref-level @ 2* 2* spaces
  56. ;
  57. : CL.DUMP.NAME ( xt -- )
  58. cl.indent
  59. >name id. cr
  60. ;
  61. : CL.DICT[] ( relptr -- addr )
  62. cl-dict-base @ +
  63. ;
  64. : CL, ( cell -- , comma into clone dictionary )
  65. cl-dict-ptr @ cl.dict[] !
  66. cell cl-dict-ptr +!
  67. ;
  68. : CL.FREE.DICT ( -- , free dictionary we built into )
  69. cl-dict-alloc @ ?dup
  70. IF
  71. free dup ?error
  72. 0 cl-dict-alloc !
  73. THEN
  74. ;
  75. : CL.FREE.REFS ( -- , free dictionary we built into )
  76. cl-references @ ?dup
  77. IF
  78. free dup ?error
  79. 0 cl-references !
  80. THEN
  81. ;
  82. : CL.ALLOC.REFS ( -- , allocate references to track )
  83. cl-initial-refs @ \ initial number of references
  84. dup cl-max-refs ! \ maximum allowed
  85. sizeof() cl.reference *
  86. allocate dup ?error
  87. cl-references !
  88. ;
  89. : CL.RESIZE.REFS ( -- , allocate references to track )
  90. cl-max-refs @ \ current number of references allocated
  91. 5 * 4 / dup cl-max-refs ! \ new maximum allowed
  92. \ cl.indent ." Resize # references to " dup . cr
  93. sizeof() cl.reference *
  94. cl-references @ swap resize dup ?error
  95. cl-references !
  96. ;
  97. : CL.ALLOC.DICT ( -- , allocate dictionary to build into )
  98. cl-initial-dict @ \ initial dictionary size
  99. dup cl-dict-size !
  100. allocate dup ?error
  101. cl-dict-alloc !
  102. \
  103. \ kludge dictionary if testing
  104. cl-test-mode @
  105. IF
  106. cl-dict-alloc @ code-base @ - cl-dict-ptr +!
  107. code-base @ cl-dict-base !
  108. ELSE
  109. cl-dict-alloc @ cl-dict-base !
  110. THEN
  111. ." CL.ALLOC.DICT" cr
  112. ." cl-dict-alloc = $" cl-dict-alloc @ .hex cr
  113. ." cl-dict-base = $" cl-dict-base @ .hex cr
  114. ." cl-dict-ptr = $" cl-dict-ptr @ .hex cr
  115. ;
  116. : CODEADDR>DATASIZE { code-addr -- datasize }
  117. \ Determine size of any literal data following execution token.
  118. \ Examples are text following (."), or branch offsets.
  119. code-addr @
  120. CASE
  121. ['] (literal) OF cell ENDOF \ a number
  122. ['] 0branch OF cell ENDOF \ branch offset
  123. ['] branch OF cell ENDOF
  124. ['] (do) OF 0 ENDOF
  125. ['] (?do) OF cell ENDOF
  126. ['] (loop) OF cell ENDOF
  127. ['] (+loop) OF cell ENDOF
  128. ['] (.") OF code-addr cell+ c@ 1+ ENDOF \ text
  129. ['] (s") OF code-addr cell+ c@ 1+ ENDOF
  130. ['] (c") OF code-addr cell+ c@ 1+ ENDOF
  131. 0 swap
  132. ENDCASE
  133. ;
  134. : XT>SIZE ( xt -- wordsize , including code and data )
  135. dup >code
  136. swap >name
  137. dup latest =
  138. IF
  139. drop here
  140. ELSE
  141. dup c@ 1+ + aligned 8 + \ get next name
  142. name> >code \ where is next word
  143. THEN
  144. swap -
  145. ;
  146. \ ------------------------------------------------------------------
  147. : CL.TRAVERSE.SECONDARY { code-addr ca-process | xt dsize -- }
  148. \ scan secondary and pass each code-address to ca-process
  149. \ CA-PROCESS ( code-addr -- , required stack action for vector )
  150. 1 cl-ref-level +!
  151. cl-ref-level @ cl-level-abort @ > abort" Clone exceeded CL-ABORT-LEVEL"
  152. BEGIN
  153. code-addr @ -> xt
  154. \ cl.indent ." CL.TRAVERSE.SECONDARY - code-addr = $" code-addr .hex ." , xt = $" xt .hex cr
  155. code-addr codeaddr>datasize -> dsize \ any data after this?
  156. code-addr ca-process execute \ process it
  157. code-addr cell+ dsize + aligned -> code-addr \ skip past data
  158. \ !!! Bummer! EXIT called in middle of secondary will cause early stop.
  159. xt ['] EXIT = \ stop when we get to EXIT
  160. UNTIL
  161. -1 cl-ref-level +!
  162. ;
  163. \ ------------------------------------------------------------------
  164. : CL.DUMP.XT ( xt -- )
  165. cl-trace @
  166. IF
  167. dup primitive?
  168. IF ." PRI: "
  169. ELSE ." SEC: "
  170. THEN
  171. cl.dump.name
  172. ELSE
  173. drop
  174. THEN
  175. ;
  176. \ ------------------------------------------------------------------
  177. : CL.REF[] ( index -- clref )
  178. sizeof() cl.reference *
  179. cl-references @ +
  180. ;
  181. : CL.DUMP.REFS ( -- , print references )
  182. cl-num-refs @ 0
  183. DO
  184. i 3 .r ." : "
  185. i cl.ref[]
  186. dup s@ clr_OriginalXT >name id. ." => "
  187. dup s@ clr_NewXT .
  188. ." , size = "
  189. dup s@ clr_TotalSize . cr
  190. drop \ clref
  191. loop
  192. ;
  193. : CL.XT>REF_INDEX { xt | indx flag -- index flag , true if found }
  194. BEGIN
  195. \ cl.indent ." CL.XT>REF_INDEX - indx = " indx . cr
  196. indx cl-num-refs @ >=
  197. IF
  198. true
  199. ELSE
  200. indx cl.ref[] s@ clr_OriginalXT
  201. \ cl.indent ." CL.XT>REF_INDEX - clr_OriginalXT = " dup . cr
  202. xt =
  203. IF
  204. true
  205. dup -> flag
  206. ELSE
  207. false
  208. indx 1+ -> indx
  209. THEN
  210. THEN
  211. UNTIL
  212. indx flag
  213. \ cl.indent ." CL.XT>REF_INDEX - " xt >name id. space indx . flag . cr
  214. ;
  215. : CL.ADD.REF { xt | clref -- , add referenced secondary to list }
  216. cl-references @ 0= abort" CL.ADD.REF - References not allocated!"
  217. \
  218. \ do we need to allocate more room?
  219. cl-num-refs @ cl-max-refs @ >=
  220. IF
  221. cl.resize.refs
  222. THEN
  223. \
  224. cl-num-refs @ cl.ref[] -> clref \ index into array
  225. xt clref s! clr_OriginalXT
  226. 0 clref s! clr_NewXT
  227. xt xt>size clref s! clr_TotalSize
  228. \
  229. 1 cl-num-refs +!
  230. ;
  231. \ ------------------------------------------------------------------
  232. \ called by cl.traverse.secondary to compile each piece of secondary
  233. : CL.RECOMPILE.SECONDARY { code-addr | xt clref dsize -- , }
  234. \ recompile to new location
  235. \ cl.indent ." CL.RECOMPILE.SECONDARY - enter - " .s cr
  236. code-addr @ -> xt
  237. \ cl.indent ." CL.RECOMPILE.SECONDARY - xt = $" dup .hex dup >name id. cr
  238. xt cl.dump.xt
  239. xt primitive?
  240. IF
  241. xt cl,
  242. ELSE
  243. xt CL.XT>REF_INDEX
  244. IF
  245. cl.ref[] -> clref
  246. clref s@ clr_NewXT
  247. dup 0= abort" CL.RECOMPILE.SECONDARY - unresolved NewXT"
  248. cl,
  249. ELSE
  250. cl.indent ." CL.RECOMPILE.SECONDARY - xt not in ref table!" cr
  251. abort
  252. THEN
  253. THEN
  254. \
  255. \ transfer any literal data
  256. code-addr codeaddr>datasize -> dsize
  257. dsize 0>
  258. IF
  259. \ cl.indent ." CL.RECOMPILE.SECONDARY - copy inline data of size" dsize . cr
  260. code-addr cell+ cl-dict-ptr @ cl.dict[] dsize move
  261. cl-dict-ptr @ dsize + aligned cl-dict-ptr !
  262. THEN
  263. \ cl.indent ." CL.RECOMPILE.SECONDARY - leave - " .s cr
  264. ;
  265. : CL.RECOMPILE.REF { indx | clref codesize datasize -- }
  266. \ all references have been resolved so recompile new secondary
  267. depth >r
  268. indx cl.ref[] -> clref
  269. cl-trace @
  270. IF
  271. cl.indent
  272. clref s@ clr_OriginalXT >name id. ." recompiled at $"
  273. cl-dict-ptr @ .hex cr \ new address
  274. THEN
  275. cl-dict-ptr @ clref s! clr_NewXT
  276. \
  277. \ traverse this secondary and compile into new dictionary
  278. clref s@ clr_OriginalXT
  279. >code ['] cl.recompile.secondary cl.traverse.secondary
  280. \
  281. \ determine whether there is any data following definition
  282. cl-dict-ptr @
  283. clref s@ clr_NewXT - -> codesize \ size of cloned code
  284. clref s@ clr_TotalSize \ total bytes
  285. codesize - -> datasize
  286. cl-trace @
  287. IF
  288. cl.indent
  289. ." Move data: data size = " datasize . ." codesize = " codesize . cr
  290. THEN
  291. \
  292. \ copy any data that followed definition
  293. datasize 0>
  294. IF
  295. clref s@ clr_OriginalXT >code codesize +
  296. clref s@ clr_NewXT cl-dict-base @ + codesize +
  297. datasize move
  298. datasize cl-dict-ptr +! \ allot space in clone dictionary
  299. THEN
  300. depth r> - abort" Stack depth change in CL.RECOMPILE.REF"
  301. ;
  302. \ ------------------------------------------------------------------
  303. : CL.SCAN.SECONDARY ( code-addr -- , scan word and add referenced secondaries to list )
  304. depth 1- >r
  305. \ cl.indent ." CL.SCAN.SECONDARY - enter - " .s cr
  306. cl-ref-level @ cl-level-max @ MAX cl-level-max !
  307. @ ( get xt )
  308. \ cl.indent ." CL.SCAN.SECONDARY - xt = " dup . dup >name id. cr
  309. dup cl.dump.xt
  310. dup primitive?
  311. IF
  312. drop
  313. \ cl.indent ." CL.SCAN.SECONDARY - found primitive." cr
  314. ELSE
  315. dup CL.XT>REF_INDEX
  316. IF
  317. drop \ indx \ already referenced once so ignore
  318. drop \ xt
  319. ELSE
  320. >r \ indx
  321. dup cl.add.ref
  322. >code 'self cl.traverse.secondary \ use 'self for recursion!
  323. r> cl.recompile.ref \ now that all refs resolved, recompile
  324. THEN
  325. THEN
  326. \ cl.indent ." CL.SCAN.SECONDARY - leave - " .s cr
  327. depth r> - abort" Stack depth change in CL.SCAN.SECONDARY"
  328. ;
  329. : CL.CLONE.XT ( xt -- , scan top word and add referenced secondaries to list )
  330. dup primitive? abort" Cannot CLONE a PRIMITIVE word!"
  331. 0 cl-ref-level !
  332. 0 cl-level-max !
  333. 0 cl-num-refs !
  334. dup cl.add.ref \ word being cloned is top of ref list
  335. >code ['] cl.scan.secondary cl.traverse.secondary
  336. 0 cl.recompile.ref
  337. ;
  338. \ ------------------------------------------------------------------
  339. : CL.XT>NEW_XT ( xt -- xt' , convert normal xt to xt in cloned dict )
  340. cl.xt>ref_index 0= abort" not in cloned dictionary!"
  341. cl.ref[] s@ clr_NewXT
  342. ;
  343. : CL.XT>NEW_ADDR ( xt -- addr , addr in cloned dict )
  344. cl.xt>New_XT
  345. cl-dict-base @ +
  346. ;
  347. : CL.REPORT ( -- )
  348. ." Clone scan went " cl-level-max @ . ." levels deep." cr
  349. ." Clone scanned " cl-num-refs @ . ." secondaries." cr
  350. ." New dictionary size = " cl-dict-ptr @ cl-dict-base @ - . cr
  351. ;
  352. \ ------------------------------------------------------------------
  353. : CL.TERM ( -- , cleanup )
  354. cl.free.refs
  355. cl.free.dict
  356. ;
  357. : CL.INIT ( -- )
  358. cl.term
  359. 0 cl-dict-size !
  360. ['] first_colon cl-dict-ptr !
  361. cl.alloc.dict
  362. cl.alloc.refs
  363. ;
  364. : 'CLONE ( xt -- , clone dictionary from this word )
  365. cl.init
  366. cl.clone.xt
  367. cl.report
  368. cl.dump.refs
  369. cl-test-mode @
  370. IF ." WARNING - CL-TEST-MODE on so we can't save cloned image." cr
  371. THEN
  372. ;
  373. : SAVE-CLONE ( <filename> -- )
  374. bl word
  375. ." Save cloned image in " dup count type
  376. drop ." SAVE-CLONE unimplemented!" \ %Q
  377. ;
  378. : CLONE ( <name> -- )
  379. ' 'clone
  380. ;
  381. if.forgotten cl.term
  382. \ ---------------------------------- TESTS --------------------
  383. : TEST.CLONE ( -- )
  384. cl-test-mode @ not abort" CL-TEST-MODE not on!"
  385. 0 cl.ref[] s@ clr_NewXT execute
  386. ;
  387. : TEST.CLONE.REAL ( -- )
  388. cl-test-mode @ abort" CL-TEST-MODE on!"
  389. code-base @
  390. 0 cl.ref[] s@ clr_NewXT \ get cloned execution token
  391. cl-dict-base @ code-base !
  392. \ WARNING - code-base munged, only execute primitives or cloned code
  393. execute
  394. code-base ! \ restore code base for normal
  395. ;
  396. : TCL1
  397. 34 dup +
  398. ;
  399. : TCL2
  400. ." Hello " tcl1 . cr
  401. ;
  402. : TCL3
  403. 4 0
  404. DO
  405. tcl2
  406. i . cr
  407. i 100 + . cr
  408. LOOP
  409. ;
  410. create VAR1 567 ,
  411. : TCL4
  412. 345 var1 !
  413. ." VAR1 = " var1 @ . cr
  414. var1 @ 345 -
  415. IF
  416. ." TCL4 failed!" cr
  417. ELSE
  418. ." TCL4 succeded! Yay!" cr
  419. THEN
  420. ;
  421. \ do deferred words get cloned!
  422. defer tcl.vector
  423. : TCL.DOIT ." Hello Fred!" cr ;
  424. ' tcl.doit is tcl.vector
  425. : TCL.DEFER
  426. 12 . cr
  427. tcl.vector
  428. 999 dup + . cr
  429. ;
  430. trace-stack on
  431. cl-test-mode on