/
const-does.html
358 lines (281 loc) · 11.4 KB
/
const-does.html
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
<title>CONST-DOES> proposal</title>
<h3><a name="const-does">CONST-DOES></a></h3>
[<a href="proposals.html">Other proposals</a>]
<h4>Problem</h4>
Many uses of <code>create</code>...<code>does></code> are just for
shifting data from the <code>create</code> time to the execution time
of the code after <code>does></code>; i.e., after the word is fully
defined, the data remains constant. A prototypical example of this
use is the definition
<pre>
: constant ( n "name" -- )
create ,
does> ( -- n )
@ ;
42 constant answer
</pre>
Here, <em>n</em> is just shifted from <code>create</code> time to
<em>name</em> execution time.
<p>It would be nice if a native-code compiler could optimize a use of
<code>answer</code> in the same way that it would optimize a use of
<code>42</code>. However, this is not possible, because the compiler
has to consider the following possibility:
<pre>
5 ' answer >body !
</pre>
I.e., the data in a
<code>create</code>...<code>does></code>-defined word can change at
almost any time. So at best a compiler can compile
<code>answer</code> to the same code as <code>[ ' answer >body ]
literal @</code>.
<p>The effects of this difference on the resulting code depend on the
context. E.g., consider the code <code>answer cells + @</code>: If
the compiler could optimize <code>answer</code> to <code>42</code>, it
could compile this sequence to one instruction on the MIPS
architecture:
<pre>
lw v0,168(a0) ; 42 cells + @
</pre>
Without this optimization, it needs at least five instructions:
<pre>
lui v1,... ; [ ' answer >body ] literal
lw v0,...(v1); @
sll v0,v0,2 ; cells
addu v0,v0,a0 ; +
lw v0,0(v0) ; @
</pre>
So, the problem is how to communicate to the compiler that the data in
<code>answer</code> will not change.
<h4>Proposal</h4>
(See <em>Remarks</em> for an informal, easier-to-understand
description.)
<p>
<code>CONST-DOES></code> ``const-does'' core
<dl>
<dt>Interpretation:<dd> Interpretation semantics for this word are undefined.
<dt>Compilation:<dd>( C: colon-sys1 -- colon-sys2 )
<p>Append the run-time semantics below to the current definition. Whether
or not the current definition is rendered findable in the dictionary
by the compilation of <code>const-does></code> is implementation defined.
<dt>Run-time:<dd> ( u1*x u2*r u1 u2 ``name'' R: nest-sys1 -- )
<p>Create a word <em>name</em> with execution semantics given below.
Return control to the calling definition specified by
<em>nest-sys1</em>. The <em>u1</em> cells and <em>u2</em> floats can
be interleaved in any order.
<dt><em>name</em> execution:<dd> ( ... -- ... )
<p>Perform initiation semantics below. Transfer control to the code
right after the <code>const-does></code>.
<dt>Initiation:<dd> ( -- u1*x u2*r R: nest-sys2 )
<p>Save information <em>next-sys2</em> about the calling definition.
After pushing the <em>u1</em> cells and <em>u2</em> floats, they are
in the same order as they were at the start of the run-time semantics.
</dl>
<h4>Typical use</h4>
<pre>
: constant ( n "name" -- )
1 0 const-does> ( -- n )
;
: fconstant ( r "name" -- )
0 1 const-does> ( -- r )
;
: simple-field ( n "name" -- )
1 0 const-does> ( addr1 -- addr2 )
+ ;
</pre>
Note that the stack comments after <code>const-does></code> reflect the
total stack effect of <em>name</em> (including initiation semantics),
not the stack effect of the following code.
<h4>Remarks</h4>
The ANS-Forth-style formal proposal may be a bit hard to penetrate, so
here are the essentials: <code>Const-does></code> defines a word
(the role of <code>create</code>) and its behaviour (the role of
<code>does></code>). The main other thing it does is to shift
<em>u1</em> cells and <em>u2</em> floats from the definition time of
<em>name</em> to its execution time. As a consequence, a simple
definition like <code>constant</code> specifies just how many cells
and floats it wants to shift, and needs to do nothing else.
<p>Note that this works for both separate and combined data/FP stacks: On
a system with separate stacks <code>const-does></code> shifts <em>u1</em>
cells and <em>u2</em> floats from definition to execution. On system
with a combined stack is just shifts as many cells as these cells and
floats take.
<p>An optimizing native code compiler could compile a word defined with
<code>const-does></code> by compiling the <em>u1</em> cells and <em>u2</em>
floats as literals, and then compiling (and possibly inlining) a call to
the code behind the <code>const-does></code>. The compiler would know that
these literals are constant, and could optimize accordingly.
<p>There are several alternative approaches to attack the problem:
<ul>
<li>An ANS-compliant way is to use colon definitions and
literals. E.g.:
<pre>
: constant ( n "name" -- )
\ name execution: ( -- n )
>r : r> POSTPONE literal POSTPONE ; ;
: fconstant ( r "name" -- )
\ name execution: ( -- r )
\ environmental dependency: separate FP stack
: POSTPONE fliteral POSTPONE ; ;
: simple-field ( n "name" -- )
\ name execution: ( addr1 -- addr2 )
>r : r> POSTPONE literal POSTPONE + POSTPONE ; ;
</pre>
A good compiler could inline the resulting colon definitions and
optimize the results.
<p>The downside of this approach is that it is hard to read (even with
<code>]]</code>...<code>[[</code> to eliminate the
<code>POSTPONEs</code>). And to avoid the environmental dependency
for passing floats, we would have to save them in global variables,
which would make the code even harder to read.
<li>An alternative syntax would make <code>does-const></code> behave
like <code>does></code>, but it would remove the ability to apply
<code>>body</code> to the execution token of words it is applied
to. Advantage: The syntax would be familiar and thus easier to learn.
Disadvantages: Optimizing this would require a more complex compiler.
The defining word is cluttered with code for saving and restoring the
data to be shifted between definition and execution, making the code
harder to read. Simple implementations would not enforce the
restriction on not applying <code>>body</code>, leading to
portability bugs (in contrast, the proposed
<code>const-does></code> does not expose the placement of the data,
so a programmer cannot even access the data without learning
implementation details).
<li>In a similar vein, Stephen Pelc suggested defining the idiom
<code>constant</code>...<code>does></code> (currently non-standard,
but widely used) to mean that the body must not be changed. The
advantages and disadvantages are similar to
<code>does-const></code> above. Additional disadvantages: hard to
implement in Forth systems where constants are not implemented in the
traditional way; the syntax becomes quite unnatural when more than two
cells or one float have to be transferred from definition-time to
run-time.
<li>Stephen Pelc also suggested a word <code>invariant ( addr u --
)</code> that would just tell the compiler that a memory area would
not change; this could be used to solve the problem like this:
<pre>
: constant ( n "name" -- )
create here >r ,
r> 1 cells invariant
does> ( -- n )
@ ;
</pre>
The advantage of this approach is that it is more general
(<code>invariant</code> could also be used to optimize accesses to
other unchanging memory areas). The disadvantages are decreased
readability and more complexity in optimizing (it requires a new
optimization, whereas <code>const-does></code> can be optimized
through inlining).
<li>One implementation issue for this proposal is that <em>u1</em> and
<em>u2</em> are supplied at run-time. If the numbers were supplied at
compile-time (with a syntax like <code>[ 1 0 ]does-code></code>), a
native-code compiler using this for optimization could be slightly
simpler (and the reference implementation below could be faster).
However, a native-code compiler probably can use the same mechanism
here that it uses for optimizing <code>pick</code>, and supplying the
number at run-time is easier for the user (both syntactically and
mentally) and more flexible.
</ul>
<h4>Reference implementation</h4>
This ANS Forth implementation of <code>const-does></code> behaves as it
should, but does not give you the performance advantages
(rather to the contrary).
<pre>
: const-does>-prelude ( u1*x u2*r u1 u2 ``name'' -- )
\ create name and store u1*x u2*r there
create 2dup 2,
over cells allot here >r
falign dup floats allot here ( u1*x u2*r u1 u2 addr2 )
swap 0 ?do
-1 floats + dup f!
loop
drop r> ( u1*x u1 addr1 )
swap 0 ?do
-1 cells + tuck !
loop
drop ;
: const-does>-postlude ( addr -- u1*x u2*r )
\ fetch u1*x u2*r from addr
dup 2 cells +
swap 2@ >r
0 ?do
dup @ swap cell+
loop
faligned
r> 0 ?do
dup f@ float+
loop
drop ;
: const-does> ( compilation: colon-sys1 -- colon-sys2 )
\ run-time: ( u1*x u2*r u1 u2 ``name'' R: nest-sys1 -- )
\ name initiation: ( -- u1*x u2*r R: nest-sys2 )
POSTPONE const-does>-prelude
POSTPONE does>
POSTPONE const-does>-postlude
; immediate
</pre>
The following implementation is used in Gforth; it translates the
defined words into colon definitions containing literals and a call,
like this:
<pre>
\ input:
: simple-field ( n "name" -- )
1 0 const-does> ( addr1 -- addr2 )
+ ;
8 simple-field field1
\ SEE output:
: simple-field
1 0 249544 (const-does>) ;
\ 249544 xt-see
noname :
+ ;
: field1
8 <249544> ;
</pre>
The implementation code itself is not quite ANS Forth compliant, but
porting it to other systems should not be hard:
<pre>
: compile-literals ( w*u u -- ; run-time: -- w*u ) recursive
\ compile u literals, starting with the bottommost one
?dup-if
swap >r 1- compile-literals
r> POSTPONE literal
endif ;
: compile-fliterals ( r*u u -- ; run-time: -- w*u ) recursive
\ compile u fliterals, starting with the bottommost one
?dup-if
{ F: r } 1- compile-fliterals
r POSTPONE fliteral
endif ;
: (const-does>) ( w*uw r*ur uw ur target "name" -- )
\ define a colon definition "name" containing w*uw r*ur as
\ literals and a call to target.
{ uw ur target }
header docol: cfa, \ start colon def without stack junk
ur compile-fliterals uw compile-literals
target compile, POSTPONE exit reveal ;
: const-does> ( run-time: w*uw r*ur uw ur "name" -- )
here >r 0 POSTPONE literal
POSTPONE (const-does>)
POSTPONE ;
noname : POSTPONE rdrop
lastxt r> cell+ ! \ patch the literal
; immediate
</pre>
<h4>Experience</h4>
<code>Const-does></code> is implemented in Gforth since November 2000.
<h4>Comments</h4>
Stephen Pelc pointed out that <code>const-does></code> is not
practical for defining words that contain larger tables (you don't
want to pass the table from definition-time to use-time on the stack).
<p>This is true, but such words are rare in my experience, and uses of
such words where the fetches can be optimized are probably even more
rare (usually you will index the table with a value that has to be
computed at run-time, and then you cannot optimize the fetch anyway).
For the exceptions a solution based on <code>invariant</code> would
help.
Guido Draheim:
<pre>
`const-does>` is good stuff - although I would like to have an alias `does>@`
where just the specification says that `to` won't work.
</pre>
<hr><a href="http://www.complang.tuwien.ac.at/anton/">Anton Ertl</a>