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