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