Files
LithosAnanake/docs/working/hardware/performance-profiling/ASM_OPTIMIZATIONS.adoc
T

622 lines
12 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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 L4Res allocators:
[source,c]
----
#include <l4/re/c/mem_alloc.h>
#include <l4/re/c/dataspace.h>
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 <l4/sys/ipc.h>
// 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 doesnt 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.