// Moved from docs/src/performance-profiling/ASM_OPTIMIZATIONS.adoc to docs/working/scratch/src/performance-profiling/ASM_OPTIMIZATIONS.adoc on 2026-06-16 (docs reorg Phase 2) == StarForth Assembly Optimizations :toc: left :toc-title: Contents :toclevels: 3 xref:../README.adoc[← Back to Documentation Index] === Overview This document describes the x86_64 assembly optimizations available for StarForth, suitable for both standard Linux environments and L4Re microkernel deployments (StarshipOS). === Files * `+include/vm_asm_opt.h+` - Basic stack and arithmetic optimizations * `+include/vm_inner_interp_asm.h+` - Advanced direct-threaded inner interpreter === Performance Impact [width="100%",cols="28%,12%,12%,48%",options="header",] |=== |Optimization |Impact |Speedup |Use Case |Stack operations |EXTREME |2-3x |Every word execution |Inner interpreter |EXTREME |3-5x |Threaded code (colon definitions) |Arithmetic ops |HIGH |1.5-2x |Math-heavy code |Dictionary lookup |HIGH |2-3x |Word compilation/interpretation |String operations |MEDIUM |1.5-2x |Text processing |=== === Quick Start ==== 1. Enable Optimizations Edit your `+Makefile+` or build script: [source,makefile] ---- # Add to CFLAGS CFLAGS += -DUSE_ASM_OPT=1 -DUSE_DIRECT_THREADING=1 -O3 -march=native # For L4Re/StarshipOS CFLAGS += -DUSE_ASM_OPT=1 -march=x86-64-v2 -O3 ---- ==== 2. Include Headers In your `+src/stack_management.c+`: [source,c] ---- #include "../include/vm.h" #include "../include/vm_asm_opt.h" void vm_push(VM *vm, cell_t value) { #if USE_ASM_OPT vm_push_asm(vm, value); #else // Original C implementation if (vm->dsp >= STACK_SIZE - 1) { vm->error = 1; return; } vm->data_stack[++vm->dsp] = value; #endif } cell_t vm_pop(VM *vm) { #if USE_ASM_OPT return vm_pop_asm(vm); #else // Original C implementation if (vm->dsp < 0) { vm->error = 1; return 0; } return vm->data_stack[vm->dsp--]; #endif } ---- ==== 3. Optimize Arithmetic Words In `+src/word_source/arithmetic_words.c+`: [source,c] ---- #include "../../include/vm_asm_opt.h" void arithmetic_word_plus(VM *vm) { if (vm->dsp < 1) { vm->error = 1; return; } #if USE_ASM_OPT cell_t n2 = vm_pop_asm(vm); cell_t n1 = vm_pop_asm(vm); cell_t result; if (vm_add_check_overflow(n1, n2, &result)) { // Handle overflow log_message(LOG_ERROR, "+: Overflow detected"); vm->error = 1; return; } vm_push_asm(vm, result); #else // Original implementation cell_t n2 = vm_pop(vm); cell_t n1 = vm_pop(vm); vm_push(vm, n1 + n2); #endif } void arithmetic_word_star_slash(VM *vm) { // n1 n2 n3 -- n4 (n1*n2/n3) if (vm->dsp < 2) { vm->error = 1; return; } #if USE_ASM_OPT cell_t n3 = vm_pop_asm(vm); cell_t n2 = vm_pop_asm(vm); cell_t n1 = vm_pop_asm(vm); if (n3 == 0) { vm->error = 1; return; } // Use double-width multiplication cell_t hi, lo; vm_mul_double(n1, n2, &hi, &lo); // Now divide the 128-bit result by n3 // (Note: Full 128-bit division requires more complex code) // For now, assume result fits in 64 bits: cell_t result = lo / n3; // Simplified vm_push_asm(vm, result); #else // Original implementation cell_t n3 = vm_pop(vm); cell_t n2 = vm_pop(vm); cell_t n1 = vm_pop(vm); vm_push(vm, (n1 * n2) / n3); #endif } ---- ==== 4. Optimize Dictionary Lookup In `+src/dictionary_management.c+`: [source,c] ---- #include "../include/vm_asm_opt.h" DictEntry *vm_find_word(VM *vm, const char *name, size_t len) { for (DictEntry *e = vm->latest; e; e = e->link) { if (e->name_len == len) { #if USE_ASM_OPT if (vm_strcmp_asm(e->name, name, len) == 0) { return e; } #else if (memcmp(e->name, name, len) == 0) { return e; } #endif } } return NULL; } ---- === Advanced: Direct-Threaded Inner Interpreter This is the most complex but highest-impact optimization. ==== Understanding Direct Threading Traditional C implementation: [source,c] ---- void execute_colon_word(VM *vm) { cell_t *ip = vm->ip; while (*ip) { DictEntry *word = (DictEntry*)*ip++; word->func(vm); // Function call overhead! if (vm->exit_colon) break; } } ---- Problems: * Function call/return for EVERY word (expensive) * Poor CPU branch prediction * Register spilling Direct-threaded solution: [source,c] ---- void execute_colon_word_fast(VM *vm) { vm_setup_registers(vm); // Now IP, stacks are in registers // Each word does its work and jumps to NEXT // No function call overhead! vm_save_registers(vm); } ---- ==== Implementation Example Create a new file `+src/vm_primitives_asm.c+`: [source,c] ---- #include "../include/vm.h" #include "../include/vm_inner_interp_asm.h" #if USE_DIRECT_THREADING // Each primitive word uses the PRIM_* macros and NEXT_ASM() void forth_dup_fast(void) { PRIM_DUP(); NEXT_ASM(); } void forth_drop_fast(void) { PRIM_DROP(); NEXT_ASM(); } void forth_swap_fast(void) { PRIM_SWAP(); NEXT_ASM(); } void forth_plus_fast(void) { PRIM_PLUS(); NEXT_ASM(); } void forth_minus_fast(void) { PRIM_MINUS(); NEXT_ASM(); } void forth_star_fast(void) { PRIM_STAR(); NEXT_ASM(); } void forth_fetch_fast(void) { PRIM_FETCH(); NEXT_ASM(); } void forth_store_fast(void) { PRIM_STORE(); NEXT_ASM(); } void forth_to_r_fast(void) { PRIM_TO_R(); NEXT_ASM(); } void forth_r_from_fast(void) { PRIM_R_FROM(); NEXT_ASM(); } void forth_branch_fast(void) { PRIM_BRANCH(); NEXT_ASM(); } void forth_zbranch_fast(void) { PRIM_ZBRANCH(); NEXT_ASM(); } #endif // USE_DIRECT_THREADING ---- === Benchmarking ==== 1. Create Test Suite Create `+benchmarks/stack_bench.fth+`: [source,forth] ---- : BENCH-DUP ( n -- ) 0 DO 123 DUP DROP LOOP ; : BENCH-MATH ( n -- ) 0 DO 10 20 + 5 * 100 / DROP LOOP ; : BENCH-STACK ( n -- ) 0 DO 1 2 3 4 5 DROP DROP DROP DROP DROP LOOP ; \ Run benchmarks ." Testing DUP/DROP: " 1000000 BENCH-DUP ." Done" CR ." Testing arithmetic: " 1000000 BENCH-MATH ." Done" CR ." Testing stack ops: " 1000000 BENCH-STACK ." Done" CR ---- ==== 2. Measure Performance [source,bash] ---- # Baseline (no optimizations) make clean make CFLAGS="-std=c99 -O2 -Iinclude" time ./build/starforth benchmarks/stack_bench.fth # With assembly optimizations make clean make CFLAGS="-std=c99 -O3 -march=native -DUSE_ASM_OPT=1 -Iinclude" time ./build/starforth benchmarks/stack_bench.fth # With direct threading make clean make CFLAGS="-std=c99 -O3 -march=native -DUSE_ASM_OPT=1 -DUSE_DIRECT_THREADING=1 -Iinclude" time ./build/starforth benchmarks/stack_bench.fth ---- ==== 3. Use perf for Detailed Analysis [source,bash] ---- # Profile baseline perf record -g ./build/starforth benchmarks/stack_bench.fth perf report # Profile optimized perf record -g ./build/starforth_asm benchmarks/stack_bench.fth perf report # Compare: perf stat -r 10 ./build/starforth benchmarks/stack_bench.fth perf stat -r 10 ./build/starforth_asm benchmarks/stack_bench.fth ---- === L4Re / StarshipOS Integration ==== Build for L4Re [source,makefile] ---- # In your L4Re package Makefile CFLAGS += -DUSE_ASM_OPT=1 -O3 -march=x86-64-v2 CFLAGS += -fno-stack-protector # If in kernel context CFLAGS += -mno-red-zone # Required for kernel code # Link with L4Re libraries LIBS += -ll4re-util -ll4sys ---- ==== Memory Management For L4Re, use L4Re’s allocators: [source,c] ---- #include #include void vm_init_l4re(VM *vm) { // Allocate VM memory using L4Re dataspace l4re_ds_t ds; l4_cap_idx_t ds_cap = l4re_util_cap_alloc(); long ret = l4re_ma_alloc(VM_MEMORY_SIZE, ds_cap, 0); if (ret) { // Handle error return; } // Map dataspace ret = l4re_rm_attach(&vm->memory, VM_MEMORY_SIZE, L4RE_RM_F_SEARCH_ADDR | L4RE_RM_F_RW, ds_cap, 0, L4_PAGESHIFT); if (ret) { // Handle error return; } // Continue with normal initialization vm->dsp = -1; vm->rsp = -1; // ... } ---- ==== IPC Integration For inter-VM communication: [source,c] ---- #include // Send Forth data to another L4Re task void vm_ipc_send(VM *vm, l4_cap_idx_t dest) { l4_msg_regs_t *mr = l4_utcb_mr(); // Pack stack data into message registers mr->mr[0] = vm->dsp; for (int i = 0; i <= vm->dsp && i < L4_UTCB_GENERIC_DATA_SIZE; i++) { mr->mr[i+1] = vm->data_stack[i]; } l4_msgtag_t tag = l4_msgtag(0, vm->dsp + 2, 0, 0); tag = l4_ipc_send(dest, l4_utcb(), tag, L4_IPC_NEVER); if (l4_ipc_error(tag, l4_utcb())) { vm->error = 1; } } ---- === Debugging ==== Disable Optimizations for Debugging [source,makefile] ---- debug: $(MAKE) CFLAGS="$(BASE_CFLAGS) -O0 -g -DUSE_ASM_OPT=0" all ---- ==== GDB Tips [source,bash] ---- # Debug assembly-optimized code gdb ./build/starforth (gdb) break vm_push_asm (gdb) layout asm (gdb) stepi (gdb) info registers r12 r13 r14 r15 ---- ==== Verify Correctness Create test suite that runs in both modes: [source,c] ---- void test_stack_ops(void) { VM vm_c, vm_asm; vm_init(&vm_c); vm_init(&vm_asm); // Test with C version #undef USE_ASM_OPT vm_push(&vm_c, 42); vm_push(&vm_c, 17); cell_t result_c = vm_pop(&vm_c); // Test with ASM version #define USE_ASM_OPT 1 vm_push_asm(&vm_asm, 42); vm_push_asm(&vm_asm, 17); cell_t result_asm = vm_pop_asm(&vm_asm); assert(result_c == result_asm); assert(vm_c.dsp == vm_asm.dsp); } ---- === Platform Compatibility [width="100%",cols="22%,15%,17%,25%,21%",options="header",] |=== |Platform |Stack Ops |Arithmetic |Direct Threading |Notes |Linux x86_64 |✓ |✓ |✓ |Full support |L4Re x86_64 |✓ |✓ |✓ |Full support |StarshipOS |✓ |✓ |✓ |Kernel & user |ARM64 |✗ |✗ |✗ |Future work |RISC-V |✗ |✗ |✗ |Future work |=== === Safety Considerations ==== Stack Overflow Protection The assembly code checks for overflow, but for extra safety: [source,c] ---- #if USE_ASM_OPT // Add guard pages mprotect(vm->data_stack + STACK_SIZE, PAGE_SIZE, PROT_NONE); mprotect(vm->return_stack + STACK_SIZE, PAGE_SIZE, PROT_NONE); #endif ---- ==== Error Handling Assembly code sets `+vm->error+` on overflow/underflow, but doesn’t log (for performance). Add logging in debug builds: [source,c] ---- #ifdef DEBUG #define VM_ERROR_LOG(msg) log_message(LOG_ERROR, msg) #else #define VM_ERROR_LOG(msg) do {} while(0) #endif ---- === Performance Tips [arabic] . *Use PGO (Profile-Guided Optimization)* + [source,bash] ---- # Generate profile make CFLAGS="-O3 -fprofile-generate" ./build/starforth benchmarks/typical_workload.fth # Use profile make clean make CFLAGS="-O3 -fprofile-use" ---- . *Align hot structures* + [source,c] ---- typedef struct VM { // ... fields ... } __attribute__((aligned(64))) VM; // Cache line aligned ---- . *Prefetch dictionary entries* + [source,c] ---- __builtin_prefetch(vm->latest); __builtin_prefetch(vm->latest->link); ---- . *Use huge pages for VM memory* + [source,c] ---- vm->memory = mmap(NULL, VM_MEMORY_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, -1, 0); ---- === Troubleshooting ==== "`Illegal instruction`" error * Check CPU features: `+cat /proc/cpuinfo+` * Reduce `+-march=native+` to `+-march=x86-64-v2+` ==== Crashes in assembly code * Check stack alignment (must be 16-byte aligned) * Verify register clobbers are correct * Use `+-fsanitize=address+` to catch errors ==== Performance not improving * Profile with `+perf+` to find actual bottleneck * Check if compiler is inlining the wrappers * Verify optimizations are actually enabled === Contributing When adding new assembly optimizations: [arabic] . Benchmark before and after . Add both C and ASM versions . Test on multiple CPUs . Document register usage . Add to this README === References * https://www.complang.tuwien.ac.at/forth/threaded-code.html[Threaded Code] * https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html[GCC Inline Assembly] * https://github.com/hjl-tools/x86-psABI/wiki/x86-64-psABI-1.0.pdf[x86_64 ABI] * https://l4re.org/doc/[L4Re Documentation] === License Public domain / CC0. No warranty. Use at your own risk.