about summary refs log tree commit diff
path: root/compilation/top-level.e
blob: 7c714cbcdef7842a191af3bb068d6008d8bf020d (plain)
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
~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~ ~~ 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.


: compilation-definition-top-level-filename ;
: compilation-definition-static-filenames 8 + ;
: compilation-definition-dynamic-filenames 2 8 * + ;
: compilation-definition-boot-filenames 3 8 * + ;
: compilation-definition-compile-header 4 8 * + ;
: compilation-definition-compile-footer 5 8 * + ;
: allocate-compilation-definition
  6 8 * allocate ;


~   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 compilation-definition structure which is a description of an
~ executable, compile and output that executable as runnable binary, to
~ standard output.
~
~ (definition structure pointer --)
: enchant-from-definition
  dup dup dup
  compilation-definition-boot-filenames @ 3unroll
  compilation-definition-dynamic-filenames @ 3unroll
  compilation-definition-static-filenames @ 3unroll
  read-inputs
  ~ (definition structure pointer, 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 ,
  4 pick compilation-definition-compile-header @ literal
  4 pick compilation-definition-compile-footer @ literal
  4 roll literal
  3 roll literal
  swap literal
  x' output-contents ,
  x' exit ,

  label-loop
  1 3unroll sys-write
  bye ;


~   Look up the word "latest-executable-data" dynamically, thus taking the
~ most recent copy of it at runtime, invoke it, and use the resulting value
~ as the compilation definition for enchant-from-definition.
~
~   The typical way to use this word is immediately after including the
~ top-level source file of the executable you want to enchant.
: enchant
  s" latest-executable-data" find entry-to-execution-token execute
  enchant-from-definition ;


~   Given a compilation-definition structure which is a description of an
~ executable, compile and output that executable as commented hex dump, to
~ standard output. Processing that 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.
~
~ (definition structure pointer --)
: inscribe-from-definition
  ~   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
  ~ (definition structure pointer, hex-transformed source pointer,
  ~  output point)

  2 pick compilation-definition-static-filenames @
  read-inputs-from-filename-list

  2 pick compilation-definition-dynamic-filenames @
  read-inputs-from-filename-list

  2 pick compilation-definition-boot-filenames @
  read-inputs-from-filename-list

  ~   The top-level source file will try to look up its own filename, but this
  ~ is all on standard input, so it doesn't have one. We must deceive it!
  s"  : current-input-file-name [ s" pack-raw-string
  ~ Outputting quotation marks is, as always, a pain.
  0x22 pack8 s"  " pack-raw-string
  2 pick compilation-definition-top-level-filename @ pack-raw-string
  0x22 pack8
  s"  literal-string ] ; " pack-raw-string

  ~ Having set that up, now we can include the actual top-level file.
  2 pick compilation-definition-top-level-filename @
  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.
  ~
  ~   With that said, though, since one way or another it's going to be
  ~ looking up latest-executable-data dynamically, there's no reason to try
  ~ any sophisticated means of passing individual fields or anything.
  ~
  ~   It might seem as if that's an unnecessary level of indirection, but for
  ~ the two compile hooks we specifically need to make sure the enchantment
  ~ uses the copies of them that are under the hex transform. So, one way or
  ~ another it's always relying on the code from the top-level source file.
  s"  enchant" pack-raw-string
  ~ (definition struction pointer, hex-transformed source pointer,
  ~  output point)
  0 pack8 8 packalign here !

  ~ That's everything we need from the definition.
  swap drop
  ~ (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 ;


~   Look up the word "latest-executable-data" dynamically, thus taking the
~ most recent copy of it at runtime, invoke it, and use the resulting value
~ as the compilation definition for enchant-from-definition.
~
~   The typical way to use this word is immediately after including the
~ top-level source file of the executable you want to inscribe.
: inscribe
  s" latest-executable-data" find entry-to-execution-token execute
  inscribe-from-definition ;