diff options
| author | Irene Knapp <ireneista@irenes.space> | 2026-05-19 00:38:46 -0700 |
|---|---|---|
| committer | Irene Knapp <ireneista@irenes.space> | 2026-05-19 00:38:46 -0700 |
| commit | ebd8be68201201fd86fc4a4e4e0a535738af86bd (patch) | |
| tree | f609d37f12d58ba4dbb5f2a3551cd9344b974c86 /execution-support.e | |
| parent | eec336dea3d86e176c4bd86c435e6be35fec64e2 (diff) | |
yessssssss
so okay, now all the machine code stuff is implemented and it builds without crashing the generated executable still crashes though, but this was enough work that it's getting a celebratory check-in Force-Push: yes Change-Id: I201e6912253647da58ef3537c735b478b0dca9fb
Diffstat (limited to 'execution-support.e')
| -rw-r--r-- | execution-support.e | 107 |
1 files changed, 107 insertions, 0 deletions
diff --git a/execution-support.e b/execution-support.e new file mode 100644 index 0000000..b668157 --- /dev/null +++ b/execution-support.e @@ -0,0 +1,107 @@ +~ Execution support +~ ~~~~~~~~~~~~~~~~~ +~ +~ These macros are an important part of the execution model described in +~ execution.e. They're here, in this file, because they need to be statically +~ available via the label transform, so that the log-load transform can rely +~ on them. + + +~ Macro next +~ ~~~~~~~~~~ +~ +~ Include this inline at the end of a word implemented in machine-code. +~ Conceptually, it returns. What it actually does is do the next thing the +~ caller would do, which is call the next word from the caller's array of +~ word pointers. +~ +~ This is a widespread technique in Forth implementation, referred to as +~ indirect threaded code. It's "threaded" in the sense that each word takes +~ responsibility for finishing up by following the notional thread through the +~ metaphorical labyrinth to figure out the next word that its caller wants to +~ run after it. In other words, control never directly returns to the parent, +~ it proceeds directly to the sibling. +~ +~ Registers in: +~ +~ * rsi points to the address of the word to execute +~ +~ Registers out: +~ +~ * rax points to the codeword in the contents of the word that was executed +~ * rsi points to the next word-address after this one +~ +~ Flags +~ * DF = 0 is required +~ +~ (base address -- new base address) +: pack-next + ~ Copy the next word's address from *rsi into rax. Increment rsi (as per the + ~ DF flag). + lods64 + + ~ Load the codeword from the word's contents, and jump to the interpreter it + ~ points to. + :rax jmp-abs-indirect-reg64 ; + + +~ Macro beforenext +~ ~~~~~~~~~~~~~~~~ +~ +~ Sometimes we want to transfer control from a word implemented in +~ machine-code to another word, without coming back after, as if we were +~ simply jumping to it. This is an innovation of ours; Jonesforth doesn't do +~ it. It is similar to the tail-call optimization that many Lisp dialects +~ have. +~ +~ This implementation will work regardless of how the receiving word is +~ implemented. It impersonates the "next" snippet, setting up rax to point +~ to the codeword then jumping to the interpreter. Since it doesn't change +~ the control stack or rsi, when the receiving word eventually invokes +~ "next"; it will pick up in the same place as if this sending word had done +~ it. +~ +~ Thus, notionally we are doing just this one transfer of control before +~ eventually getting around to inlining "next". Hence the name. +~ +~ (target address, base address -- new base address) +: pack-beforenext + ~ Do a permanent transfer of control by setting rax and invoking the + ~ codeword. Of course, we could jump to docol ourselves but this will work + ~ regardless of what the receiving codeword is. + :rax mov-reg64-imm64 + :rax jmp-abs-indirect-reg64 ; + + +~ Macros pushcontrol +~ popcontrol +~ ~~~~~~~~~~~~~~~~~~ +~ +~ Include these inline to push an address onto the control stack, or pop +~ one off of it. You will recall the control stack is kept in rbp. The +~ parameter is given in a user-specified register. +~ +~ Jonesforth's analogous macros are called PUSHRSP and POPRSP but I think +~ that's super confusing, since rsp is also the name of a register, but a +~ different one. I guess it was less confusing in 32-bit, since esp doesn't +~ start with an "r". Anyway, this has to be named something that +~ distinguishes it from Intel's PUSH and POP opcodes, so... +~ +~ "Load effective address" is just a cute way to do arithmetic on a +~ register, here. To push or pop we decrement or increment rbp by 8. To +~ actually interact with the space in the stack, we indirect through rbp. +~ +~ Registers in and out: +~ +~ * rbp points to the top of the control stack. +~ +~ (source register, base address -- new base address) +: pack-pushcontrol + swap :rbp -8 :rbp lea-reg64-disp8-reg64 + swap :rbp 0 mov-disp8-reg64-reg64 ; + +~ (target register, base address -- new base address) +: pack-popcontrol + :rbp 0 3roll mov-reg64-disp8-reg64 + :rbp 8 :rbp lea-reg64-disp8-reg64 ; + |