File: clone.fth

package info (click to toggle)
pforth 1%3A2.0.1-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, forky, sid, trixie
  • size: 904 kB
  • sloc: ansic: 6,283; makefile: 410
file content (489 lines) | stat: -rw-r--r-- 12,929 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
\ @(#) clone.fth 97/12/10 1.1
\ Clone for PForth
\
\ Create the smallest dictionary required to run an application.
\
\ Clone decompiles the Forth dictionary starting with the top
\ word in the program.  It then moves all referenced secondaries
\ into a new dictionary.
\
\ This work was inspired by the CLONE feature that Mike Haas wrote
\ for JForth.  Mike's CLONE disassembled 68000 machine code then
\ reassembled it which is much more difficult.
\
\ Copyright Phil Burk & 3DO 1994
\
\ O- trap custom 'C' calls
\ O- investigate ALITERAL, XLITERAL, use XLITERAL in [']

anew task-clone.fth
decimal

\ move to 'C'
: PRIMITIVE? ( xt -- flag , true if primitive )
    ['] FIRST_COLON <
;

: 'SELF ( -- xt , return xt of word being compiled )
    ?comp
    latest name>
    [compile] literal
; immediate


:struct CL.REFERENCE
    long  clr_OriginalXT    \ original XT of word
    long  clr_NewXT         \ corresponding XT in cloned dictionary
    long  clr_TotalSize     \ size including data in body
;struct

variable CL-INITIAL-REFS \ initial number of refs to allocate
100 cl-initial-refs !
variable CL-REF-LEVEL    \ level of threading while scanning
variable CL-NUM-REFS     \ number of secondaries referenced
variable CL-MAX-REFS     \ max number of secondaries allocated
variable CL-LEVEL-MAX    \ max level reached while scanning
variable CL-LEVEL-ABORT  \ max level before aborting
10 cl-level-abort !
variable CL-REFERENCES   \ pointer to cl.reference array
variable CL-TRACE        \ print debug stuff if true

\ Cloned dictionary builds in allocated memory but XTs are relative
\ to normal code-base, if CL-TEST-MODE true.
variable CL-TEST-MODE

variable CL-INITIAL-DICT \ initial size of dict to allocate
20 1024 * cl-initial-dict !
variable CL-DICT-SIZE    \ size of allocated cloned dictionary
variable CL-DICT-BASE    \ pointer to virtual base of cloned dictionary
variable CL-DICT-ALLOC   \ pointer to allocated dictionary memory
variable CL-DICT-PTR     \ rel pointer index into cloned dictionary
0 cl-dict-base !


: CL.INDENT ( -- )
    cl-ref-level @ 2* 2* spaces
;
: CL.DUMP.NAME ( xt -- )
    cl.indent
    >name id. cr
;

: CL.DICT[] ( relptr -- addr )
    cl-dict-base @ +
;

: CL,  ( cell -- , comma into clone dictionary )
    cl-dict-ptr @ cl.dict[] !
    cell cl-dict-ptr +!
;


: CL.FREE.DICT ( -- , free dictionary we built into )
    cl-dict-alloc @ ?dup
    IF
        free dup ?error
        0 cl-dict-alloc !
    THEN
;

: CL.FREE.REFS ( -- , free dictionary we built into )
    cl-references @ ?dup
    IF
        free dup ?error
        0 cl-references !
    THEN
;

: CL.ALLOC.REFS ( --  , allocate references to track )
    cl-initial-refs @  \ initial number of references
    dup cl-max-refs ! \ maximum allowed
    sizeof() cl.reference *
    allocate dup ?error
    cl-references !
;

: CL.RESIZE.REFS ( -- , allocate references to track )
    cl-max-refs @   \ current number of references allocated
    5 * 4 / dup cl-max-refs ! \ new maximum allowed
\ cl.indent ." Resize # references to " dup . cr
    sizeof() cl.reference *
    cl-references @ swap resize dup ?error
    cl-references !
;


: CL.ALLOC.DICT ( -- , allocate dictionary to build into )
    cl-initial-dict @  \ initial dictionary size
    dup cl-dict-size !
    allocate dup ?error
    cl-dict-alloc !
\
\ kludge dictionary if testing
    cl-test-mode @
    IF
        cl-dict-alloc @ code-base @ - cl-dict-ptr +!
        code-base @ cl-dict-base !
    ELSE
        cl-dict-alloc @  cl-dict-base !
    THEN
    ." CL.ALLOC.DICT" cr
    ."   cl-dict-alloc = $" cl-dict-alloc @ .hex cr
    ."   cl-dict-base  = $" cl-dict-base @ .hex cr
    ."   cl-dict-ptr   = $" cl-dict-ptr @ .hex cr
;

: CODEADDR>DATASIZE { code-addr -- datasize }
\ Determine size of any literal data following execution token.
\ Examples are text following (."), or branch offsets.
    code-addr @
    CASE
    ['] (literal) OF cell ENDOF   \ a number
    ['] 0branch   OF cell ENDOF   \ branch offset
    ['] branch    OF cell ENDOF
    ['] (do)      OF    0 ENDOF
    ['] (?do)     OF cell ENDOF
    ['] (loop)    OF cell ENDOF
    ['] (+loop)   OF cell ENDOF
    ['] (.")      OF code-addr cell+ c@ 1+ ENDOF  \ text
    ['] (s")      OF code-addr cell+ c@ 1+ ENDOF
    ['] (c")      OF code-addr cell+ c@ 1+ ENDOF
    0 swap
    ENDCASE
;

: XT>SIZE  ( xt -- wordsize , including code and data )
    dup >code
    swap >name
    dup latest =
    IF
        drop here
    ELSE
        dup c@ 1+ + aligned 8 + \ get next name
        name> >code \ where is next word
    THEN
    swap -
;

\ ------------------------------------------------------------------
: CL.TRAVERSE.SECONDARY { code-addr ca-process | xt dsize --  }
\ scan secondary and pass each code-address to ca-process
\ CA-PROCESS ( code-addr -- , required stack action for vector )
    1 cl-ref-level +!
    cl-ref-level @ cl-level-abort @ > abort" Clone exceeded CL-ABORT-LEVEL"
    BEGIN
        code-addr @ -> xt
\ cl.indent ." CL.TRAVERSE.SECONDARY - code-addr = $" code-addr .hex ." , xt = $" xt .hex cr
        code-addr codeaddr>datasize -> dsize      \ any data after this?
        code-addr ca-process execute              \ process it
        code-addr cell+ dsize + aligned -> code-addr  \ skip past data
\ !!! Bummer! EXIT called in middle of secondary will cause early stop.
        xt  ['] EXIT  =                           \ stop when we get to EXIT
    UNTIL
    -1 cl-ref-level +!
;

\ ------------------------------------------------------------------

: CL.DUMP.XT ( xt -- )
    cl-trace @
    IF
        dup primitive?
        IF   ." PRI:  "
        ELSE ." SEC:  "
        THEN
        cl.dump.name
    ELSE
        drop
    THEN
;

\ ------------------------------------------------------------------
: CL.REF[] ( index -- clref )
    sizeof() cl.reference *
    cl-references @ +
;

: CL.DUMP.REFS ( -- , print references )
    cl-num-refs @ 0
    DO
        i 3 .r ."  : "
        i cl.ref[]
        dup s@ clr_OriginalXT >name id. ."  => "
        dup s@ clr_NewXT .
        ." , size = "
        dup s@ clr_TotalSize . cr
        drop \ clref
    loop
;

: CL.XT>REF_INDEX { xt | indx flag -- index flag , true if found }
    BEGIN
\ cl.indent ." CL.XT>REF_INDEX - indx = " indx . cr
        indx cl-num-refs @ >=
        IF
            true
        ELSE
            indx cl.ref[] s@ clr_OriginalXT
\ cl.indent ." CL.XT>REF_INDEX - clr_OriginalXT = " dup . cr
            xt  =
            IF
                true
                dup -> flag
            ELSE
                false
                indx 1+ -> indx
            THEN
        THEN
    UNTIL
    indx flag
\ cl.indent ." CL.XT>REF_INDEX - " xt >name id. space  indx . flag . cr
;

: CL.ADD.REF  { xt | clref -- , add referenced secondary to list }
    cl-references @ 0= abort" CL.ADD.REF - References not allocated!"
\
\ do we need to allocate more room?
    cl-num-refs @ cl-max-refs @ >=
    IF
        cl.resize.refs
    THEN
\
    cl-num-refs @ cl.ref[] -> clref    \ index into array
    xt clref s! clr_OriginalXT
    0 clref s! clr_NewXT
    xt xt>size clref s! clr_TotalSize
\
    1 cl-num-refs +!
;

\ ------------------------------------------------------------------

\ called by cl.traverse.secondary to compile each piece of secondary
: CL.RECOMPILE.SECONDARY { code-addr | xt clref dsize -- ,  }
\ recompile to new location
\ cl.indent ." CL.RECOMPILE.SECONDARY - enter - " .s cr
    code-addr @ -> xt
\ cl.indent ." CL.RECOMPILE.SECONDARY - xt = $" dup .hex dup >name id. cr
    xt cl.dump.xt
    xt primitive?
    IF
        xt cl,
    ELSE
        xt CL.XT>REF_INDEX
        IF
            cl.ref[] -> clref
            clref s@ clr_NewXT
            dup 0= abort" CL.RECOMPILE.SECONDARY - unresolved NewXT"
            cl,
        ELSE
            cl.indent ." CL.RECOMPILE.SECONDARY - xt not in ref table!" cr
            abort
        THEN
    THEN
\
\ transfer any literal data
    code-addr codeaddr>datasize -> dsize
    dsize 0>
    IF
\ cl.indent ." CL.RECOMPILE.SECONDARY - copy inline data of size" dsize . cr
        code-addr cell+  cl-dict-ptr @ cl.dict[]  dsize  move
        cl-dict-ptr @ dsize + aligned cl-dict-ptr !
    THEN
\ cl.indent ." CL.RECOMPILE.SECONDARY - leave - " .s cr
;

: CL.RECOMPILE.REF { indx | clref codesize datasize -- }
\ all references have been resolved so recompile new secondary
    depth >r
    indx cl.ref[] -> clref
    cl-trace @
    IF
        cl.indent
        clref s@ clr_OriginalXT >name id. ."  recompiled at $"
        cl-dict-ptr @ .hex cr    \ new address
    THEN
    cl-dict-ptr @  clref s! clr_NewXT
\
\ traverse this secondary and compile into new dictionary
    clref s@ clr_OriginalXT
    >code ['] cl.recompile.secondary cl.traverse.secondary
\
\ determine whether there is any data following definition
    cl-dict-ptr @
    clref s@ clr_NewXT - -> codesize \ size of cloned code
    clref s@ clr_TotalSize \ total bytes
    codesize - -> datasize
    cl-trace @
    IF
        cl.indent
        ." Move data: data size = " datasize . ." codesize = " codesize . cr
    THEN
\
\ copy any data that followed definition
    datasize 0>
    IF
        clref s@ clr_OriginalXT >code codesize +
        clref s@ clr_NewXT cl-dict-base @ + codesize +
        datasize move
        datasize cl-dict-ptr +!  \ allot space in clone dictionary
    THEN

    depth r> - abort" Stack depth change in CL.RECOMPILE.REF"
;

\ ------------------------------------------------------------------
: CL.SCAN.SECONDARY ( code-addr -- , scan word and add referenced secondaries to list )
    depth 1- >r
\ cl.indent ." CL.SCAN.SECONDARY - enter - " .s cr
    cl-ref-level @ cl-level-max @  MAX cl-level-max !
    @ ( get xt )
\ cl.indent ." CL.SCAN.SECONDARY - xt = " dup . dup >name id. cr
    dup cl.dump.xt
    dup primitive?
    IF
        drop
\ cl.indent ." CL.SCAN.SECONDARY - found primitive." cr
    ELSE
        dup CL.XT>REF_INDEX
        IF
            drop \ indx   \ already referenced once so ignore
            drop \ xt
        ELSE
            >r \ indx
            dup cl.add.ref
            >code 'self cl.traverse.secondary   \ use 'self for recursion!
            r> cl.recompile.ref    \ now that all refs resolved, recompile
        THEN
    THEN
\ cl.indent ." CL.SCAN.SECONDARY - leave - " .s cr
    depth r> - abort" Stack depth change in CL.SCAN.SECONDARY"
;

: CL.CLONE.XT ( xt -- , scan top word and add referenced secondaries to list )
    dup primitive? abort" Cannot CLONE a PRIMITIVE word!"
    0 cl-ref-level !
    0 cl-level-max !
    0 cl-num-refs !
    dup cl.add.ref     \ word being cloned is top of ref list
    >code ['] cl.scan.secondary cl.traverse.secondary
    0 cl.recompile.ref
;

\ ------------------------------------------------------------------
: CL.XT>NEW_XT ( xt -- xt' , convert normal xt to xt in cloned dict )
    cl.xt>ref_index 0= abort" not in cloned dictionary!"
    cl.ref[] s@ clr_NewXT
;
: CL.XT>NEW_ADDR ( xt -- addr , addr in cloned dict )
    cl.xt>New_XT
    cl-dict-base @ +
;

: CL.REPORT ( -- )
    ." Clone scan went " cl-level-max @ . ." levels deep." cr
    ." Clone scanned " cl-num-refs @ . ." secondaries." cr
    ." New dictionary size =  " cl-dict-ptr @ cl-dict-base @ - . cr
;


\ ------------------------------------------------------------------
: CL.TERM ( -- , cleanup )
    cl.free.refs
    cl.free.dict
;

: CL.INIT ( -- )
    cl.term
    0 cl-dict-size !
    ['] first_colon cl-dict-ptr !
    cl.alloc.dict
    cl.alloc.refs
;

: 'CLONE ( xt -- , clone dictionary from this word )
    cl.init
    cl.clone.xt
    cl.report
    cl.dump.refs
    cl-test-mode @
    IF ." WARNING - CL-TEST-MODE on so we can't save cloned image." cr
    THEN
;

: SAVE-CLONE  ( <filename> -- )
    bl word
    ." Save cloned image in " dup count type
    drop ." SAVE-CLONE unimplemented!" \ %Q
;

: CLONE ( <name> -- )
    ' 'clone
;

if.forgotten cl.term

\ ---------------------------------- TESTS --------------------


: TEST.CLONE ( -- )
    cl-test-mode @ not abort" CL-TEST-MODE not on!"
    0 cl.ref[] s@ clr_NewXT  execute
;


: TEST.CLONE.REAL ( -- )
    cl-test-mode @ abort" CL-TEST-MODE on!"
    code-base @
    0 cl.ref[] s@ clr_NewXT  \ get cloned execution token
    cl-dict-base @ code-base !
\ WARNING - code-base munged, only execute primitives or cloned code
    execute
    code-base !   \ restore code base for normal
;


: TCL1
    34 dup +
;

: TCL2
    ." Hello " tcl1  . cr
;

: TCL3
    4 0
    DO
        tcl2
        i . cr
        i 100 + . cr
    LOOP
;

create VAR1 567 ,
: TCL4
    345 var1 !
    ." VAR1 = " var1 @ . cr
    var1 @ 345 -
    IF
        ." TCL4 failed!" cr
    ELSE
        ." TCL4 succeded! Yay!" cr
    THEN
;

\ do deferred words get cloned!
defer tcl.vector

: TCL.DOIT ." Hello Fred!" cr ;
' tcl.doit is tcl.vector

: TCL.DEFER
    12 . cr
    tcl.vector
    999 dup + . cr
;

trace-stack on
cl-test-mode on