File: clone.fth

package info (click to toggle)
pforth 21-10
  • links: PTS
  • area: main
  • in suites: etch, etch-m68k
  • size: 820 kB
  • ctags: 873
  • sloc: ansic: 5,050; makefile: 102
file content (489 lines) | stat: -rw-r--r-- 11,837 bytes parent folder | download | duplicates (5)
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