Reorganize source tree: kernel/, v3/, v4/ split and board infrastructure
Source tree reorganization: - Move StarForth v3 engine to v3/ (src/, include/, Makefile) - Move kernel to kernel/ (src/, include/, linker/, Makefile) - Create v4/ skeleton for F18-ISA golden model (DECOMPOSITION.md, JUSTIFICATION.md) - Move FABRIC-0..4.md to docs/fabric/ - Move ONTOLOGY.md and ROADMAP.md to docs/ Board infrastructure: - Add boards/ser5/, boards/raspi/, boards/milkv/, boards/zynq7020/ - Each board has board.mk (ISA, CPU flags, boot recipe) and README.md - Root Makefile becomes thin dispatcher: boot_image, all, clean, docs take TARGET - make boot_image TARGET=SER5|RASPI|MILKV builds one GPT/MBR image per board - ZYNQ7020 target exists but stops with clear error (ARMv7 port not built yet) - scripts/mkdiskimage.sh builds disk images for all boards Docs pipeline: - docs/book/ with LaTeX master (main.tex) and Makefile - pandoc converts Markdown to LaTeX at build time - Two Lua filters: table-widths.lua (wide tables wrap), code-breaks.lua (inline code breaks) - make docs builds single PDF (754 pages, 0 missing characters) - make docs TARGET=<board> adds board appendix - build/docs/<book|board>/meta.tex stamps git commit into PDF Bug fixes: - 42 include paths that only worked by accident now use correct relative paths - clang-18 hardcode replaced with configurable CC variable (fixed aarch64 build) - Pi 5: kernel_2712.img linked at 0x80000, .bss zeroed, memory reserved - Doxyfile, .clang-tidy, README.md, Kconfig paths updated Verified: - Hosted v3 build passes 1012 tests, 0 failures - SER5 image boots in QEMU (OVMF), POST passes, K exact (65536 = Q48_ONE) - Milk-V image boots in QEMU (OpenSBI + U-Boot + bootefi), POST passes - make clean TARGET=<board> removes only that board and its ISA objects - make all builds all boards, hosted v3, and docs in one run Co-authored-by: Junie <junie@jetbrains.com>
This commit is contained in:
@@ -0,0 +1,643 @@
|
||||
/*
|
||||
StarForth — Steady-State Virtual Machine Runtime
|
||||
|
||||
Copyright (c) 2023–2025 Robert A. James
|
||||
All rights reserved.
|
||||
|
||||
This file is part of the StarForth project.
|
||||
|
||||
Licensed under the StarForth License, Version 1.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
||||
You may obtain a copy of the License at:
|
||||
https://github.com/star.4th@proton.me/StarForth/LICENSE.txt
|
||||
|
||||
This software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
express or implied, including but not limited to the warranties of
|
||||
merchantability, fitness for a particular purpose, and noninfringement.
|
||||
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
StarForth — Steady-State Virtual Machine Runtime
|
||||
Copyright (c) 2023–2025 Robert A. James
|
||||
All rights reserved.
|
||||
|
||||
This file is part of the StarForth project.
|
||||
|
||||
Licensed under the StarForth License, Version 1.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
||||
You may obtain a copy of the License at:
|
||||
https://github.com/star.4th@proton.me/StarForth/LICENSE.txt
|
||||
|
||||
This software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
express or implied, including but not limited to the warranties of
|
||||
merchantability, fitness for a particular purpose, and noninfringement.
|
||||
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
*** StarForth ***
|
||||
vm_inner_interp_asm.h - Optimized Inner Interpreter using Direct Threading
|
||||
Compatible with: Linux, bare metal x86_64
|
||||
|
||||
HISTORICAL: the "L4Re Compatibility"/"L4RE INTEGRATION NOTES" throughout
|
||||
this file describe a target that was supported through mid-2026. L4Re
|
||||
support has since been removed as an active target; these notes are
|
||||
retained as historical design rationale.
|
||||
|
||||
This work is released under license terms in LICENSE file.
|
||||
No warranty. Use at your own risk.
|
||||
*/
|
||||
|
||||
#ifndef VM_INNER_INTERP_ASM_H
|
||||
#define VM_INNER_INTERP_ASM_H
|
||||
|
||||
#include "vm.h"
|
||||
|
||||
/*
|
||||
* INNER INTERPRETER OPTIMIZATION
|
||||
* ===============================
|
||||
*
|
||||
* This is the MOST CRITICAL optimization for Forth performance.
|
||||
*
|
||||
* Background:
|
||||
* -----------
|
||||
* Forth uses "threaded code" where compiled words are sequences of pointers
|
||||
* to other words. The inner interpreter's job is to:
|
||||
* 1. Fetch the next word pointer (IP)
|
||||
* 2. Execute that word
|
||||
* 3. Increment IP
|
||||
* 4. Repeat
|
||||
*
|
||||
* Traditional C implementation has massive overhead:
|
||||
* - Function call/return for each word (push/pop return address)
|
||||
* - Register spilling
|
||||
* - No tail-call optimization in most C compilers
|
||||
*
|
||||
* This optimization uses:
|
||||
* - Direct threading: Each word ends with a jump to NEXT
|
||||
* - Register allocation: Keep IP, SP, RP in registers
|
||||
* - Tail calls: Use jmp instead of call/ret
|
||||
* - Computed goto: Fast dispatch using indirect jump
|
||||
*
|
||||
* Performance Impact:
|
||||
* ------------------
|
||||
* Can improve interpreter speed by 2-5x for threaded code execution
|
||||
*
|
||||
* L4Re Compatibility: YES
|
||||
* All operations are unprivileged and pure computation
|
||||
*/
|
||||
|
||||
#ifndef USE_DIRECT_THREADING
|
||||
#define USE_DIRECT_THREADING 0
|
||||
#endif
|
||||
|
||||
#if USE_DIRECT_THREADING
|
||||
|
||||
/**
|
||||
* @brief Register allocation strategy for optimized execution
|
||||
*
|
||||
* Register usage:
|
||||
* @arg r12 VM pointer (callee-saved, preserved across calls)
|
||||
* @arg r13 Instruction Pointer (IP) - points to current cell
|
||||
* @arg r14 Data Stack Pointer (DSP) - points to vm->data_stack[vm->dsp]
|
||||
* @arg r15 Return Stack Pointer (RSP) - points to vm->return_stack[vm->rsp]
|
||||
*
|
||||
* Callee-saved registers are used because:
|
||||
* - They're preserved across function calls
|
||||
* - Allows mixing C and assembly code
|
||||
* - No need to save/restore on each word execution
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Core inner interpreter macro - executes next word
|
||||
* @details Fetches next word pointer from IP, increments IP, and jumps to word
|
||||
*/
|
||||
#define NEXT_ASM() \
|
||||
__asm__ __volatile__ ( \
|
||||
"movq (%%r13), %%rax\n\t" /* Load word pointer from [IP] */ \
|
||||
"addq $8, %%r13\n\t" /* IP++ */ \
|
||||
"jmpq *%%rax\n\t" /* Jump to word's code */ \
|
||||
::: "rax", "memory" \
|
||||
)
|
||||
|
||||
/**
|
||||
* @brief Enter a colon definition (DOCOL)
|
||||
* @param vm Pointer to VM instance
|
||||
* @details Called when executing a user-defined word
|
||||
*/
|
||||
static inline void vm_docol_asm(VM *vm) {
|
||||
__asm__ __volatile__(
|
||||
/* Save current IP on return stack */
|
||||
"movq %%r13, (%%r15)\n\t" /* *RSP = IP */
|
||||
"addq $8, %%r15\n\t" /* RSP++ */
|
||||
|
||||
/* IP = address of colon definition body (from DictEntry) */
|
||||
"movq %[entry], %%rax\n\t"
|
||||
"movq (%%rax), %%r13\n\t" /* IP = entry->code_field */
|
||||
|
||||
/* Continue with NEXT */
|
||||
"movq (%%r13), %%rax\n\t"
|
||||
"addq $8, %%r13\n\t"
|
||||
"jmpq *%%rax\n\t"
|
||||
:
|
||||
: [entry]"r"(vm->current_executing_entry)
|
||||
: "rax", "r13", "r15", "memory"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return from a colon definition (EXIT)
|
||||
* @details Pops IP from return stack and continues execution
|
||||
*/
|
||||
static inline void vm_exit_asm(void) {
|
||||
__asm__ __volatile__(
|
||||
/* Restore IP from return stack */
|
||||
"subq $8, %%r15\n\t" /* RSP-- */
|
||||
"movq (%%r15), %%r13\n\t" /* IP = *RSP */
|
||||
|
||||
/* Continue with NEXT */
|
||||
"movq (%%r13), %%rax\n\t"
|
||||
"addq $8, %%r13\n\t"
|
||||
"jmpq *%%rax\n\t"
|
||||
::: "rax", "r13", "r15", "memory"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Main interpreter loop implementation
|
||||
* @details Replaces execute_colon_word function with optimized assembly
|
||||
*
|
||||
* Usage example:
|
||||
* @code
|
||||
* vm_setup_registers(vm);
|
||||
* VM_INNER_LOOP();
|
||||
* @endcode
|
||||
*/
|
||||
|
||||
/* Setup registers before entering inner loop */
|
||||
static inline void vm_setup_registers(VM *vm) {
|
||||
__asm__ __volatile__(
|
||||
"movq %[vm], %%r12\n\t" /* R12 = VM pointer */
|
||||
"movq %[ip], %%r13\n\t" /* R13 = IP */
|
||||
|
||||
/* Calculate actual stack pointers */
|
||||
"movl %[dsp], %%eax\n\t"
|
||||
"leaq (%[dstack], %%rax, 8), %%r14\n\t" /* R14 = &data_stack[dsp] */
|
||||
|
||||
"movl %[rsp], %%eax\n\t"
|
||||
"leaq (%[rstack], %%rax, 8), %%r15\n\t" /* R15 = &return_stack[rsp] */
|
||||
:
|
||||
: [vm]"r"(vm),
|
||||
[ip]"r"(vm->ip),
|
||||
[dsp]"m"(vm->dsp),
|
||||
[rsp]"m"(vm->rsp),
|
||||
[dstack]"r"(vm->data_stack),
|
||||
[rstack]"r"(vm->return_stack)
|
||||
: "r12", "r13", "r14", "r15", "rax", "memory"
|
||||
);
|
||||
}
|
||||
|
||||
/* Save registers back to VM structure */
|
||||
static inline void vm_save_registers(VM *vm) {
|
||||
__asm__ __volatile__(
|
||||
"movq %%r13, %[ip]\n\t" /* Save IP */
|
||||
|
||||
/* Calculate dsp from R14 */
|
||||
"movq %%r14, %%rax\n\t"
|
||||
"subq %[dstack], %%rax\n\t"
|
||||
"shrq $3, %%rax\n\t" /* / 8 */
|
||||
"movl %%eax, %[dsp]\n\t"
|
||||
|
||||
/* Calculate rsp from R15 */
|
||||
"movq %%r15, %%rax\n\t"
|
||||
"subq %[rstack], %%rax\n\t"
|
||||
"shrq $3, %%rax\n\t"
|
||||
"movl %%eax, %[rsp]\n\t"
|
||||
: [ip]"=m"(vm->ip),
|
||||
[dsp]"=m"(vm->dsp),
|
||||
[rsp]"=m"(vm->rsp)
|
||||
: [dstack]"r"(vm->data_stack),
|
||||
[rstack]"r"(vm->return_stack)
|
||||
: "rax", "memory"
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* Fast primitive word implementations using register-based stacks
|
||||
*
|
||||
* These use the register-allocated stack pointers (R14/R15) directly
|
||||
* without going through memory or function calls
|
||||
*/
|
||||
|
||||
/* DUP - duplicate top of stack */
|
||||
#define PRIM_DUP() \
|
||||
__asm__ __volatile__ ( \
|
||||
"movq (%%r14), %%rax\n\t" /* Load TOS */ \
|
||||
"addq $8, %%r14\n\t" /* DSP++ */ \
|
||||
"movq %%rax, (%%r14)\n\t" /* Store duplicate */ \
|
||||
::: "rax", "r14", "memory" \
|
||||
)
|
||||
|
||||
/* DROP - remove top of stack */
|
||||
#define PRIM_DROP() \
|
||||
__asm__ __volatile__ ( \
|
||||
"subq $8, %%r14\n\t" /* DSP-- */ \
|
||||
::: "r14" \
|
||||
)
|
||||
|
||||
/* SWAP - swap top two stack items */
|
||||
#define PRIM_SWAP() \
|
||||
__asm__ __volatile__ ( \
|
||||
"movq (%%r14), %%rax\n\t" /* Load TOS */ \
|
||||
"movq -8(%%r14), %%rcx\n\t" /* Load NOS */ \
|
||||
"movq %%rcx, (%%r14)\n\t" /* Store NOS to TOS */ \
|
||||
"movq %%rax, -8(%%r14)\n\t" /* Store TOS to NOS */ \
|
||||
::: "rax", "rcx", "memory" \
|
||||
)
|
||||
|
||||
/* + - add top two stack items */
|
||||
#define PRIM_PLUS() \
|
||||
__asm__ __volatile__ ( \
|
||||
"movq (%%r14), %%rax\n\t" /* Load TOS */ \
|
||||
"addq %%rax, -8(%%r14)\n\t" /* Add to NOS */ \
|
||||
"subq $8, %%r14\n\t" /* DSP-- */ \
|
||||
::: "rax", "r14", "cc", "memory" \
|
||||
)
|
||||
|
||||
/* - - subtract top from second */
|
||||
#define PRIM_MINUS() \
|
||||
__asm__ __volatile__ ( \
|
||||
"movq (%%r14), %%rax\n\t" /* Load TOS */ \
|
||||
"subq %%rax, -8(%%r14)\n\t" /* Subtract from NOS */ \
|
||||
"subq $8, %%r14\n\t" /* DSP-- */ \
|
||||
::: "rax", "r14", "cc", "memory" \
|
||||
)
|
||||
|
||||
/* * - multiply top two stack items */
|
||||
#define PRIM_STAR() \
|
||||
__asm__ __volatile__ ( \
|
||||
"movq (%%r14), %%rax\n\t" /* Load TOS */ \
|
||||
"imulq -8(%%r14), %%rax\n\t" /* Multiply with NOS */ \
|
||||
"movq %%rax, -8(%%r14)\n\t" /* Store result */ \
|
||||
"subq $8, %%r14\n\t" /* DSP-- */ \
|
||||
::: "rax", "rdx", "r14", "cc", "memory" \
|
||||
)
|
||||
|
||||
/* @ - fetch cell from memory */
|
||||
#define PRIM_FETCH() \
|
||||
__asm__ __volatile__ ( \
|
||||
"movq (%%r14), %%rax\n\t" /* Load address from TOS */ \
|
||||
"movq (%%r12), %%rcx\n\t" /* Load VM->memory */ \
|
||||
"movq (%%rcx, %%rax, 1), %%rax\n\t" /* Load cell from memory */ \
|
||||
"movq %%rax, (%%r14)\n\t" /* Store to TOS */ \
|
||||
::: "rax", "rcx", "memory" \
|
||||
)
|
||||
|
||||
/* ! - store cell to memory */
|
||||
#define PRIM_STORE() \
|
||||
__asm__ __volatile__ ( \
|
||||
"movq (%%r14), %%rax\n\t" /* Load address from TOS */ \
|
||||
"movq -8(%%r14), %%rcx\n\t" /* Load value from NOS */ \
|
||||
"movq (%%r12), %%rdx\n\t" /* Load VM->memory */ \
|
||||
"movq %%rcx, (%%rdx, %%rax, 1)\n\t" /* Store to memory */ \
|
||||
"subq $16, %%r14\n\t" /* DSP -= 2 */ \
|
||||
::: "rax", "rcx", "rdx", "r14", "memory" \
|
||||
)
|
||||
|
||||
/* >R - push to return stack */
|
||||
#define PRIM_TO_R() \
|
||||
__asm__ __volatile__ ( \
|
||||
"movq (%%r14), %%rax\n\t" /* Load from TOS */ \
|
||||
"subq $8, %%r14\n\t" /* DSP-- */ \
|
||||
"movq %%rax, (%%r15)\n\t" /* Store to return stack */ \
|
||||
"addq $8, %%r15\n\t" /* RSP++ */ \
|
||||
::: "rax", "r14", "r15", "memory" \
|
||||
)
|
||||
|
||||
/* R> - pop from return stack */
|
||||
#define PRIM_R_FROM() \
|
||||
__asm__ __volatile__ ( \
|
||||
"subq $8, %%r15\n\t" /* RSP-- */ \
|
||||
"movq (%%r15), %%rax\n\t" /* Load from return stack */ \
|
||||
"addq $8, %%r14\n\t" /* DSP++ */ \
|
||||
"movq %%rax, (%%r14)\n\t" /* Store to TOS */ \
|
||||
::: "rax", "r14", "r15", "memory" \
|
||||
)
|
||||
|
||||
/* R@ - copy top of return stack to data stack */
|
||||
#define PRIM_R_FETCH() \
|
||||
__asm__ __volatile__ ( \
|
||||
"movq -8(%%r15), %%rax\n\t" /* Load from top of return stack */ \
|
||||
"addq $8, %%r14\n\t" /* DSP++ */ \
|
||||
"movq %%rax, (%%r14)\n\t" /* Store to TOS */ \
|
||||
::: "rax", "r14", "memory" \
|
||||
)
|
||||
|
||||
/*
|
||||
* BRANCH and 0BRANCH - Control flow primitives
|
||||
* These modify IP based on conditions
|
||||
*/
|
||||
|
||||
/* BRANCH - unconditional branch */
|
||||
#define PRIM_BRANCH() \
|
||||
__asm__ __volatile__ ( \
|
||||
"movq (%%r13), %%rax\n\t" /* Load offset from IP */ \
|
||||
"addq %%rax, %%r13\n\t" /* IP += offset */ \
|
||||
::: "rax", "r13", "memory" \
|
||||
)
|
||||
|
||||
/* 0BRANCH - branch if TOS is zero */
|
||||
#define PRIM_ZBRANCH() \
|
||||
__asm__ __volatile__ ( \
|
||||
"movq (%%r14), %%rax\n\t" /* Load TOS */ \
|
||||
"subq $8, %%r14\n\t" /* DSP-- */ \
|
||||
"testq %%rax, %%rax\n\t" /* Test if zero */ \
|
||||
"jnz 1f\n\t" /* Skip if non-zero */ \
|
||||
"movq (%%r13), %%rax\n\t" /* Load offset */ \
|
||||
"addq %%rax, %%r13\n\t" /* IP += offset */ \
|
||||
"jmp 2f\n\t" \
|
||||
"1:\n\t" \
|
||||
"addq $8, %%r13\n\t" /* Skip offset */ \
|
||||
"2:\n\t" \
|
||||
::: "rax", "r13", "r14", "cc", "memory" \
|
||||
)
|
||||
|
||||
/* OVER - copy second item to top of stack */
|
||||
#define PRIM_OVER() \
|
||||
__asm__ __volatile__ ( \
|
||||
"movq -8(%%r14), %%rax\n\t" /* Load NOS */ \
|
||||
"addq $8, %%r14\n\t" /* DSP++ */ \
|
||||
"movq %%rax, (%%r14)\n\t" /* Store NOS as new TOS */ \
|
||||
::: "rax", "r14", "memory" \
|
||||
)
|
||||
|
||||
/* ROT - rotate top three items ( a b c -- b c a ) */
|
||||
#define PRIM_ROT() \
|
||||
__asm__ __volatile__ ( \
|
||||
"movq (%%r14), %%rax\n\t" /* c = TOS */ \
|
||||
"movq -8(%%r14), %%rcx\n\t" /* b = NOS */ \
|
||||
"movq -16(%%r14), %%rdx\n\t" /* a = third */ \
|
||||
"movq %%rdx, -8(%%r14)\n\t" /* a -> NOS */ \
|
||||
"movq %%rax, -16(%%r14)\n\t" /* c -> third */ \
|
||||
"movq %%rcx, (%%r14)\n\t" /* b -> TOS */ \
|
||||
::: "rax", "rcx", "rdx", "memory" \
|
||||
)
|
||||
|
||||
/* / - signed divide second by top */
|
||||
#define PRIM_SLASH() \
|
||||
__asm__ __volatile__ ( \
|
||||
"movq -8(%%r14), %%rax\n\t" /* Load dividend (NOS) */ \
|
||||
"cqo\n\t" /* Sign-extend rax to rdx:rax */ \
|
||||
"idivq (%%r14)\n\t" /* Divide by TOS */ \
|
||||
"movq %%rax, -8(%%r14)\n\t" /* Store quotient to NOS slot */ \
|
||||
"subq $8, %%r14\n\t" /* DSP-- */ \
|
||||
::: "rax", "rdx", "r14", "cc", "memory" \
|
||||
)
|
||||
|
||||
/* MOD - signed modulo */
|
||||
#define PRIM_MOD() \
|
||||
__asm__ __volatile__ ( \
|
||||
"movq -8(%%r14), %%rax\n\t" /* Load dividend (NOS) */ \
|
||||
"cqo\n\t" /* Sign-extend */ \
|
||||
"idivq (%%r14)\n\t" /* rdx = remainder */ \
|
||||
"movq %%rdx, -8(%%r14)\n\t" /* Store remainder to NOS slot */ \
|
||||
"subq $8, %%r14\n\t" /* DSP-- */ \
|
||||
::: "rax", "rdx", "r14", "cc", "memory" \
|
||||
)
|
||||
|
||||
/* AND - bitwise and */
|
||||
#define PRIM_AND() \
|
||||
__asm__ __volatile__ ( \
|
||||
"movq (%%r14), %%rax\n\t" /* Load TOS */ \
|
||||
"andq %%rax, -8(%%r14)\n\t" /* NOS &= TOS */ \
|
||||
"subq $8, %%r14\n\t" /* DSP-- */ \
|
||||
::: "rax", "r14", "cc", "memory" \
|
||||
)
|
||||
|
||||
/* OR - bitwise or */
|
||||
#define PRIM_OR() \
|
||||
__asm__ __volatile__ ( \
|
||||
"movq (%%r14), %%rax\n\t" /* Load TOS */ \
|
||||
"orq %%rax, -8(%%r14)\n\t" /* NOS |= TOS */ \
|
||||
"subq $8, %%r14\n\t" /* DSP-- */ \
|
||||
::: "rax", "r14", "cc", "memory" \
|
||||
)
|
||||
|
||||
/* XOR - bitwise xor */
|
||||
#define PRIM_XOR() \
|
||||
__asm__ __volatile__ ( \
|
||||
"movq (%%r14), %%rax\n\t" /* Load TOS */ \
|
||||
"xorq %%rax, -8(%%r14)\n\t" /* NOS ^= TOS */ \
|
||||
"subq $8, %%r14\n\t" /* DSP-- */ \
|
||||
::: "rax", "r14", "cc", "memory" \
|
||||
)
|
||||
|
||||
/* INVERT - bitwise complement */
|
||||
#define PRIM_INVERT() \
|
||||
__asm__ __volatile__ ( \
|
||||
"notq (%%r14)\n\t" /* TOS = ~TOS */ \
|
||||
::: "memory" \
|
||||
)
|
||||
|
||||
/* NEGATE - two's complement negation */
|
||||
#define PRIM_NEGATE() \
|
||||
__asm__ __volatile__ ( \
|
||||
"negq (%%r14)\n\t" /* TOS = -TOS */ \
|
||||
::: "cc", "memory" \
|
||||
)
|
||||
|
||||
/* C@ - fetch byte from memory */
|
||||
#define PRIM_C_FETCH() \
|
||||
__asm__ __volatile__ ( \
|
||||
"movq (%%r14), %%rax\n\t" /* Load address from TOS */ \
|
||||
"movq (%%r12), %%rcx\n\t" /* Load VM->memory */ \
|
||||
"movzbq (%%rcx, %%rax, 1), %%rax\n\t" /* Load byte, zero-extend */ \
|
||||
"movq %%rax, (%%r14)\n\t" /* Store byte value to TOS */ \
|
||||
::: "rax", "rcx", "memory" \
|
||||
)
|
||||
|
||||
/* C! - store byte to memory */
|
||||
#define PRIM_C_STORE() \
|
||||
__asm__ __volatile__ ( \
|
||||
"movq (%%r14), %%rax\n\t" /* Load address from TOS */ \
|
||||
"movq -8(%%r14), %%rcx\n\t" /* Load byte value from NOS */ \
|
||||
"movq (%%r12), %%rdx\n\t" /* Load VM->memory */ \
|
||||
"movb %%cl, (%%rdx, %%rax, 1)\n\t" /* Store byte */ \
|
||||
"subq $16, %%r14\n\t" /* DSP -= 2 */ \
|
||||
::: "rax", "rcx", "rdx", "r14", "memory" \
|
||||
)
|
||||
|
||||
/* 2DUP - duplicate top two items ( a b -- a b a b ) */
|
||||
#define PRIM_2DUP() \
|
||||
__asm__ __volatile__ ( \
|
||||
"movq -8(%%r14), %%rax\n\t" /* Load NOS (a) */ \
|
||||
"movq (%%r14), %%rcx\n\t" /* Load TOS (b) */ \
|
||||
"movq %%rax, 8(%%r14)\n\t" /* Store a above TOS */ \
|
||||
"movq %%rcx, 16(%%r14)\n\t" /* Store b above that */ \
|
||||
"addq $16, %%r14\n\t" /* DSP += 2 */ \
|
||||
::: "rax", "rcx", "r14", "memory" \
|
||||
)
|
||||
|
||||
/* 2DROP - drop top two items */
|
||||
#define PRIM_2DROP() \
|
||||
__asm__ __volatile__ ( \
|
||||
"subq $16, %%r14\n\t" /* DSP -= 2 */ \
|
||||
::: "r14" \
|
||||
)
|
||||
|
||||
/* 0= - true (-1) if TOS is zero, false (0) otherwise */
|
||||
#define PRIM_ZERO_EQUALS() \
|
||||
__asm__ __volatile__ ( \
|
||||
"movq (%%r14), %%rax\n\t" /* Load TOS */ \
|
||||
"testq %%rax, %%rax\n\t" /* Test for zero */ \
|
||||
"sete %%al\n\t" /* AL = 1 if zero */ \
|
||||
"movzbq %%al, %%rax\n\t" /* Zero-extend */ \
|
||||
"negq %%rax\n\t" /* -1 if true, 0 if false */ \
|
||||
"movq %%rax, (%%r14)\n\t" /* Store result */ \
|
||||
::: "rax", "cc", "memory" \
|
||||
)
|
||||
|
||||
/* 0< - true (-1) if TOS is negative, false (0) otherwise */
|
||||
#define PRIM_ZERO_LESS() \
|
||||
__asm__ __volatile__ ( \
|
||||
"movq (%%r14), %%rax\n\t" /* Load TOS */ \
|
||||
"sarq $63, %%rax\n\t" /* Arithmetic shift: -1 if negative, 0 otherwise */ \
|
||||
"movq %%rax, (%%r14)\n\t" /* Store result */ \
|
||||
::: "rax", "cc", "memory" \
|
||||
)
|
||||
|
||||
/* = - true (-1) if top two items are equal, false (0) otherwise */
|
||||
#define PRIM_EQUALS() \
|
||||
__asm__ __volatile__ ( \
|
||||
"movq (%%r14), %%rax\n\t" /* Load TOS (b) */ \
|
||||
"cmpq %%rax, -8(%%r14)\n\t" /* Compare NOS (a) with TOS (b) */ \
|
||||
"sete %%al\n\t" /* AL = 1 if equal */ \
|
||||
"movzbq %%al, %%rax\n\t" /* Zero-extend */ \
|
||||
"negq %%rax\n\t" /* -1 if true, 0 if false */ \
|
||||
"movq %%rax, -8(%%r14)\n\t" /* Store result to NOS slot */ \
|
||||
"subq $8, %%r14\n\t" /* DSP-- */ \
|
||||
::: "rax", "r14", "cc", "memory" \
|
||||
)
|
||||
|
||||
/* < - true (-1) if NOS < TOS (signed), false (0) otherwise */
|
||||
#define PRIM_LESS() \
|
||||
__asm__ __volatile__ ( \
|
||||
"movq (%%r14), %%rcx\n\t" /* Load TOS (b) */ \
|
||||
"movq -8(%%r14), %%rax\n\t" /* Load NOS (a) */ \
|
||||
"cmpq %%rcx, %%rax\n\t" /* rax - rcx: signed compare a vs b */ \
|
||||
"setl %%al\n\t" /* AL = 1 if a < b */ \
|
||||
"movzbq %%al, %%rax\n\t" /* Zero-extend */ \
|
||||
"negq %%rax\n\t" /* -1 if true, 0 if false */ \
|
||||
"movq %%rax, -8(%%r14)\n\t" /* Store result to NOS slot */ \
|
||||
"subq $8, %%r14\n\t" /* DSP-- */ \
|
||||
::: "rax", "rcx", "r14", "cc", "memory" \
|
||||
)
|
||||
|
||||
/* > - true (-1) if NOS > TOS (signed), false (0) otherwise */
|
||||
#define PRIM_GREATER() \
|
||||
__asm__ __volatile__ ( \
|
||||
"movq (%%r14), %%rcx\n\t" /* Load TOS (b) */ \
|
||||
"movq -8(%%r14), %%rax\n\t" /* Load NOS (a) */ \
|
||||
"cmpq %%rcx, %%rax\n\t" /* rax - rcx: signed compare a vs b */ \
|
||||
"setg %%al\n\t" /* AL = 1 if a > b */ \
|
||||
"movzbq %%al, %%rax\n\t" /* Zero-extend */ \
|
||||
"negq %%rax\n\t" /* -1 if true, 0 if false */ \
|
||||
"movq %%rax, -8(%%r14)\n\t" /* Store result to NOS slot */ \
|
||||
"subq $8, %%r14\n\t" /* DSP-- */ \
|
||||
::: "rax", "rcx", "r14", "cc", "memory" \
|
||||
)
|
||||
|
||||
/* EXECUTE - execute word whose execution token is on stack */
|
||||
#define PRIM_EXECUTE() \
|
||||
__asm__ __volatile__ ( \
|
||||
"movq (%%r14), %%rax\n\t" /* Load xt from TOS */ \
|
||||
"subq $8, %%r14\n\t" /* DSP-- */ \
|
||||
"jmpq *%%rax\n\t" /* Jump to execution token */ \
|
||||
::: "rax", "r14", "memory" \
|
||||
)
|
||||
|
||||
#else /* !USE_DIRECT_THREADING */
|
||||
|
||||
/* Fallback definitions when direct threading is disabled */
|
||||
#define NEXT_ASM() do {} while(0)
|
||||
#define vm_setup_registers(vm) do {} while(0)
|
||||
#define vm_save_registers(vm) do {} while(0)
|
||||
|
||||
#endif /* USE_DIRECT_THREADING */
|
||||
|
||||
/*
|
||||
* ============================================================================
|
||||
* INTEGRATION GUIDE
|
||||
* ============================================================================
|
||||
*
|
||||
* To integrate these optimizations:
|
||||
*
|
||||
* 1. Enable in your build:
|
||||
* -DUSE_DIRECT_THREADING=1 -DUSE_ASM_OPT=1
|
||||
*
|
||||
* 2. Modify your inner interpreter in vm.c:
|
||||
*
|
||||
* void execute_threaded_code(VM *vm) {
|
||||
* vm_setup_registers(vm);
|
||||
*
|
||||
* // Execute words...
|
||||
* // Each primitive word should use PRIM_* macros
|
||||
* // and end with NEXT_ASM()
|
||||
*
|
||||
* vm_save_registers(vm);
|
||||
* }
|
||||
*
|
||||
* 3. For each primitive word, replace function with macro:
|
||||
*
|
||||
* Instead of:
|
||||
* void word_dup(VM *vm) { vm_push(vm, vm->data_stack[vm->dsp]); }
|
||||
*
|
||||
* Use:
|
||||
* PRIM_DUP(); NEXT_ASM();
|
||||
*
|
||||
* 4. Benchmark before and after!
|
||||
*
|
||||
* ============================================================================
|
||||
* PERFORMANCE NOTES
|
||||
* ============================================================================
|
||||
*
|
||||
* Expected speedup: 2-5x for threaded code execution
|
||||
*
|
||||
* Why so fast?
|
||||
* - No function call overhead (10+ cycles saved per word)
|
||||
* - Register allocation eliminates memory traffic
|
||||
* - Direct jumps instead of call/return (better branch prediction)
|
||||
* - Cache-friendly: hot path stays in I-cache
|
||||
*
|
||||
* Trade-offs:
|
||||
* - More complex code
|
||||
* - Harder to debug (use regular C version for debugging)
|
||||
* - Platform-specific (x86_64 only)
|
||||
* - Requires more careful register management
|
||||
*
|
||||
* ============================================================================
|
||||
* L4RE INTEGRATION NOTES
|
||||
* ============================================================================
|
||||
*
|
||||
* All safe for L4Re because:
|
||||
* - No privileged instructions
|
||||
* - No system calls
|
||||
* - Preserved ABI (callee-saved registers)
|
||||
* - Can be used in both user and kernel contexts
|
||||
*
|
||||
* For StarshipOS:
|
||||
* - These optimizations work in L4Re tasks
|
||||
* - Safe for real-time contexts (deterministic timing)
|
||||
* - No FPU usage (kernel-safe)
|
||||
* - Stack usage is minimal
|
||||
*
|
||||
* ============================================================================
|
||||
*/
|
||||
|
||||
#endif /* VM_INNER_INTERP_ASM_H */
|
||||
Reference in New Issue
Block a user