512 lines
15 KiB
C
512 lines
15 KiB
C
/*
|
||
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.
|
||
|
||
*/
|
||
|
||
/**
|
||
* @file arch_detect.h
|
||
* @brief Architecture Detection and Unified API for StarForth
|
||
* @details Automatically selects x86_64 or ARM64 optimizations and provides
|
||
* a unified interface for architecture-specific optimizations.
|
||
*
|
||
* This work is released under license terms in LICENSE file.
|
||
* No warranty. Use at your own risk.
|
||
*/
|
||
|
||
#ifndef ARCH_DETECT_H
|
||
#define ARCH_DETECT_H
|
||
|
||
/**
|
||
* @defgroup arch_detect Architecture Detection
|
||
* @{
|
||
*
|
||
* @brief Automatic detection of target architecture and inclusion of optimization headers
|
||
*
|
||
* This module automatically detects the target architecture at compile time and includes
|
||
* the appropriate assembly optimization headers.
|
||
*
|
||
* Supported architectures:
|
||
* - x86_64 (AMD64, Intel 64)
|
||
* - ARM64 (AArch64, ARMv8-A)
|
||
*
|
||
* Unsupported architectures fall back to pure C implementations.
|
||
* @}
|
||
*/
|
||
|
||
/* Detect architecture at compile time */
|
||
#if defined(__x86_64__) || defined(_M_X64) || defined(__amd64__)
|
||
#define ARCH_X86_64 1
|
||
#define ARCH_ARM64 0
|
||
#define ARCH_RISCV64 0
|
||
#define ARCH_NAME "x86_64"
|
||
#elif defined(__aarch64__) || defined(_M_ARM64) || defined(__arm64__)
|
||
#define ARCH_X86_64 0
|
||
#define ARCH_ARM64 1
|
||
#define ARCH_RISCV64 0
|
||
#define ARCH_NAME "ARM64"
|
||
#elif defined(__riscv) && (__riscv_xlen == 64)
|
||
#define ARCH_X86_64 0
|
||
#define ARCH_ARM64 0
|
||
#define ARCH_RISCV64 1
|
||
#define ARCH_NAME "RISC-V 64"
|
||
#else
|
||
#define ARCH_X86_64 0
|
||
#define ARCH_ARM64 0
|
||
#define ARCH_RISCV64 0
|
||
#define ARCH_NAME "Unknown"
|
||
#warning "Unknown architecture, assembly optimizations disabled"
|
||
#endif
|
||
|
||
/* Include architecture-specific headers */
|
||
#if ARCH_X86_64 && USE_ASM_OPT
|
||
#include "vm_asm_opt.h"
|
||
#if USE_DIRECT_THREADING
|
||
#include "vm_inner_interp_asm.h"
|
||
#endif
|
||
#elif ARCH_ARM64 && USE_ASM_OPT
|
||
#include "vm_asm_opt_arm64.h"
|
||
#if USE_DIRECT_THREADING
|
||
#include "vm_inner_interp_arm64.h"
|
||
#endif
|
||
#elif ARCH_RISCV64 && USE_ASM_OPT
|
||
#include "vm_asm_opt_riscv64.h"
|
||
#if USE_DIRECT_THREADING
|
||
#include "vm_inner_interp_riscv64.h"
|
||
#endif
|
||
#endif
|
||
|
||
/**
|
||
* @defgroup unified_api Unified API Wrappers
|
||
* @{
|
||
*
|
||
* @brief Consistent API macros across all architectures
|
||
*
|
||
* These macros provide a consistent API across all architectures by automatically
|
||
* dispatching to the appropriate optimized or fallback implementation based on
|
||
* the target architecture and available optimizations.
|
||
* @}
|
||
*/
|
||
|
||
/* ========== Stack Operations ========== */
|
||
|
||
#if USE_ASM_OPT && (ARCH_X86_64 || ARCH_ARM64 || ARCH_RISCV64)
|
||
/* Use optimized assembly implementations */
|
||
#define vm_push_opt(vm, val) vm_push_asm(vm, val)
|
||
#define vm_pop_opt(vm) vm_pop_asm(vm)
|
||
#define vm_rpush_opt(vm, val) vm_rpush_asm(vm, val)
|
||
#define vm_rpop_opt(vm) vm_rpop_asm(vm)
|
||
#else
|
||
/* Fall back to standard C implementations */
|
||
#define vm_push_opt(vm, val) vm_push(vm, val)
|
||
#define vm_pop_opt(vm) vm_pop(vm)
|
||
#define vm_rpush_opt(vm, val) vm_rpush(vm, val)
|
||
#define vm_rpop_opt(vm) vm_rpop(vm)
|
||
#endif
|
||
|
||
/* ========== Arithmetic Operations ========== */
|
||
|
||
#if USE_ASM_OPT && (ARCH_X86_64 || ARCH_ARM64 || ARCH_RISCV64)
|
||
#define vm_add_overflow_opt(a, b, res) vm_add_check_overflow(a, b, res)
|
||
#define vm_sub_overflow_opt(a, b, res) vm_sub_check_overflow(a, b, res)
|
||
#define vm_mul_double_opt(a, b, hi, lo) vm_mul_double(a, b, hi, lo)
|
||
#define vm_divmod_opt(dividend, divisor, quot, rem) vm_divmod(dividend, divisor, quot, rem)
|
||
#else
|
||
/* Fallback implementations */
|
||
static inline int vm_add_overflow_opt(cell_t a, cell_t b, cell_t *res) {
|
||
*res = a + b;
|
||
/* Simple overflow check (not perfect) */
|
||
return ((a > 0 && b > 0 && *res < 0) || (a < 0 && b < 0 && *res > 0));
|
||
}
|
||
|
||
static inline int vm_sub_overflow_opt(cell_t a, cell_t b, cell_t *res) {
|
||
*res = a - b;
|
||
return ((a > 0 && b < 0 && *res < 0) || (a < 0 && b > 0 && *res > 0));
|
||
}
|
||
|
||
static inline void vm_mul_double_opt(cell_t a, cell_t b, cell_t *hi, cell_t *lo) {
|
||
/* Simplified - assumes 64-bit cell_t */
|
||
*lo = a * b;
|
||
*hi = 0; /* Would need 128-bit arithmetic for proper high word */
|
||
}
|
||
|
||
static inline void vm_divmod_opt(cell_t dividend, cell_t divisor, cell_t *quot, cell_t *rem) {
|
||
*quot = dividend / divisor;
|
||
*rem = dividend % divisor;
|
||
}
|
||
#endif
|
||
|
||
/* ========== String Operations ========== */
|
||
|
||
#if USE_ASM_OPT && (ARCH_X86_64 || ARCH_ARM64 || ARCH_RISCV64)
|
||
#define vm_strcmp_opt(s1, s2, len) vm_strcmp_asm(s1, s2, len)
|
||
#define vm_memcpy_opt(dst, src, len) vm_memcpy_asm(dst, src, len)
|
||
#define vm_memzero_opt(dst, len) vm_memzero_asm(dst, len)
|
||
#else
|
||
#include <string.h>
|
||
|
||
static inline int vm_strcmp_opt(const char *s1, const char *s2, size_t len) {
|
||
return memcmp(s1, s2, len) != 0;
|
||
}
|
||
|
||
static inline void vm_memcpy_opt(void *dst, const void *src, size_t len) {
|
||
memcpy(dst, src, len);
|
||
}
|
||
|
||
static inline void vm_memzero_opt(void *dst, size_t len) {
|
||
memset(dst, 0, len);
|
||
}
|
||
#endif
|
||
|
||
/* ========== Min/Max Operations ========== */
|
||
|
||
#if USE_ASM_OPT && (ARCH_X86_64 || ARCH_ARM64 || ARCH_RISCV64)
|
||
#define vm_min_opt(a, b) vm_min_asm(a, b)
|
||
#define vm_max_opt(a, b) vm_max_asm(a, b)
|
||
#else
|
||
static inline cell_t vm_min_opt(cell_t a, cell_t b) {
|
||
return (a < b) ? a : b;
|
||
}
|
||
|
||
static inline cell_t vm_max_opt(cell_t a, cell_t b) {
|
||
return (a > b) ? a : b;
|
||
}
|
||
#endif
|
||
|
||
/* ========== Absolute Value ========== */
|
||
|
||
#if USE_ASM_OPT && (ARCH_ARM64 || ARCH_RISCV64)
|
||
#define vm_abs_opt(a) vm_abs_asm(a)
|
||
#else
|
||
static inline cell_t vm_abs_opt(cell_t a) {
|
||
return (a < 0) ? -a : a;
|
||
}
|
||
#endif
|
||
|
||
/* ========== Bit Operations ========== */
|
||
|
||
#if USE_ASM_OPT && (ARCH_ARM64 || ARCH_RISCV64)
|
||
#define vm_clz_opt(x) vm_clz(x)
|
||
#define vm_ctz_opt(x) vm_ctz(x)
|
||
#define vm_popcnt_opt(x) vm_popcnt(x)
|
||
#elif USE_ASM_OPT && ARCH_X86_64
|
||
static inline int vm_clz_opt(cell_t x) {
|
||
return __builtin_clzll((unsigned long long) x);
|
||
}
|
||
static inline int vm_ctz_opt(cell_t x) {
|
||
return __builtin_ctzll((unsigned long long) x);
|
||
}
|
||
static inline int vm_popcnt_opt(cell_t x) {
|
||
return __builtin_popcountll((unsigned long long) x);
|
||
}
|
||
#else
|
||
/* Software fallback */
|
||
static inline int vm_clz_opt(cell_t x) {
|
||
if (x == 0) return 64;
|
||
int n = 0;
|
||
if ((x & 0xFFFFFFFF00000000ULL) == 0) {
|
||
n += 32;
|
||
x <<= 32;
|
||
}
|
||
if ((x & 0xFFFF000000000000ULL) == 0) {
|
||
n += 16;
|
||
x <<= 16;
|
||
}
|
||
if ((x & 0xFF00000000000000ULL) == 0) {
|
||
n += 8;
|
||
x <<= 8;
|
||
}
|
||
if ((x & 0xF000000000000000ULL) == 0) {
|
||
n += 4;
|
||
x <<= 4;
|
||
}
|
||
if ((x & 0xC000000000000000ULL) == 0) {
|
||
n += 2;
|
||
x <<= 2;
|
||
}
|
||
if ((x & 0x8000000000000000ULL) == 0) { n += 1; }
|
||
return n;
|
||
}
|
||
|
||
static inline int vm_ctz_opt(cell_t x) {
|
||
if (x == 0) return 64;
|
||
int n = 0;
|
||
if ((x & 0x00000000FFFFFFFFULL) == 0) {
|
||
n += 32;
|
||
x >>= 32;
|
||
}
|
||
if ((x & 0x000000000000FFFFULL) == 0) {
|
||
n += 16;
|
||
x >>= 16;
|
||
}
|
||
if ((x & 0x00000000000000FFULL) == 0) {
|
||
n += 8;
|
||
x >>= 8;
|
||
}
|
||
if ((x & 0x000000000000000FULL) == 0) {
|
||
n += 4;
|
||
x >>= 4;
|
||
}
|
||
if ((x & 0x0000000000000003ULL) == 0) {
|
||
n += 2;
|
||
x >>= 2;
|
||
}
|
||
if ((x & 0x0000000000000001ULL) == 0) { n += 1; }
|
||
return n;
|
||
}
|
||
|
||
static inline int vm_popcnt_opt(cell_t x) {
|
||
int count = 0;
|
||
while (x) {
|
||
count += x & 1;
|
||
x >>= 1;
|
||
}
|
||
return count;
|
||
}
|
||
#endif
|
||
|
||
/* ========== Cache Operations ========== */
|
||
|
||
#if USE_ASM_OPT && (ARCH_ARM64 || ARCH_RISCV64)
|
||
#define vm_prefetch_opt(addr) vm_prefetch(addr)
|
||
#define vm_prefetch_stream_opt(addr) vm_prefetch_stream(addr)
|
||
#elif USE_ASM_OPT && ARCH_X86_64
|
||
static inline void vm_prefetch_opt(const void *addr) {
|
||
__builtin_prefetch(addr, 0, 3); /* Read, high temporal locality */
|
||
}
|
||
static inline void vm_prefetch_stream_opt(const void *addr) {
|
||
__builtin_prefetch(addr, 0, 0); /* Read, no temporal locality */
|
||
}
|
||
#else
|
||
#define vm_prefetch_opt(addr) ((void)0)
|
||
#define vm_prefetch_stream_opt(addr) ((void)0)
|
||
#endif
|
||
|
||
/* ========== Inner Interpreter (Direct Threading) ========== */
|
||
|
||
#if USE_DIRECT_THREADING && ARCH_RISCV64
|
||
#define NEXT_OPT() NEXT_RV64()
|
||
#define vm_setup_registers_opt(vm) vm_setup_registers_rv64(vm)
|
||
#define vm_save_registers_opt(vm) vm_save_registers_rv64(vm)
|
||
|
||
#define PRIM_DUP_OPT() PRIM_DUP_RV64()
|
||
#define PRIM_DROP_OPT() PRIM_DROP_RV64()
|
||
#define PRIM_SWAP_OPT() PRIM_SWAP_RV64()
|
||
#define PRIM_OVER_OPT() PRIM_OVER_RV64()
|
||
#define PRIM_ROT_OPT() PRIM_ROT_RV64()
|
||
#define PRIM_PLUS_OPT() PRIM_PLUS_RV64()
|
||
#define PRIM_MINUS_OPT() PRIM_MINUS_RV64()
|
||
#define PRIM_STAR_OPT() PRIM_STAR_RV64()
|
||
#define PRIM_SLASH_OPT() PRIM_SLASH_RV64()
|
||
#define PRIM_MOD_OPT() PRIM_MOD_RV64()
|
||
#define PRIM_AND_OPT() PRIM_AND_RV64()
|
||
#define PRIM_OR_OPT() PRIM_OR_RV64()
|
||
#define PRIM_XOR_OPT() PRIM_XOR_RV64()
|
||
#define PRIM_INVERT_OPT() PRIM_INVERT_RV64()
|
||
#define PRIM_NEGATE_OPT() PRIM_NEGATE_RV64()
|
||
#define PRIM_FETCH_OPT() PRIM_FETCH_RV64()
|
||
#define PRIM_STORE_OPT() PRIM_STORE_RV64()
|
||
#define PRIM_C_FETCH_OPT() PRIM_C_FETCH_RV64()
|
||
#define PRIM_C_STORE_OPT() PRIM_C_STORE_RV64()
|
||
#define PRIM_TO_R_OPT() PRIM_TO_R_RV64()
|
||
#define PRIM_R_FROM_OPT() PRIM_R_FROM_RV64()
|
||
#define PRIM_R_FETCH_OPT() PRIM_R_FETCH_RV64()
|
||
#define PRIM_2DUP_OPT() PRIM_2DUP_RV64()
|
||
#define PRIM_2DROP_OPT() PRIM_2DROP_RV64()
|
||
#define PRIM_ZERO_EQUALS_OPT() PRIM_ZERO_EQUALS_RV64()
|
||
#define PRIM_ZERO_LESS_OPT() PRIM_ZERO_LESS_RV64()
|
||
#define PRIM_EQUALS_OPT() PRIM_EQUALS_RV64()
|
||
#define PRIM_LESS_OPT() PRIM_LESS_RV64()
|
||
#define PRIM_GREATER_OPT() PRIM_GREATER_RV64()
|
||
#define PRIM_BRANCH_OPT() PRIM_BRANCH_RV64()
|
||
#define PRIM_ZBRANCH_OPT() PRIM_ZBRANCH_RV64()
|
||
#define PRIM_EXECUTE_OPT() PRIM_EXECUTE_RV64()
|
||
|
||
#elif USE_DIRECT_THREADING && ARCH_X86_64
|
||
#define NEXT_OPT() NEXT_ASM()
|
||
#define vm_setup_registers_opt(vm) vm_setup_registers(vm)
|
||
#define vm_save_registers_opt(vm) vm_save_registers(vm)
|
||
|
||
/* Primitive word macros */
|
||
#define PRIM_DUP_OPT() PRIM_DUP()
|
||
#define PRIM_DROP_OPT() PRIM_DROP()
|
||
#define PRIM_SWAP_OPT() PRIM_SWAP()
|
||
#define PRIM_PLUS_OPT() PRIM_PLUS()
|
||
#define PRIM_MINUS_OPT() PRIM_MINUS()
|
||
#define PRIM_STAR_OPT() PRIM_STAR()
|
||
#define PRIM_FETCH_OPT() PRIM_FETCH()
|
||
#define PRIM_STORE_OPT() PRIM_STORE()
|
||
#define PRIM_TO_R_OPT() PRIM_TO_R()
|
||
#define PRIM_R_FROM_OPT() PRIM_R_FROM()
|
||
#define PRIM_R_FETCH_OPT() PRIM_R_FETCH()
|
||
#define PRIM_BRANCH_OPT() PRIM_BRANCH()
|
||
#define PRIM_ZBRANCH_OPT() PRIM_ZBRANCH()
|
||
|
||
#elif USE_DIRECT_THREADING && ARCH_ARM64
|
||
#define NEXT_OPT() NEXT_ARM64()
|
||
#define vm_setup_registers_opt(vm) vm_setup_registers_arm64(vm)
|
||
#define vm_save_registers_opt(vm) vm_save_registers_arm64(vm)
|
||
|
||
/* Primitive word macros */
|
||
#define PRIM_DUP_OPT() PRIM_DUP_ARM64()
|
||
#define PRIM_DROP_OPT() PRIM_DROP_ARM64()
|
||
#define PRIM_SWAP_OPT() PRIM_SWAP_ARM64()
|
||
#define PRIM_OVER_OPT() PRIM_OVER_ARM64()
|
||
#define PRIM_ROT_OPT() PRIM_ROT_ARM64()
|
||
#define PRIM_PLUS_OPT() PRIM_PLUS_ARM64()
|
||
#define PRIM_MINUS_OPT() PRIM_MINUS_ARM64()
|
||
#define PRIM_STAR_OPT() PRIM_STAR_ARM64()
|
||
#define PRIM_SLASH_OPT() PRIM_SLASH_ARM64()
|
||
#define PRIM_MOD_OPT() PRIM_MOD_ARM64()
|
||
#define PRIM_AND_OPT() PRIM_AND_ARM64()
|
||
#define PRIM_OR_OPT() PRIM_OR_ARM64()
|
||
#define PRIM_XOR_OPT() PRIM_XOR_ARM64()
|
||
#define PRIM_INVERT_OPT() PRIM_INVERT_ARM64()
|
||
#define PRIM_NEGATE_OPT() PRIM_NEGATE_ARM64()
|
||
#define PRIM_FETCH_OPT() PRIM_FETCH_ARM64()
|
||
#define PRIM_STORE_OPT() PRIM_STORE_ARM64()
|
||
#define PRIM_C_FETCH_OPT() PRIM_C_FETCH_ARM64()
|
||
#define PRIM_C_STORE_OPT() PRIM_C_STORE_ARM64()
|
||
#define PRIM_TO_R_OPT() PRIM_TO_R_ARM64()
|
||
#define PRIM_R_FROM_OPT() PRIM_R_FROM_ARM64()
|
||
#define PRIM_R_FETCH_OPT() PRIM_R_FETCH_ARM64()
|
||
#define PRIM_2DUP_OPT() PRIM_2DUP_ARM64()
|
||
#define PRIM_2DROP_OPT() PRIM_2DROP_ARM64()
|
||
#define PRIM_ZERO_EQUALS_OPT() PRIM_ZERO_EQUALS_ARM64()
|
||
#define PRIM_ZERO_LESS_OPT() PRIM_ZERO_LESS_ARM64()
|
||
#define PRIM_EQUALS_OPT() PRIM_EQUALS_ARM64()
|
||
#define PRIM_LESS_OPT() PRIM_LESS_ARM64()
|
||
#define PRIM_GREATER_OPT() PRIM_GREATER_ARM64()
|
||
#define PRIM_BRANCH_OPT() PRIM_BRANCH_ARM64()
|
||
#define PRIM_ZBRANCH_OPT() PRIM_ZBRANCH_ARM64()
|
||
#define PRIM_EXECUTE_OPT() PRIM_EXECUTE_ARM64()
|
||
|
||
#else
|
||
/* No direct threading - use standard C function calls */
|
||
#define NEXT_OPT() do {} while(0)
|
||
#define vm_setup_registers_opt(vm) do {} while(0)
|
||
#define vm_save_registers_opt(vm) do {} while(0)
|
||
|
||
/* Primitives fall back to function calls */
|
||
#define PRIM_DUP_OPT() do {} while(0)
|
||
#define PRIM_DROP_OPT() do {} while(0)
|
||
/* ... etc ... */
|
||
#endif
|
||
|
||
/**
|
||
* @defgroup runtime_detect Runtime Architecture Detection
|
||
* @{
|
||
*
|
||
* @brief Runtime detection of CPU features
|
||
*
|
||
* Functions for detecting available CPU features at runtime, such as SIMD
|
||
* support (SSE4.2, AVX, NEON) and other architecture-specific capabilities.
|
||
* @}
|
||
*/
|
||
|
||
#if ARCH_X86_64
|
||
/* Check for SSE4.2, AVX, etc. */
|
||
static inline int vm_has_sse42_opt(void) {
|
||
|
||
|
||
#if USE_ASM_OPT
|
||
return vm_has_sse42();
|
||
#else
|
||
return 0;
|
||
#endif
|
||
}
|
||
#elif ARCH_ARM64
|
||
/* Check for NEON, CRC32, etc. */
|
||
static inline int vm_has_neon_opt(void) {
|
||
|
||
|
||
#if USE_ASM_OPT
|
||
return vm_has_neon();
|
||
#else
|
||
return 0;
|
||
#endif
|
||
}
|
||
#else
|
||
static inline int vm_has_sse42_opt(void) { return 0; }
|
||
static inline int vm_has_neon_opt(void) { return 0; }
|
||
#endif
|
||
|
||
/**
|
||
* @defgroup build_info Build Information
|
||
* @{
|
||
*
|
||
* @brief Functions for querying build configuration
|
||
*
|
||
* Provides information about the build configuration, including target
|
||
* architecture, enabled optimizations, and threading model.
|
||
* @}
|
||
*/
|
||
|
||
static inline const char *vm_get_arch_name(void) {
|
||
return ARCH_NAME;
|
||
}
|
||
|
||
static inline int vm_has_asm_opt(void) {
|
||
#if USE_ASM_OPT
|
||
return 1;
|
||
#else
|
||
return 0;
|
||
#endif
|
||
}
|
||
|
||
static inline int vm_has_direct_threading(void) {
|
||
#if USE_DIRECT_THREADING
|
||
return 1;
|
||
#else
|
||
return 0;
|
||
#endif
|
||
}
|
||
|
||
static inline const char *vm_get_build_info(void) {
|
||
static char info[256];
|
||
snprintf(info, sizeof(info),
|
||
"Architecture: %s, ASM: %s, Direct Threading: %s",
|
||
vm_get_arch_name(),
|
||
vm_has_asm_opt() ? "enabled" : "disabled",
|
||
vm_has_direct_threading() ? "enabled" : "disabled"
|
||
);
|
||
return info;
|
||
}
|
||
|
||
#endif /* ARCH_DETECT_H */ |