Skip to content

Commit c35e261

Browse files
committed
Move optimizations to a new file.
1 parent fa13648 commit c35e261

File tree

2 files changed

+84
-72
lines changed

2 files changed

+84
-72
lines changed

pkg/forth/optimize.go

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package forth
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
)
7+
8+
type Optimizer struct {
9+
u *Ulp
10+
}
11+
12+
func (o *Optimizer) Optimize() error {
13+
// mark all recursive words for later passes
14+
err := o.tagRecursion()
15+
if err != nil {
16+
return errors.Join(fmt.Errorf("could not tag recursion during optimization"), err)
17+
}
18+
// change calls at end of words to tail calls
19+
err = o.putTailCalls()
20+
if err != nil {
21+
return errors.Join(fmt.Errorf("could not create tail calls, please file a bug report"), err)
22+
}
23+
return nil
24+
}
25+
26+
// Mark the flag of every word that has recursion.
27+
func (o *Optimizer) tagRecursion() error {
28+
// unmark all of the words
29+
for _, w := range o.u.forthWords {
30+
w.Entry.Flag.recursive = false
31+
}
32+
for _, w := range o.u.forthWords {
33+
o.clearVisited() // clear every word every time
34+
for _, c := range w.Cells { // check every cell in that word
35+
if c.IsRecursive(w) {
36+
w.Entry.Flag.recursive = true
37+
break
38+
}
39+
}
40+
}
41+
return nil
42+
}
43+
44+
// Put tail calls as an optimization.
45+
func (o *Optimizer) putTailCalls() error {
46+
for _, w := range o.u.forthWords {
47+
length := len(w.Cells) - 1
48+
for i := 0; i < length; i++ {
49+
// check if the first cell is a forth word
50+
firstAddress, ok := w.Cells[i].(CellAddress)
51+
if !ok {
52+
continue
53+
}
54+
word, ok := firstAddress.Entry.Word.(*WordForth)
55+
if !ok {
56+
continue
57+
}
58+
// check if the second cell is an EXIT
59+
secondAddress, ok := w.Cells[i+1].(CellAddress)
60+
if !ok {
61+
continue
62+
}
63+
if !secondAddress.Entry.Flag.isExit {
64+
continue
65+
}
66+
// replace both cells with the tail call!
67+
tailCall := CellTailCall{word} // create the tail call
68+
w.Cells[i] = &tailCall // replace the word
69+
before := w.Cells[:i+1] // get the cells before, including the tail call
70+
after := w.Cells[i+2:] // get the cells after, excluding the exit
71+
w.Cells = append(before, after...) // recreate the list
72+
length -= 1 // shift the length
73+
}
74+
}
75+
return nil
76+
}
77+
78+
func (o *Optimizer) clearVisited() {
79+
for _, w := range o.u.forthWords {
80+
w.Entry.ClearVisited()
81+
}
82+
}

pkg/forth/ulpBuild.go

Lines changed: 2 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,8 @@ func (u *Ulp) buildAssemblyHelper(vm *VirtualMachine, header string) (string, er
157157
return "", err
158158
}
159159
// optimize!
160-
err = u.optimize()
160+
optimizer := Optimizer{u}
161+
err = optimizer.Optimize()
161162
if err != nil {
162163
return "", err
163164
}
@@ -201,77 +202,6 @@ func (u *Ulp) buildAssemblyHelper(vm *VirtualMachine, header string) (string, er
201202
return strings.Join(i, "\r\n"), nil
202203
}
203204

204-
func (u *Ulp) optimize() error {
205-
// mark all recursive words for later passes
206-
err := u.tagRecursion()
207-
if err != nil {
208-
return errors.Join(fmt.Errorf("could not tag recursion during optimization"), err)
209-
}
210-
err = u.putTailCalls()
211-
if err != nil {
212-
return errors.Join(fmt.Errorf("could not create tail calls, please file a bug report"), err)
213-
}
214-
return nil
215-
}
216-
217-
// Mark the flag of every word that has recursion.
218-
func (u *Ulp) tagRecursion() error {
219-
// unmark all of the words
220-
for _, w := range u.forthWords {
221-
w.Entry.Flag.recursive = false
222-
}
223-
for _, w := range u.forthWords {
224-
u.clearVisited() // clear every word every time
225-
for _, c := range w.Cells { // check every cell in that word
226-
if c.IsRecursive(w) {
227-
w.Entry.Flag.recursive = true
228-
break
229-
}
230-
}
231-
}
232-
return nil
233-
}
234-
235-
// Put tail calls as an optimization.
236-
func (u *Ulp) putTailCalls() error {
237-
for _, w := range u.forthWords {
238-
length := len(w.Cells) - 1
239-
for i := 0; i < length; i++ {
240-
// check if the first cell is a forth word
241-
firstAddress, ok := w.Cells[i].(CellAddress)
242-
if !ok {
243-
continue
244-
}
245-
word, ok := firstAddress.Entry.Word.(*WordForth)
246-
if !ok {
247-
continue
248-
}
249-
// check if the second cell is an EXIT
250-
secondAddress, ok := w.Cells[i+1].(CellAddress)
251-
if !ok {
252-
continue
253-
}
254-
if !secondAddress.Entry.Flag.isExit {
255-
continue
256-
}
257-
// replace both cells with the tail call!
258-
tailCall := CellTailCall{word} // create the tail call
259-
w.Cells[i] = &tailCall // replace the word
260-
before := w.Cells[:i+1] // get the cells before, including the tail call
261-
after := w.Cells[i+2:] // get the cells after, excluding the exit
262-
w.Cells = append(before, after...) // recreate the list
263-
length -= 1 // shift the length
264-
}
265-
}
266-
return nil
267-
}
268-
269-
func (u *Ulp) clearVisited() {
270-
for _, w := range u.forthWords {
271-
w.Entry.ClearVisited()
272-
}
273-
}
274-
275205
// Convert list of used subroutine-threaded assembly
276206
// words into a string.
277207
func (u *Ulp) buildAssemblyWords() (string, error) {

0 commit comments

Comments
 (0)