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
|
~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~ ~~ Top-level executable builders ~~
~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~
~ Although it's perfectly possible to manually invoke the pieces of the
~ compiler, it's inconvenient to do so. This file has helper words which kick
~ off the process of compilation from the top level.
~
~ The two most important words in the top-level facility are "enchant",
~ which produces an executable binary, and "inscribe", which produces a
~ commented hex dump corresponding to that same binary.
~
~ Significant effort has been expended to make sure that the interfaces to
~ "enchant" and "inscribe" are the same, despite them notionally existing at
~ different levels of abstraction.
~ This accepts a pointer to an existing buffer for file contents to be
~ appended to, and another pointer to the start of a packed list of filenames.
~ The filename list consists of null-terminated strings, all directly abutting
~ each other in memory, terminated by a zero-length string (which can also
~ be thought of as a freestanding null byte).
~
~ It reads the contents of the named files into the buffer, with no
~ separation between them, and returns a pointer to the end of the contents.
~
~ (buffer point, filename list point -- buffer point)
: read-inputs-from-filename-list
~ (buffer point, filename list point)
{ dup 8@ } {
dup 3roll swap pack-file-contents swap
dup stringlen 1+ +
} while drop ;
~ (static source filename list, dynamic source filename list, boot source
~ filename list -- source to precompile, source to copy to log,
~ source to copy textually)
: read-inputs
here @ dup
~ (static source filename list, dynamic source filename list,
~ boot source filename list, source to precompile, buffer point)
4 pick read-inputs-from-filename-list
0 pack8 8 packalign here !
~ (static source filename list, dynamic source filename list,
~ boot source filename list, source to precompile)
here @ dup
~ (static source filename list, dynamic source filename list,
~ boot source filename list, source to precompile, source to copy to log,
~ buffer point)
5 pick read-inputs-from-filename-list
4 pick read-inputs-from-filename-list
0 pack8 8 packalign here !
~ (static source filename list, dynamic source filename list,
~ boot source filename list, source to precompile, source to copy to log)
here @ dup
~ (static source filename list, dynamic source filename list,
~ boot source filename list, source to precompile, source to copy to log,
~ source to copy textually, buffer point)
4 pick read-inputs-from-filename-list
0 pack8 8 packalign here !
~ (static source filename list, dynamic source filename list,
~ boot source filename list, source to precompile, source to copy to log,
~ source to copy textually)
4 roll drop 4 roll drop 4 roll drop ;
~ (output memory start, current output point,
~ compile-header execution token, compile-footer execution token,
~ source to precompile, source to copy to log, source to include textually
~ -- output memory start, current output point)
: output-contents
7 roll 7 roll
~ (compile-header execution token, compile-footer execution token,
~ source to precompile, source to copy to log, source to include textually,
~ output memory start, current output point)
~ Invoke the provided header callback.
7 roll execute
~ (compile-footer execution token, source to precompile,
~ source to copy to log, source to include textually, output memory start,
~ current output point)
elf-file-header
elf-program-header
output-cold-start
~ Warm start and the log-loaded source.
output-warm-start-header
4 roll log-load-transform
~ (compile-footer execution token, source to precompile,
~ source to include textually, output memory start, current output point)
output-warm-start-post-log-load-transform
output-warm-start-attach-boot-source
output-warm-start-footer
~ The label-transformed source.
4 roll label-transform
~ (compile-footer execution token, source to include textually,
~ output memory start, current output point, final entry pointer)
~ The boot source.
4 roll output-boot-source
~ (compile-footer execution token, output memory start,
~ current output point)
~ Invoke the provided footer callback.
3roll execute
~ (output memory start, current output point)
;
~ Given a description of an executable, compile and output that executable
~ as runnable binary, to standard output.
~
~ (static source filename list, dynamic source filename list,
~ boot source filename list, compile-header word name,
~ compile-footer word name --)
: enchant-from-description
5 roll 5 roll 5 roll read-inputs
~ (compile-header word name, compile-footer word name, source to precompile,
~ source to copy to log, source to copy textually)
~ We need an execution token to pass to label-loop. There's no reason to
~ create a dictionary entry for it; we just allocate space for the
~ executable word body directly.
here @
docol ,
6 roll find entry-to-execution-token literal
5 roll find entry-to-execution-token literal
4 roll literal
3 roll literal
swap literal
x' output-contents ,
x' exit ,
label-loop
1 3unroll sys-write
bye ;
~ Given a filename containing the top-level source of an executable, compile
~ and output that executable as runnable binary, to standard output.
~
~ (top-level source filename --)
: enchant
include drop
s" latest-executable-data" find entry-to-execution-token execute
enchant-from-description ;
~ Given a filename containing the top-level source of an executable, compile
~ and output that executable as a commented hex dump, to standard output.
~ Processing the hex dump through "hex" will give an identical binary to what
~ "enchant" would have produced, with the same inputs.
~
~ Please notice that if the thing we're compiling is "evoke" and its list
~ of source files has changed, we'll be working with the new list, not
~ the old list. This is the same as what "enchant" does, and it's strictly
~ preferable behavior. We match the behavior of enchant as closely as
~ possible in terms of what's taken from the host compiler vs. the compiler
~ being compiled; see the description of "enchant", above, for more details
~ and discussion of the implications.
~
~ (top-level source filename --)
: inscribe
dup here @ = { allocate-string } if
dup include drop
s" latest-executable-data" find entry-to-execution-token execute
~ (top-level source filename, static source filename list,
~ dynamic source filename list, boot source filename list,
~ compile-header word name, compile-footer word name --)
~ We begin by constructing the full text of all the source that will be
~ run inside the scope of the hex transform. That necessarily includes not
~ only the compilation machinery, but the transformation system. Notably
~ it does not need to include "inscribe", but does need to include
~ "enchant", and having a stray unused copy of inscribe is harmless, so we
~ don't try to exclude it.
here @ dup
~ (top-level source filename, static source filename list,
~ dynamic source filename list, boot source filename list,
~ compile-header word name, compile-footer word name,
~ hex-transformed source pointer, output point)
6 pick read-inputs-from-filename-list
5 pick read-inputs-from-filename-list
4 pick read-inputs-from-filename-list
8 roll pack-file-contents
~ We need to create a call to "enchant" in the code under transformation.
~ That call doesn't need to be arrived at in exactly the same way as an
~ independent call would be, since it doesn't become part of the output, it
~ only needs to have the same effect.
~
~ So, we can take the filename list pointers that will become parameters
~ to "enchant" and pass them in directly, heedless of the fact they
~ reference locations in the outer log.
s" 0x" pack-raw-string 7 roll pack-hex64
s" 0x" pack-raw-string 6 roll pack-hex64
s" 0x" pack-raw-string 5 roll pack-hex64
~ (compile-header word name, compile-footer word name,
~ hex-transformed source pointer, output point)
~ We do not have that luxury with the two compile hooks; we need copies of
~ them that are under the hex transform. We produce code that looks them up
~ by name. Notice that this is possible because we included the top-level
~ source in our full source text.
~
~ Outputting quotation marks is, as always, a pain.
s" s" pack-raw-string 0x22 pack8 s" " pack-raw-string
4 roll pack-raw-string
0x22 pack8
s" allocate-string" pack-raw-string
s" s" pack-raw-string 0x22 pack8 s" " pack-raw-string
3roll pack-raw-string
0x22 pack8
s" allocate-string" pack-raw-string
~ (hex-transformed source pointer, output point)
s" enchant-from-description" pack-raw-string
~ (hex-transformed source pointer, output point)
0 pack8 8 packalign here !
~ (hex-transformed source pointer)
1024 1024 4 * * allocate
1024 1024 8 * * allocate
~ (hex-transformed source pointer, binary buffer pointer,
~ metadata buffer pointer)
swap dup 4 roll hex-transform bye ;
|