mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-26 18:25:35 -04:00
Remove the sljit Tier-2 in-process JIT backend
The tier mis-compiled and has been hard-gated off since 2026-06-25; overlay
gaps fall to gcc/tcc shards and the dirty-RAM interpreter, unchanged.
Re-do on master of the removal validated on the stale branch (6da8c02),
reconciled with everything master grew since the fork:
- Delete overlay_sljit.{c,h}, overlay_compile_worker.{c,h}, lib/sljit,
runtime/tests/sljit_*.c, and the runtime.cmake sources/include-dirs.
- Strip the sljit provider/backend from code_provider.{c,h},
overlay_backend.{c,h} ("sljit" in overlay_backend/PSX_OVERLAY_BACKEND now
degrades to auto so stale configs still boot), main.cpp, debug_server.c
(sljit_status/sljit_async/sljit_try TCP commands), overlay_loader.{c,h}
(live policy, JIT-on-miss, persisted-shard cache, async worker glue,
psx_sljit_call/cop2/memx host helpers — reachable only via the removed
cpu->sljit_helpers table), and cpu_state.h (SLJIT_HLP_* enum + the
sljit_helpers[] member; CPUState layout change reshards via the cg tag).
- Master-only entanglement resolved before deletion: 28165d6's Einhander WS
player-X bound emit existed in three paths (gcc code_generator, sljit,
dirty_ram_interp). The gcc shard link contract now forwards
psx_ws_player_x_bound (ABI v13, previous commit), so deleting the sljit
emit path loses no widescreen behavior; ffc6688's game-dispatch block
inside the dead psx_sljit_call helper goes with it.
- Reword comments that described sljit as a live backend (gpu.{c,h},
ws_*_detect.h, cosim_state.c, dirty_ram_interp.c, autocompile.h,
boot_state.h, config_loader.h, compile_overlays.py, and the CPS marker
comments the recompiler emits).
recompiler psxrecomp-game builds clean; game runtimes rebuilt + boot-verified
separately.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,25 +0,0 @@
|
||||
/*
|
||||
* Stack-less Just-In-Time compiler
|
||||
*
|
||||
* Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
@@ -1,68 +0,0 @@
|
||||
|
||||
SLJIT - Stack Less JIT Compiler
|
||||
|
||||
Purpose:
|
||||
A low-level, machine independent JIT compiler, which is suitable for
|
||||
translating interpreted byte code into machine code. The sljitLir.h
|
||||
describes the LIR (low-level intermediate representation) of SLJIT.
|
||||
|
||||
Key features:
|
||||
- Supports several target architectures:
|
||||
x86 32/64, ARM 32/64, RiscV 32/64, s390x 64,
|
||||
PowerPC 32/64, LoongArch 64, MIPS 32/64
|
||||
- Supports a large number of operations
|
||||
- Supports self-modifying code
|
||||
- Supports tail calls
|
||||
- Support fast calls (non-ABI compatible)
|
||||
- Supports byte order reverse (endianness switching)
|
||||
- Supports unaligned memory accesses
|
||||
- Supports SIMD / atomic operations on certain CPUs
|
||||
- Direct register access, both integer and floating point
|
||||
- Stack space allocated for function local variables can be
|
||||
accessed as a linear memory area
|
||||
- All-in-one compilation is supported
|
||||
- When sljitLir.c is directly included by a C source file,
|
||||
the jit compiler API can be completely hidden from
|
||||
external use (see SLJIT_CONFIG_STATIC macro)
|
||||
- Code can be generated for multiple target cpus
|
||||
by including sljitLir.c in different C files, where
|
||||
each compiler instance is configured to target a
|
||||
different architecture
|
||||
- The compiler can be serialized into a byte buffer
|
||||
- Useful for ahead-of-time compiling
|
||||
- Code generation can be resumed after deserialization
|
||||
(partial ahead-of-time compiling)
|
||||
|
||||
Compatible:
|
||||
C99 (C++) compilers.
|
||||
|
||||
Using sljit:
|
||||
Copy the content of sljit_src directory into your project source directory.
|
||||
Add sljitLir.c source file to your build environment. All other files are
|
||||
included by sljitLir.c (if required). Define the machine by SLJIT_CONFIG_*
|
||||
selector. See sljitConfigCPU.h for all possible values. For C++ compilers,
|
||||
rename sljitLir.c to sljitLir.cpp.
|
||||
|
||||
More info:
|
||||
https://zherczeg.github.io/sljit/
|
||||
|
||||
Contact:
|
||||
hzmester@freemail.hu
|
||||
|
||||
Special thanks:
|
||||
Alexander Nasonov
|
||||
Carlo Marcelo Arenas Belón
|
||||
Christian Persch
|
||||
Daniel Richard G.
|
||||
Giuseppe D'Angelo
|
||||
H.J. Lu
|
||||
James Cowgill
|
||||
Jason Hood
|
||||
Jiong Wang (TileGX support)
|
||||
Marc Mutz
|
||||
Martin Storsjö
|
||||
Michael McConville
|
||||
Mingtao Zhou (LoongArch support)
|
||||
Walter Lee
|
||||
Wen Xichang
|
||||
YunQiang Su
|
||||
@@ -1,137 +0,0 @@
|
||||
/*
|
||||
* Stack-less Just-In-Time compiler
|
||||
*
|
||||
* Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/mman.h>
|
||||
/*
|
||||
On macOS systems, returns MAP_JIT if it is defined _and_ we're running on a
|
||||
version where it's OK to have more than one JIT block or where MAP_JIT is
|
||||
required.
|
||||
On non-macOS systems, returns MAP_JIT if it is defined.
|
||||
*/
|
||||
#include <TargetConditionals.h>
|
||||
|
||||
#if (defined(TARGET_OS_OSX) && TARGET_OS_OSX) || (TARGET_OS_MAC && !TARGET_OS_IPHONE)
|
||||
|
||||
#if defined(SLJIT_CONFIG_X86) && SLJIT_CONFIG_X86
|
||||
|
||||
#include <sys/utsname.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define SLJIT_UPDATE_WX_FLAGS(from, to, enable_exec)
|
||||
|
||||
#ifdef MAP_JIT
|
||||
#define SLJIT_MAP_JIT (get_map_jit_flag())
|
||||
static SLJIT_INLINE int get_map_jit_flag(void)
|
||||
{
|
||||
size_t page_size;
|
||||
void *ptr;
|
||||
struct utsname name;
|
||||
static int map_jit_flag = -1;
|
||||
|
||||
if (map_jit_flag < 0) {
|
||||
map_jit_flag = 0;
|
||||
uname(&name);
|
||||
|
||||
/* Kernel version for 10.14.0 (Mojave) or later */
|
||||
if (atoi(name.release) >= 18) {
|
||||
page_size = get_page_alignment() + 1;
|
||||
/* Only use MAP_JIT if a hardened runtime is used */
|
||||
ptr = mmap(NULL, page_size, PROT_WRITE | PROT_EXEC,
|
||||
MAP_PRIVATE | MAP_ANON, -1, 0);
|
||||
|
||||
if (ptr != MAP_FAILED)
|
||||
munmap(ptr, page_size);
|
||||
else
|
||||
map_jit_flag = MAP_JIT;
|
||||
}
|
||||
}
|
||||
return map_jit_flag;
|
||||
}
|
||||
#else /* !defined(MAP_JIT) */
|
||||
#define SLJIT_MAP_JIT (0)
|
||||
#endif
|
||||
|
||||
#elif defined(SLJIT_CONFIG_ARM) && SLJIT_CONFIG_ARM
|
||||
|
||||
#include <AvailabilityMacros.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#define SLJIT_MAP_JIT (MAP_JIT)
|
||||
#define SLJIT_UPDATE_WX_FLAGS(from, to, enable_exec) \
|
||||
apple_update_wx_flags(enable_exec)
|
||||
|
||||
static SLJIT_INLINE void apple_update_wx_flags(sljit_s32 enable_exec)
|
||||
{
|
||||
#if MAC_OS_X_VERSION_MIN_REQUIRED < 110000
|
||||
if (__builtin_available(macos 11, *))
|
||||
#endif /* BigSur */
|
||||
pthread_jit_write_protect_np(enable_exec);
|
||||
}
|
||||
|
||||
#elif defined(SLJIT_CONFIG_PPC) && SLJIT_CONFIG_PPC
|
||||
|
||||
#define SLJIT_MAP_JIT (0)
|
||||
#define SLJIT_UPDATE_WX_FLAGS(from, to, enable_exec)
|
||||
|
||||
#else
|
||||
#error "Unsupported architecture"
|
||||
#endif /* SLJIT_CONFIG */
|
||||
|
||||
#else /* !TARGET_OS_OSX */
|
||||
|
||||
#ifdef MAP_JIT
|
||||
#define SLJIT_MAP_JIT (MAP_JIT)
|
||||
#else
|
||||
#define SLJIT_MAP_JIT (0)
|
||||
#endif
|
||||
|
||||
#endif /* TARGET_OS_OSX */
|
||||
|
||||
static SLJIT_INLINE void* alloc_chunk(sljit_uw size)
|
||||
{
|
||||
void *retval;
|
||||
int prot = PROT_READ | PROT_WRITE | PROT_EXEC;
|
||||
int flags = MAP_PRIVATE;
|
||||
int fd = -1;
|
||||
|
||||
flags |= MAP_ANON | SLJIT_MAP_JIT;
|
||||
|
||||
retval = mmap(NULL, size, prot, flags, fd, 0);
|
||||
if (retval == MAP_FAILED)
|
||||
return NULL;
|
||||
|
||||
SLJIT_UPDATE_WX_FLAGS(retval, (uint8_t *)retval + size, 0);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static SLJIT_INLINE void free_chunk(void *chunk, sljit_uw size)
|
||||
{
|
||||
munmap(chunk, size);
|
||||
}
|
||||
|
||||
#include "sljitExecAllocatorCore.c"
|
||||
@@ -1,327 +0,0 @@
|
||||
/*
|
||||
* Stack-less Just-In-Time compiler
|
||||
*
|
||||
* Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
This file contains a simple executable memory allocator
|
||||
|
||||
It is assumed, that executable code blocks are usually medium (or sometimes
|
||||
large) memory blocks, and the allocator is not too frequently called (less
|
||||
optimized than other allocators). Thus, using it as a generic allocator is
|
||||
not suggested.
|
||||
|
||||
How does it work:
|
||||
Memory is allocated in continuous memory areas called chunks by alloc_chunk()
|
||||
Chunk format:
|
||||
[ block ][ block ] ... [ block ][ block terminator ]
|
||||
|
||||
All blocks and the block terminator is started with block_header. The block
|
||||
header contains the size of the previous and the next block. These sizes
|
||||
can also contain special values.
|
||||
Block size:
|
||||
0 - The block is a free_block, with a different size member.
|
||||
1 - The block is a block terminator.
|
||||
n - The block is used at the moment, and the value contains its size.
|
||||
Previous block size:
|
||||
0 - This is the first block of the memory chunk.
|
||||
n - The size of the previous block.
|
||||
|
||||
Using these size values we can go forward or backward on the block chain.
|
||||
The unused blocks are stored in a chain list pointed by free_blocks. This
|
||||
list is useful if we need to find a suitable memory area when the allocator
|
||||
is called.
|
||||
|
||||
When a block is freed, the new free block is connected to its adjacent free
|
||||
blocks if possible.
|
||||
|
||||
[ free block ][ used block ][ free block ]
|
||||
and "used block" is freed, the three blocks are connected together:
|
||||
[ one big free block ]
|
||||
*/
|
||||
|
||||
/* Expected functions:
|
||||
alloc_chunk / free_chunk :
|
||||
* allocate executable system memory chunks
|
||||
* the size is always divisible by CHUNK_SIZE
|
||||
SLJIT_ALLOCATOR_LOCK / SLJIT_ALLOCATOR_UNLOCK :
|
||||
* provided as part of sljitUtils
|
||||
* only the allocator requires this lock, sljit is fully thread safe
|
||||
as it only uses local variables
|
||||
|
||||
Supported defines:
|
||||
SLJIT_HAS_CHUNK_HEADER - (optional) sljit_chunk_header is defined
|
||||
SLJIT_HAS_EXECUTABLE_OFFSET - (optional) has executable offset data
|
||||
SLJIT_UPDATE_WX_FLAGS - (optional) update WX flags
|
||||
*/
|
||||
|
||||
#ifdef SLJIT_HAS_CHUNK_HEADER
|
||||
#define CHUNK_HEADER_SIZE (sizeof(struct sljit_chunk_header))
|
||||
#else /* !SLJIT_HAS_CHUNK_HEADER */
|
||||
#define CHUNK_HEADER_SIZE 0
|
||||
#endif /* SLJIT_HAS_CHUNK_HEADER */
|
||||
|
||||
#ifndef SLJIT_UPDATE_WX_FLAGS
|
||||
#define SLJIT_UPDATE_WX_FLAGS(from, to, enable_exec)
|
||||
#endif /* SLJIT_UPDATE_WX_FLAGS */
|
||||
|
||||
#ifndef CHUNK_SIZE
|
||||
/* 64 KByte if not specified. */
|
||||
#define CHUNK_SIZE (sljit_uw)0x10000
|
||||
#endif /* CHUNK_SIZE */
|
||||
|
||||
struct block_header {
|
||||
sljit_uw size;
|
||||
sljit_uw prev_size;
|
||||
#ifdef SLJIT_HAS_EXECUTABLE_OFFSET
|
||||
sljit_sw executable_offset;
|
||||
#endif /* SLJIT_HAS_EXECUTABLE_OFFSET */
|
||||
};
|
||||
|
||||
struct free_block {
|
||||
struct block_header header;
|
||||
struct free_block *next;
|
||||
struct free_block *prev;
|
||||
sljit_uw size;
|
||||
};
|
||||
|
||||
#define AS_BLOCK_HEADER(base, offset) \
|
||||
((struct block_header*)(((sljit_u8*)base) + offset))
|
||||
#define AS_FREE_BLOCK(base, offset) \
|
||||
((struct free_block*)(((sljit_u8*)base) + offset))
|
||||
#define MEM_START(base) ((void*)((base) + 1))
|
||||
#define CHUNK_MASK (~(CHUNK_SIZE - 1))
|
||||
#define ALIGN_SIZE(size) (((size) + sizeof(struct block_header) + 7u) & ~(sljit_uw)7)
|
||||
#define CHUNK_EXTRA_SIZE (sizeof(struct block_header) + CHUNK_HEADER_SIZE)
|
||||
|
||||
static struct free_block* free_blocks;
|
||||
static sljit_uw allocated_size;
|
||||
static sljit_uw total_size;
|
||||
|
||||
static SLJIT_INLINE void sljit_insert_free_block(struct free_block *free_block, sljit_uw size)
|
||||
{
|
||||
free_block->header.size = 0;
|
||||
free_block->size = size;
|
||||
|
||||
free_block->next = free_blocks;
|
||||
free_block->prev = NULL;
|
||||
if (free_blocks)
|
||||
free_blocks->prev = free_block;
|
||||
free_blocks = free_block;
|
||||
}
|
||||
|
||||
static SLJIT_INLINE void sljit_remove_free_block(struct free_block *free_block)
|
||||
{
|
||||
if (free_block->next)
|
||||
free_block->next->prev = free_block->prev;
|
||||
|
||||
if (free_block->prev)
|
||||
free_block->prev->next = free_block->next;
|
||||
else {
|
||||
SLJIT_ASSERT(free_blocks == free_block);
|
||||
free_blocks = free_block->next;
|
||||
}
|
||||
}
|
||||
|
||||
SLJIT_API_FUNC_ATTRIBUTE void* sljit_malloc_exec(sljit_uw size)
|
||||
{
|
||||
struct block_header *header;
|
||||
struct block_header *next_header;
|
||||
struct free_block *free_block;
|
||||
sljit_uw chunk_size;
|
||||
|
||||
#ifdef SLJIT_HAS_CHUNK_HEADER
|
||||
struct sljit_chunk_header *chunk_header;
|
||||
#else /* !SLJIT_HAS_CHUNK_HEADER */
|
||||
void *chunk_header;
|
||||
#endif /* SLJIT_HAS_CHUNK_HEADER */
|
||||
|
||||
#ifdef SLJIT_HAS_EXECUTABLE_OFFSET
|
||||
sljit_sw executable_offset;
|
||||
#endif /* SLJIT_HAS_EXECUTABLE_OFFSET */
|
||||
|
||||
if (size < (64 - sizeof(struct block_header)))
|
||||
size = (64 - sizeof(struct block_header));
|
||||
size = ALIGN_SIZE(size);
|
||||
|
||||
SLJIT_ALLOCATOR_LOCK();
|
||||
free_block = free_blocks;
|
||||
while (free_block) {
|
||||
if (free_block->size >= size) {
|
||||
chunk_size = free_block->size;
|
||||
SLJIT_UPDATE_WX_FLAGS(NULL, NULL, 0);
|
||||
if (chunk_size > size + 64) {
|
||||
/* We just cut a block from the end of the free block. */
|
||||
chunk_size -= size;
|
||||
free_block->size = chunk_size;
|
||||
header = AS_BLOCK_HEADER(free_block, chunk_size);
|
||||
header->prev_size = chunk_size;
|
||||
#ifdef SLJIT_HAS_EXECUTABLE_OFFSET
|
||||
header->executable_offset = free_block->header.executable_offset;
|
||||
#endif /* SLJIT_HAS_EXECUTABLE_OFFSET */
|
||||
AS_BLOCK_HEADER(header, size)->prev_size = size;
|
||||
} else {
|
||||
sljit_remove_free_block(free_block);
|
||||
header = (struct block_header*)free_block;
|
||||
size = chunk_size;
|
||||
}
|
||||
allocated_size += size;
|
||||
header->size = size;
|
||||
SLJIT_ALLOCATOR_UNLOCK();
|
||||
return MEM_START(header);
|
||||
}
|
||||
free_block = free_block->next;
|
||||
}
|
||||
|
||||
chunk_size = (size + CHUNK_EXTRA_SIZE + CHUNK_SIZE - 1) & CHUNK_MASK;
|
||||
|
||||
chunk_header = alloc_chunk(chunk_size);
|
||||
if (!chunk_header) {
|
||||
SLJIT_ALLOCATOR_UNLOCK();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef SLJIT_HAS_EXECUTABLE_OFFSET
|
||||
executable_offset = (sljit_sw)((sljit_u8*)chunk_header->executable - (sljit_u8*)chunk_header);
|
||||
#endif /* SLJIT_HAS_EXECUTABLE_OFFSET */
|
||||
|
||||
chunk_size -= CHUNK_EXTRA_SIZE;
|
||||
total_size += chunk_size;
|
||||
|
||||
header = (struct block_header*)(((sljit_u8*)chunk_header) + CHUNK_HEADER_SIZE);
|
||||
|
||||
header->prev_size = 0;
|
||||
#ifdef SLJIT_HAS_EXECUTABLE_OFFSET
|
||||
header->executable_offset = executable_offset;
|
||||
#endif /* SLJIT_HAS_EXECUTABLE_OFFSET */
|
||||
|
||||
if (chunk_size > size + 64) {
|
||||
/* Cut the allocated space into a free and a used block. */
|
||||
allocated_size += size;
|
||||
header->size = size;
|
||||
chunk_size -= size;
|
||||
|
||||
free_block = AS_FREE_BLOCK(header, size);
|
||||
free_block->header.prev_size = size;
|
||||
#ifdef SLJIT_HAS_EXECUTABLE_OFFSET
|
||||
free_block->header.executable_offset = executable_offset;
|
||||
#endif /* SLJIT_HAS_EXECUTABLE_OFFSET */
|
||||
sljit_insert_free_block(free_block, chunk_size);
|
||||
next_header = AS_BLOCK_HEADER(free_block, chunk_size);
|
||||
} else {
|
||||
/* All space belongs to this allocation. */
|
||||
allocated_size += chunk_size;
|
||||
header->size = chunk_size;
|
||||
next_header = AS_BLOCK_HEADER(header, chunk_size);
|
||||
}
|
||||
next_header->size = 1;
|
||||
next_header->prev_size = chunk_size;
|
||||
#ifdef SLJIT_HAS_EXECUTABLE_OFFSET
|
||||
next_header->executable_offset = executable_offset;
|
||||
#endif /* SLJIT_HAS_EXECUTABLE_OFFSET */
|
||||
SLJIT_ALLOCATOR_UNLOCK();
|
||||
return MEM_START(header);
|
||||
}
|
||||
|
||||
SLJIT_API_FUNC_ATTRIBUTE void sljit_free_exec(void *ptr)
|
||||
{
|
||||
struct block_header *header;
|
||||
struct free_block *free_block;
|
||||
|
||||
SLJIT_ALLOCATOR_LOCK();
|
||||
header = AS_BLOCK_HEADER(ptr, -(sljit_sw)sizeof(struct block_header));
|
||||
#ifdef SLJIT_HAS_EXECUTABLE_OFFSET
|
||||
header = AS_BLOCK_HEADER(header, -header->executable_offset);
|
||||
#endif /* SLJIT_HAS_EXECUTABLE_OFFSET */
|
||||
allocated_size -= header->size;
|
||||
|
||||
SLJIT_UPDATE_WX_FLAGS(NULL, NULL, 0);
|
||||
|
||||
/* Connecting free blocks together if possible. */
|
||||
|
||||
/* If header->prev_size == 0, free_block will equal to header.
|
||||
In this case, free_block->header.size will be > 0. */
|
||||
free_block = AS_FREE_BLOCK(header, -(sljit_sw)header->prev_size);
|
||||
if (SLJIT_UNLIKELY(!free_block->header.size)) {
|
||||
free_block->size += header->size;
|
||||
header = AS_BLOCK_HEADER(free_block, free_block->size);
|
||||
header->prev_size = free_block->size;
|
||||
} else {
|
||||
free_block = (struct free_block*)header;
|
||||
sljit_insert_free_block(free_block, header->size);
|
||||
}
|
||||
|
||||
header = AS_BLOCK_HEADER(free_block, free_block->size);
|
||||
if (SLJIT_UNLIKELY(!header->size)) {
|
||||
free_block->size += ((struct free_block*)header)->size;
|
||||
sljit_remove_free_block((struct free_block*)header);
|
||||
header = AS_BLOCK_HEADER(free_block, free_block->size);
|
||||
header->prev_size = free_block->size;
|
||||
}
|
||||
|
||||
/* The whole chunk is free. */
|
||||
if (SLJIT_UNLIKELY(!free_block->header.prev_size && header->size == 1)) {
|
||||
/* If this block is freed, we still have (allocated_size / 2) free space. */
|
||||
if (total_size - free_block->size > (allocated_size * 3 / 2)) {
|
||||
total_size -= free_block->size;
|
||||
sljit_remove_free_block(free_block);
|
||||
free_chunk(free_block, free_block->size + CHUNK_EXTRA_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
SLJIT_UPDATE_WX_FLAGS(NULL, NULL, 1);
|
||||
SLJIT_ALLOCATOR_UNLOCK();
|
||||
}
|
||||
|
||||
SLJIT_API_FUNC_ATTRIBUTE void sljit_free_unused_memory_exec(void)
|
||||
{
|
||||
struct free_block* free_block;
|
||||
struct free_block* next_free_block;
|
||||
|
||||
SLJIT_ALLOCATOR_LOCK();
|
||||
SLJIT_UPDATE_WX_FLAGS(NULL, NULL, 0);
|
||||
|
||||
free_block = free_blocks;
|
||||
while (free_block) {
|
||||
next_free_block = free_block->next;
|
||||
if (!free_block->header.prev_size &&
|
||||
AS_BLOCK_HEADER(free_block, free_block->size)->size == 1) {
|
||||
total_size -= free_block->size;
|
||||
sljit_remove_free_block(free_block);
|
||||
free_chunk(free_block, free_block->size + CHUNK_EXTRA_SIZE);
|
||||
}
|
||||
free_block = next_free_block;
|
||||
}
|
||||
|
||||
SLJIT_ASSERT(total_size || (!total_size && !free_blocks));
|
||||
SLJIT_UPDATE_WX_FLAGS(NULL, NULL, 1);
|
||||
SLJIT_ALLOCATOR_UNLOCK();
|
||||
}
|
||||
|
||||
#ifdef SLJIT_HAS_EXECUTABLE_OFFSET
|
||||
SLJIT_API_FUNC_ATTRIBUTE sljit_sw sljit_exec_offset(void *code)
|
||||
{
|
||||
return ((struct block_header*)SLJIT_CODE_TO_PTR(code))[-1].executable_offset;
|
||||
}
|
||||
#endif /* SLJIT_HAS_EXECUTABLE_OFFSET */
|
||||
@@ -1,89 +0,0 @@
|
||||
/*
|
||||
* Stack-less Just-In-Time compiler
|
||||
*
|
||||
* Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/mman.h>
|
||||
#include <sys/procctl.h>
|
||||
|
||||
#ifdef PROC_WXMAP_CTL
|
||||
static SLJIT_INLINE int sljit_is_wx_block(void)
|
||||
{
|
||||
static int wx_block = -1;
|
||||
if (wx_block < 0) {
|
||||
int sljit_wx_enable = PROC_WX_MAPPINGS_PERMIT;
|
||||
wx_block = !!procctl(P_PID, 0, PROC_WXMAP_CTL, &sljit_wx_enable);
|
||||
}
|
||||
return wx_block;
|
||||
}
|
||||
|
||||
#define SLJIT_IS_WX_BLOCK sljit_is_wx_block()
|
||||
#else /* !PROC_WXMAP_CTL */
|
||||
#define SLJIT_IS_WX_BLOCK (1)
|
||||
#endif /* PROC_WXMAP_CTL */
|
||||
|
||||
static SLJIT_INLINE void* alloc_chunk(sljit_uw size)
|
||||
{
|
||||
void *retval;
|
||||
int prot = PROT_READ | PROT_WRITE | PROT_EXEC;
|
||||
int flags = MAP_PRIVATE;
|
||||
int fd = -1;
|
||||
|
||||
#ifdef PROT_MAX
|
||||
prot |= PROT_MAX(prot);
|
||||
#endif
|
||||
|
||||
#ifdef MAP_ANON
|
||||
flags |= MAP_ANON;
|
||||
#else /* !MAP_ANON */
|
||||
if (SLJIT_UNLIKELY((dev_zero < 0) && open_dev_zero()))
|
||||
return NULL;
|
||||
|
||||
fd = dev_zero;
|
||||
#endif /* MAP_ANON */
|
||||
|
||||
retry:
|
||||
retval = mmap(NULL, size, prot, flags, fd, 0);
|
||||
if (retval == MAP_FAILED) {
|
||||
if (!SLJIT_IS_WX_BLOCK)
|
||||
goto retry;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* HardenedBSD's mmap lies, so check permissions again. */
|
||||
if (mprotect(retval, size, PROT_READ | PROT_WRITE | PROT_EXEC) < 0) {
|
||||
munmap(retval, size);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static SLJIT_INLINE void free_chunk(void *chunk, sljit_uw size)
|
||||
{
|
||||
munmap(chunk, size);
|
||||
}
|
||||
|
||||
#include "sljitExecAllocatorCore.c"
|
||||
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Stack-less Just-In-Time compiler
|
||||
*
|
||||
* Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
static SLJIT_INLINE void* alloc_chunk(sljit_uw size)
|
||||
{
|
||||
void *retval;
|
||||
int prot = PROT_READ | PROT_WRITE | PROT_EXEC;
|
||||
int flags = MAP_PRIVATE;
|
||||
int fd = -1;
|
||||
|
||||
#ifdef PROT_MAX
|
||||
prot |= PROT_MAX(prot);
|
||||
#endif
|
||||
|
||||
#ifdef MAP_ANON
|
||||
flags |= MAP_ANON;
|
||||
#else /* !MAP_ANON */
|
||||
if (SLJIT_UNLIKELY((dev_zero < 0) && open_dev_zero()))
|
||||
return NULL;
|
||||
|
||||
fd = dev_zero;
|
||||
#endif /* MAP_ANON */
|
||||
|
||||
retval = mmap(NULL, size, prot, flags, fd, 0);
|
||||
if (retval == MAP_FAILED)
|
||||
return NULL;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static SLJIT_INLINE void free_chunk(void *chunk, sljit_uw size)
|
||||
{
|
||||
munmap(chunk, size);
|
||||
}
|
||||
|
||||
#include "sljitExecAllocatorCore.c"
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Stack-less Just-In-Time compiler
|
||||
*
|
||||
* Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#define SLJIT_UPDATE_WX_FLAGS(from, to, enable_exec)
|
||||
|
||||
static SLJIT_INLINE void* alloc_chunk(sljit_uw size)
|
||||
{
|
||||
return VirtualAlloc(NULL, size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
|
||||
}
|
||||
|
||||
static SLJIT_INLINE void free_chunk(void *chunk, sljit_uw size)
|
||||
{
|
||||
SLJIT_UNUSED_ARG(size);
|
||||
VirtualFree(chunk, 0, MEM_RELEASE);
|
||||
}
|
||||
|
||||
#include "sljitExecAllocatorCore.c"
|
||||
@@ -1,72 +0,0 @@
|
||||
/*
|
||||
* Stack-less Just-In-Time compiler
|
||||
*
|
||||
* Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#define SLJIT_HAS_CHUNK_HEADER
|
||||
#define SLJIT_HAS_EXECUTABLE_OFFSET
|
||||
|
||||
struct sljit_chunk_header {
|
||||
void *executable;
|
||||
};
|
||||
|
||||
/*
|
||||
* MAP_REMAPDUP is a NetBSD extension available sinde 8.0, make sure to
|
||||
* adjust your feature macros (ex: -D_NETBSD_SOURCE) as needed
|
||||
*/
|
||||
static SLJIT_INLINE struct sljit_chunk_header* alloc_chunk(sljit_uw size)
|
||||
{
|
||||
struct sljit_chunk_header *retval;
|
||||
|
||||
retval = (struct sljit_chunk_header *)mmap(NULL, size,
|
||||
PROT_READ | PROT_WRITE | PROT_MPROTECT(PROT_EXEC),
|
||||
MAP_ANON | MAP_SHARED, -1, 0);
|
||||
|
||||
if (retval == MAP_FAILED)
|
||||
return NULL;
|
||||
|
||||
retval->executable = mremap(retval, size, NULL, size, MAP_REMAPDUP);
|
||||
if (retval->executable == MAP_FAILED) {
|
||||
munmap((void *)retval, size);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (mprotect(retval->executable, size, PROT_READ | PROT_EXEC) == -1) {
|
||||
munmap(retval->executable, size);
|
||||
munmap((void *)retval, size);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static SLJIT_INLINE void free_chunk(void *chunk, sljit_uw size)
|
||||
{
|
||||
struct sljit_chunk_header *header = ((struct sljit_chunk_header *)chunk) - 1;
|
||||
|
||||
munmap(header->executable, size);
|
||||
munmap((void *)header, size);
|
||||
}
|
||||
|
||||
#include "sljitExecAllocatorCore.c"
|
||||
@@ -1,172 +0,0 @@
|
||||
/*
|
||||
* Stack-less Just-In-Time compiler
|
||||
*
|
||||
* Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#define SLJIT_HAS_CHUNK_HEADER
|
||||
#define SLJIT_HAS_EXECUTABLE_OFFSET
|
||||
|
||||
struct sljit_chunk_header {
|
||||
void *executable;
|
||||
};
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef O_NOATIME
|
||||
#define O_NOATIME 0
|
||||
#endif
|
||||
|
||||
/* this is a linux extension available since kernel 3.11 */
|
||||
#ifndef O_TMPFILE
|
||||
#define O_TMPFILE 0x404000
|
||||
#endif
|
||||
|
||||
#ifndef _GNU_SOURCE
|
||||
char *secure_getenv(const char *name);
|
||||
int mkostemp(char *template, int flags);
|
||||
#endif
|
||||
|
||||
static SLJIT_INLINE int create_tempfile(void)
|
||||
{
|
||||
int fd;
|
||||
char tmp_name[256];
|
||||
size_t tmp_name_len = 0;
|
||||
char *dir;
|
||||
struct stat st;
|
||||
#if defined(SLJIT_SINGLE_THREADED) && SLJIT_SINGLE_THREADED
|
||||
mode_t mode;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_MEMFD_CREATE
|
||||
/* this is a GNU extension, make sure to use -D_GNU_SOURCE */
|
||||
fd = memfd_create("sljit", MFD_CLOEXEC);
|
||||
if (fd != -1) {
|
||||
fchmod(fd, 0);
|
||||
return fd;
|
||||
}
|
||||
#endif
|
||||
|
||||
dir = secure_getenv("TMPDIR");
|
||||
|
||||
if (dir) {
|
||||
size_t len = strlen(dir);
|
||||
if (len > 0 && len < sizeof(tmp_name)) {
|
||||
if ((stat(dir, &st) == 0) && S_ISDIR(st.st_mode)) {
|
||||
memcpy(tmp_name, dir, len + 1);
|
||||
tmp_name_len = len;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef P_tmpdir
|
||||
if (!tmp_name_len) {
|
||||
tmp_name_len = strlen(P_tmpdir);
|
||||
if (tmp_name_len > 0 && tmp_name_len < sizeof(tmp_name))
|
||||
strcpy(tmp_name, P_tmpdir);
|
||||
}
|
||||
#endif
|
||||
if (!tmp_name_len) {
|
||||
strcpy(tmp_name, "/tmp");
|
||||
tmp_name_len = 4;
|
||||
}
|
||||
|
||||
SLJIT_ASSERT(tmp_name_len > 0 && tmp_name_len < sizeof(tmp_name));
|
||||
|
||||
if (tmp_name_len > 1 && tmp_name[tmp_name_len - 1] == '/')
|
||||
tmp_name[--tmp_name_len] = '\0';
|
||||
|
||||
fd = open(tmp_name, O_TMPFILE | O_EXCL | O_RDWR | O_NOATIME | O_CLOEXEC, 0);
|
||||
if (fd != -1)
|
||||
return fd;
|
||||
|
||||
if (tmp_name_len >= sizeof(tmp_name) - 7)
|
||||
return -1;
|
||||
|
||||
strcpy(tmp_name + tmp_name_len, "/XXXXXX");
|
||||
#if defined(SLJIT_SINGLE_THREADED) && SLJIT_SINGLE_THREADED
|
||||
mode = umask(0777);
|
||||
#endif
|
||||
fd = mkostemp(tmp_name, O_CLOEXEC | O_NOATIME);
|
||||
#if defined(SLJIT_SINGLE_THREADED) && SLJIT_SINGLE_THREADED
|
||||
umask(mode);
|
||||
#else
|
||||
fchmod(fd, 0);
|
||||
#endif
|
||||
|
||||
if (fd == -1)
|
||||
return -1;
|
||||
|
||||
if (unlink(tmp_name)) {
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
static SLJIT_INLINE struct sljit_chunk_header* alloc_chunk(sljit_uw size)
|
||||
{
|
||||
struct sljit_chunk_header *retval;
|
||||
int fd;
|
||||
|
||||
fd = create_tempfile();
|
||||
if (fd == -1)
|
||||
return NULL;
|
||||
|
||||
if (ftruncate(fd, (off_t)size)) {
|
||||
close(fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
retval = (struct sljit_chunk_header *)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
|
||||
if (retval == MAP_FAILED) {
|
||||
close(fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
retval->executable = mmap(NULL, size, PROT_READ | PROT_EXEC, MAP_SHARED, fd, 0);
|
||||
|
||||
if (retval->executable == MAP_FAILED) {
|
||||
munmap((void *)retval, size);
|
||||
close(fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static SLJIT_INLINE void free_chunk(void *chunk, sljit_uw size)
|
||||
{
|
||||
struct sljit_chunk_header *header = ((struct sljit_chunk_header *)chunk) - 1;
|
||||
|
||||
munmap(header->executable, size);
|
||||
munmap((void *)header, size);
|
||||
}
|
||||
|
||||
#include "sljitExecAllocatorCore.c"
|
||||
@@ -1,141 +0,0 @@
|
||||
/*
|
||||
* Stack-less Just-In-Time compiler
|
||||
*
|
||||
* Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
This file contains a simple W^X executable memory allocator
|
||||
|
||||
In *NIX, MAP_ANON is required (that is considered a feature) so make
|
||||
sure to set the right availability macros for your system or the code
|
||||
will fail to build.
|
||||
|
||||
If your system doesn't support mapping of anonymous pages (ex: IRIX) it
|
||||
is also likely that it doesn't need this allocator and should be using
|
||||
the standard one instead.
|
||||
|
||||
It allocates a separate map for each code block and may waste a lot of
|
||||
memory, because whatever was requested, will be rounded up to the page
|
||||
size (minimum 4KB, but could be even bigger).
|
||||
|
||||
It changes the page permissions (RW <-> RX) as needed and therefore, if you
|
||||
will be updating the code after it has been generated, need to make sure to
|
||||
block any concurrent execution, or could result in a SIGBUS, that could
|
||||
even manifest itself at a different address than the one that was being
|
||||
modified.
|
||||
|
||||
Only use if you are unable to use the regular allocator because of security
|
||||
restrictions and adding exceptions to your application or the system are
|
||||
not possible.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#define SLJIT_UPDATE_WX_FLAGS(from, to, enable_exec) \
|
||||
sljit_update_wx_flags((from), (to), (enable_exec))
|
||||
|
||||
#if !(defined SLJIT_SINGLE_THREADED && SLJIT_SINGLE_THREADED)
|
||||
#include <pthread.h>
|
||||
#define SLJIT_SE_LOCK() pthread_mutex_lock(&se_lock)
|
||||
#define SLJIT_SE_UNLOCK() pthread_mutex_unlock(&se_lock)
|
||||
#else
|
||||
#define SLJIT_SE_LOCK()
|
||||
#define SLJIT_SE_UNLOCK()
|
||||
#endif /* !SLJIT_SINGLE_THREADED */
|
||||
|
||||
#define SLJIT_WX_IS_BLOCK(ptr, size) generic_check_is_wx_block(ptr, size)
|
||||
|
||||
static SLJIT_INLINE int generic_check_is_wx_block(void *ptr, sljit_uw size)
|
||||
{
|
||||
if (SLJIT_LIKELY(!mprotect(ptr, size, PROT_EXEC)))
|
||||
return !!mprotect(ptr, size, PROT_READ | PROT_WRITE);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
SLJIT_API_FUNC_ATTRIBUTE void* sljit_malloc_exec(sljit_uw size)
|
||||
{
|
||||
#if !(defined SLJIT_SINGLE_THREADED && SLJIT_SINGLE_THREADED)
|
||||
static pthread_mutex_t se_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
#endif
|
||||
static int wx_block = -1;
|
||||
int prot = PROT_READ | PROT_WRITE;
|
||||
sljit_uw* ptr;
|
||||
|
||||
if (SLJIT_UNLIKELY(wx_block > 0))
|
||||
return NULL;
|
||||
|
||||
#ifdef PROT_MAX
|
||||
prot |= PROT_MAX(PROT_READ | PROT_WRITE | PROT_EXEC);
|
||||
#endif
|
||||
|
||||
size += sizeof(sljit_uw);
|
||||
ptr = (sljit_uw*)mmap(NULL, size, prot, MAP_PRIVATE | MAP_ANON, -1, 0);
|
||||
|
||||
if (ptr == MAP_FAILED)
|
||||
return NULL;
|
||||
|
||||
if (SLJIT_UNLIKELY(wx_block < 0)) {
|
||||
SLJIT_SE_LOCK();
|
||||
wx_block = SLJIT_WX_IS_BLOCK(ptr, size);
|
||||
SLJIT_SE_UNLOCK();
|
||||
if (SLJIT_UNLIKELY(wx_block)) {
|
||||
munmap((void *)ptr, size);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
*ptr++ = size;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
#undef SLJIT_SE_UNLOCK
|
||||
#undef SLJIT_SE_LOCK
|
||||
|
||||
SLJIT_API_FUNC_ATTRIBUTE void sljit_free_exec(void* ptr)
|
||||
{
|
||||
sljit_uw *start_ptr = ((sljit_uw*)ptr) - 1;
|
||||
munmap((void*)start_ptr, *start_ptr);
|
||||
}
|
||||
|
||||
static void sljit_update_wx_flags(void *from, void *to, int enable_exec)
|
||||
{
|
||||
sljit_uw page_mask = (sljit_uw)get_page_alignment();
|
||||
sljit_uw start = (sljit_uw)from;
|
||||
sljit_uw end = (sljit_uw)to;
|
||||
int prot = PROT_READ | (enable_exec ? PROT_EXEC : PROT_WRITE);
|
||||
|
||||
SLJIT_ASSERT(start < end);
|
||||
|
||||
start &= ~page_mask;
|
||||
end = (end + page_mask) & ~page_mask;
|
||||
|
||||
mprotect((void*)start, end - start, prot);
|
||||
}
|
||||
|
||||
SLJIT_API_FUNC_ATTRIBUTE void sljit_free_unused_memory_exec(void)
|
||||
{
|
||||
/* This allocator does not keep unused memory for future allocations. */
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
/*
|
||||
* Stack-less Just-In-Time compiler
|
||||
*
|
||||
* Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
This file contains a simple W^X executable memory allocator
|
||||
|
||||
In *NIX, MAP_ANON is required (that is considered a feature) so make
|
||||
sure to set the right availability macros for your system or the code
|
||||
will fail to build.
|
||||
|
||||
If your system doesn't support mapping of anonymous pages (ex: IRIX) it
|
||||
is also likely that it doesn't need this allocator and should be using
|
||||
the standard one instead.
|
||||
|
||||
It allocates a separate map for each code block and may waste a lot of
|
||||
memory, because whatever was requested, will be rounded up to the page
|
||||
size (minimum 4KB, but could be even bigger).
|
||||
|
||||
It changes the page permissions (RW <-> RX) as needed and therefore, if you
|
||||
will be updating the code after it has been generated, need to make sure to
|
||||
block any concurrent execution, or could result in a SIGBUS, that could
|
||||
even manifest itself at a different address than the one that was being
|
||||
modified.
|
||||
|
||||
Only use if you are unable to use the regular allocator because of security
|
||||
restrictions and adding exceptions to your application or the system are
|
||||
not possible.
|
||||
*/
|
||||
|
||||
#define SLJIT_UPDATE_WX_FLAGS(from, to, enable_exec) \
|
||||
sljit_update_wx_flags((from), (to), (enable_exec))
|
||||
|
||||
SLJIT_API_FUNC_ATTRIBUTE void* sljit_malloc_exec(sljit_uw size)
|
||||
{
|
||||
sljit_uw *ptr;
|
||||
|
||||
size += sizeof(sljit_uw);
|
||||
ptr = (sljit_uw*)VirtualAlloc(NULL, size,
|
||||
MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
||||
|
||||
if (!ptr)
|
||||
return NULL;
|
||||
|
||||
*ptr++ = size;
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
SLJIT_API_FUNC_ATTRIBUTE void sljit_free_exec(void* ptr)
|
||||
{
|
||||
sljit_uw start = (sljit_uw)ptr - sizeof(sljit_uw);
|
||||
#if defined(SLJIT_DEBUG) && SLJIT_DEBUG
|
||||
sljit_uw page_mask = (sljit_uw)get_page_alignment();
|
||||
|
||||
SLJIT_ASSERT(!(start & page_mask));
|
||||
#endif
|
||||
VirtualFree((void*)start, 0, MEM_RELEASE);
|
||||
}
|
||||
|
||||
static void sljit_update_wx_flags(void *from, void *to, sljit_s32 enable_exec)
|
||||
{
|
||||
DWORD oldprot;
|
||||
sljit_uw page_mask = (sljit_uw)get_page_alignment();
|
||||
sljit_uw start = (sljit_uw)from;
|
||||
sljit_uw end = (sljit_uw)to;
|
||||
DWORD prot = enable_exec ? PAGE_EXECUTE : PAGE_READWRITE;
|
||||
|
||||
SLJIT_ASSERT(start < end);
|
||||
|
||||
start &= ~page_mask;
|
||||
end = (end + page_mask) & ~page_mask;
|
||||
|
||||
VirtualProtect((void*)start, end - start, prot, &oldprot);
|
||||
}
|
||||
|
||||
SLJIT_API_FUNC_ATTRIBUTE void sljit_free_unused_memory_exec(void)
|
||||
{
|
||||
/* This allocator does not keep unused memory for future allocations. */
|
||||
}
|
||||
@@ -1,142 +0,0 @@
|
||||
/*
|
||||
* Stack-less Just-In-Time compiler
|
||||
*
|
||||
* Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef SLJIT_CONFIG_H_
|
||||
#define SLJIT_CONFIG_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
This file contains the basic configuration options for the SLJIT compiler
|
||||
and their default values. These options can be overridden in the
|
||||
sljitConfigPre.h header file when SLJIT_HAVE_CONFIG_PRE is set to a
|
||||
non-zero value.
|
||||
*/
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
/* Utilities */
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
/* Implements a stack like data structure (by using mmap / VirtualAlloc */
|
||||
/* or a custom allocator). */
|
||||
#ifndef SLJIT_UTIL_STACK
|
||||
/* Enabled by default */
|
||||
#define SLJIT_UTIL_STACK 1
|
||||
#endif
|
||||
|
||||
/* Uses user provided allocator to allocate the stack (see SLJIT_UTIL_STACK) */
|
||||
#ifndef SLJIT_UTIL_SIMPLE_STACK_ALLOCATION
|
||||
/* Disabled by default */
|
||||
#define SLJIT_UTIL_SIMPLE_STACK_ALLOCATION 0
|
||||
#endif
|
||||
|
||||
/* Single threaded application. Does not require any locks. */
|
||||
#ifndef SLJIT_SINGLE_THREADED
|
||||
/* Disabled by default. */
|
||||
#define SLJIT_SINGLE_THREADED 0
|
||||
#endif
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
/* Configuration */
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
/* If SLJIT_STD_MACROS_DEFINED is not defined, the application should
|
||||
define SLJIT_MALLOC, SLJIT_FREE, SLJIT_MEMCPY, and NULL. */
|
||||
#ifndef SLJIT_STD_MACROS_DEFINED
|
||||
/* Disabled by default. */
|
||||
#define SLJIT_STD_MACROS_DEFINED 0
|
||||
#endif
|
||||
|
||||
/* Executable code allocation:
|
||||
If SLJIT_EXECUTABLE_ALLOCATOR is not defined, the application should
|
||||
define SLJIT_MALLOC_EXEC and SLJIT_FREE_EXEC.
|
||||
Optionally, depending on the implementation used for the allocator,
|
||||
SLJIT_EXEC_OFFSET and SLJIT_UPDATE_WX_FLAGS might also be needed. */
|
||||
#ifndef SLJIT_EXECUTABLE_ALLOCATOR
|
||||
/* Enabled by default. */
|
||||
#define SLJIT_EXECUTABLE_ALLOCATOR 1
|
||||
|
||||
/* When SLJIT_PROT_EXECUTABLE_ALLOCATOR is enabled SLJIT uses
|
||||
an allocator which does not set writable and executable
|
||||
permission flags at the same time.
|
||||
Instead, it creates a shared memory segment (usually backed by a file)
|
||||
and maps it twice, with different permissions, depending on the use
|
||||
case.
|
||||
The trade-off is increased use of virtual memory, incompatibility with
|
||||
fork(), and some possible additional security risks by the use of
|
||||
publicly accessible files for the generated code. */
|
||||
#ifndef SLJIT_PROT_EXECUTABLE_ALLOCATOR
|
||||
/* Disabled by default. */
|
||||
#define SLJIT_PROT_EXECUTABLE_ALLOCATOR 0
|
||||
#endif
|
||||
|
||||
/* When SLJIT_WX_EXECUTABLE_ALLOCATOR is enabled SLJIT uses an
|
||||
allocator which does not set writable and executable permission
|
||||
flags at the same time.
|
||||
Instead, it creates a new independent map on each invocation and
|
||||
switches permissions at the underlying pages as needed.
|
||||
The trade-off is increased memory use and degraded performance. */
|
||||
#ifndef SLJIT_WX_EXECUTABLE_ALLOCATOR
|
||||
/* Disabled by default. */
|
||||
#define SLJIT_WX_EXECUTABLE_ALLOCATOR 0
|
||||
#endif
|
||||
|
||||
#endif /* !SLJIT_EXECUTABLE_ALLOCATOR */
|
||||
|
||||
/* Return with error when an invalid argument is passed. */
|
||||
#ifndef SLJIT_ARGUMENT_CHECKS
|
||||
/* Disabled by default */
|
||||
#define SLJIT_ARGUMENT_CHECKS 0
|
||||
#endif
|
||||
|
||||
/* Debug checks (assertions, etc.). */
|
||||
#ifndef SLJIT_DEBUG
|
||||
/* Enabled by default */
|
||||
#define SLJIT_DEBUG 1
|
||||
#endif
|
||||
|
||||
/* Verbose operations. */
|
||||
#ifndef SLJIT_VERBOSE
|
||||
/* Enabled by default */
|
||||
#define SLJIT_VERBOSE 1
|
||||
#endif
|
||||
|
||||
/*
|
||||
SLJIT_IS_FPU_AVAILABLE
|
||||
The availability of the FPU can be controlled by SLJIT_IS_FPU_AVAILABLE.
|
||||
zero value - FPU is NOT present.
|
||||
nonzero value - FPU is present.
|
||||
*/
|
||||
|
||||
/* For further configurations, see the beginning of sljitConfigInternal.h */
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* SLJIT_CONFIG_H_ */
|
||||
@@ -1,188 +0,0 @@
|
||||
/*
|
||||
* Stack-less Just-In-Time compiler
|
||||
*
|
||||
* Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef SLJIT_CONFIG_CPU_H_
|
||||
#define SLJIT_CONFIG_CPU_H_
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
/* Architecture */
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
/* Architecture selection. */
|
||||
/* #define SLJIT_CONFIG_X86_32 1 */
|
||||
/* #define SLJIT_CONFIG_X86_64 1 */
|
||||
/* #define SLJIT_CONFIG_ARM_V6 1 */
|
||||
/* #define SLJIT_CONFIG_ARM_V7 1 */
|
||||
/* #define SLJIT_CONFIG_ARM_THUMB2 1 */
|
||||
/* #define SLJIT_CONFIG_ARM_64 1 */
|
||||
/* #define SLJIT_CONFIG_PPC_32 1 */
|
||||
/* #define SLJIT_CONFIG_PPC_64 1 */
|
||||
/* #define SLJIT_CONFIG_MIPS_32 1 */
|
||||
/* #define SLJIT_CONFIG_MIPS_64 1 */
|
||||
/* #define SLJIT_CONFIG_RISCV_32 1 */
|
||||
/* #define SLJIT_CONFIG_RISCV_64 1 */
|
||||
/* #define SLJIT_CONFIG_S390X 1 */
|
||||
/* #define SLJIT_CONFIG_LOONGARCH_64 */
|
||||
|
||||
/* #define SLJIT_CONFIG_AUTO 1 */
|
||||
/* #define SLJIT_CONFIG_UNSUPPORTED 1 */
|
||||
|
||||
/*****************/
|
||||
/* Sanity check. */
|
||||
/*****************/
|
||||
|
||||
#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) \
|
||||
+ (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) \
|
||||
+ (defined SLJIT_CONFIG_ARM_V6 && SLJIT_CONFIG_ARM_V6) \
|
||||
+ (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7) \
|
||||
+ (defined SLJIT_CONFIG_ARM_THUMB2 && SLJIT_CONFIG_ARM_THUMB2) \
|
||||
+ (defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64) \
|
||||
+ (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) \
|
||||
+ (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) \
|
||||
+ (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) \
|
||||
+ (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64) \
|
||||
+ (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) \
|
||||
+ (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64) \
|
||||
+ (defined SLJIT_CONFIG_S390X && SLJIT_CONFIG_S390X) \
|
||||
+ (defined SLJIT_CONFIG_LOONGARCH_64 && SLJIT_CONFIG_LOONGARCH_64) \
|
||||
+ (defined SLJIT_CONFIG_AUTO && SLJIT_CONFIG_AUTO) \
|
||||
+ (defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED) >= 2
|
||||
#error "Multiple architectures are selected"
|
||||
#endif
|
||||
|
||||
#if !(defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) \
|
||||
&& !(defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) \
|
||||
&& !(defined SLJIT_CONFIG_ARM_V6 && SLJIT_CONFIG_ARM_V6) \
|
||||
&& !(defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7) \
|
||||
&& !(defined SLJIT_CONFIG_ARM_THUMB2 && SLJIT_CONFIG_ARM_THUMB2) \
|
||||
&& !(defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64) \
|
||||
&& !(defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) \
|
||||
&& !(defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) \
|
||||
&& !(defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) \
|
||||
&& !(defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64) \
|
||||
&& !(defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) \
|
||||
&& !(defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64) \
|
||||
&& !(defined SLJIT_CONFIG_S390X && SLJIT_CONFIG_S390X) \
|
||||
&& !(defined SLJIT_CONFIG_LOONGARCH_64 && SLJIT_CONFIG_LOONGARCH_64) \
|
||||
&& !(defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED) \
|
||||
&& !(defined SLJIT_CONFIG_AUTO && SLJIT_CONFIG_AUTO)
|
||||
#if defined SLJIT_CONFIG_AUTO && !SLJIT_CONFIG_AUTO
|
||||
#error "An architecture must be selected"
|
||||
#else /* SLJIT_CONFIG_AUTO */
|
||||
#define SLJIT_CONFIG_AUTO 1
|
||||
#endif /* !SLJIT_CONFIG_AUTO */
|
||||
#endif /* !SLJIT_CONFIG */
|
||||
|
||||
/********************************************************/
|
||||
/* Automatic CPU detection (requires compiler support). */
|
||||
/********************************************************/
|
||||
|
||||
#if (defined SLJIT_CONFIG_AUTO && SLJIT_CONFIG_AUTO)
|
||||
#ifndef _WIN32
|
||||
|
||||
#if defined(__i386__) || defined(__i386)
|
||||
#define SLJIT_CONFIG_X86_32 1
|
||||
#elif defined(__x86_64__)
|
||||
#define SLJIT_CONFIG_X86_64 1
|
||||
#elif defined(__aarch64__)
|
||||
#define SLJIT_CONFIG_ARM_64 1
|
||||
#elif defined(__thumb2__)
|
||||
#define SLJIT_CONFIG_ARM_THUMB2 1
|
||||
#elif (defined(__ARM_ARCH) && __ARM_ARCH >= 7) || \
|
||||
((defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7S__)) \
|
||||
|| (defined(__ARM_ARCH_8A__) || defined(__ARM_ARCH_8R__)) \
|
||||
|| (defined(__ARM_ARCH_9A__)))
|
||||
#define SLJIT_CONFIG_ARM_V7 1
|
||||
#elif defined(__arm__) || defined (__ARM__)
|
||||
#define SLJIT_CONFIG_ARM_V6 1
|
||||
#elif defined(__ppc64__) || defined(__powerpc64__) || (defined(_ARCH_PPC64) && defined(__64BIT__)) || (defined(_POWER) && defined(__64BIT__))
|
||||
#define SLJIT_CONFIG_PPC_64 1
|
||||
#elif defined(__ppc__) || defined(__powerpc__) || defined(_ARCH_PPC) || defined(_ARCH_PWR) || defined(_ARCH_PWR2) || defined(_POWER)
|
||||
#define SLJIT_CONFIG_PPC_32 1
|
||||
#elif defined(__mips__) && !defined(_LP64)
|
||||
#define SLJIT_CONFIG_MIPS_32 1
|
||||
#elif defined(__mips64)
|
||||
#define SLJIT_CONFIG_MIPS_64 1
|
||||
#elif defined (__riscv_xlen) && (__riscv_xlen == 32)
|
||||
#define SLJIT_CONFIG_RISCV_32 1
|
||||
#elif defined (__riscv_xlen) && (__riscv_xlen == 64)
|
||||
#define SLJIT_CONFIG_RISCV_64 1
|
||||
#elif defined (__loongarch_lp64)
|
||||
#define SLJIT_CONFIG_LOONGARCH_64 1
|
||||
#elif defined(__s390x__)
|
||||
#define SLJIT_CONFIG_S390X 1
|
||||
#else
|
||||
/* Unsupported architecture */
|
||||
#define SLJIT_CONFIG_UNSUPPORTED 1
|
||||
#endif
|
||||
|
||||
#else /* _WIN32 */
|
||||
|
||||
#if defined(_M_X64) || defined(__x86_64__)
|
||||
#define SLJIT_CONFIG_X86_64 1
|
||||
#elif (defined(_M_ARM) && _M_ARM >= 7 && defined(_M_ARMT)) || defined(__thumb2__)
|
||||
#define SLJIT_CONFIG_ARM_THUMB2 1
|
||||
#elif (defined(_M_ARM) && _M_ARM >= 7)
|
||||
#define SLJIT_CONFIG_ARM_V7 1
|
||||
#elif defined(_ARM_)
|
||||
#define SLJIT_CONFIG_ARM_V6 1
|
||||
#elif defined(_M_ARM64) || defined(__aarch64__)
|
||||
#define SLJIT_CONFIG_ARM_64 1
|
||||
#else
|
||||
#define SLJIT_CONFIG_X86_32 1
|
||||
#endif
|
||||
|
||||
#endif /* !_WIN32 */
|
||||
#endif /* SLJIT_CONFIG_AUTO */
|
||||
|
||||
#if (defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED)
|
||||
#undef SLJIT_EXECUTABLE_ALLOCATOR
|
||||
#endif /* SLJIT_CONFIG_UNSUPPORTED */
|
||||
|
||||
/******************************/
|
||||
/* CPU family type detection. */
|
||||
/******************************/
|
||||
|
||||
#if (defined SLJIT_CONFIG_ARM_V6 && SLJIT_CONFIG_ARM_V6) || (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7) \
|
||||
|| (defined SLJIT_CONFIG_ARM_THUMB2 && SLJIT_CONFIG_ARM_THUMB2)
|
||||
#define SLJIT_CONFIG_ARM_32 1
|
||||
#endif
|
||||
|
||||
#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) || (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
|
||||
#define SLJIT_CONFIG_X86 1
|
||||
#elif (defined SLJIT_CONFIG_ARM_32 && SLJIT_CONFIG_ARM_32) || (defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64)
|
||||
#define SLJIT_CONFIG_ARM 1
|
||||
#elif (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) || (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
|
||||
#define SLJIT_CONFIG_PPC 1
|
||||
#elif (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) || (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64)
|
||||
#define SLJIT_CONFIG_MIPS 1
|
||||
#elif (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) || (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64)
|
||||
#define SLJIT_CONFIG_RISCV 1
|
||||
#elif (defined SLJIT_CONFIG_LOONGARCH_64 && SLJIT_CONFIG_LOONGARCH_64)
|
||||
#define SLJIT_CONFIG_LOONGARCH 1
|
||||
#endif
|
||||
|
||||
#endif /* SLJIT_CONFIG_CPU_H_ */
|
||||
@@ -1,944 +0,0 @@
|
||||
/*
|
||||
* Stack-less Just-In-Time compiler
|
||||
*
|
||||
* Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef SLJIT_CONFIG_INTERNAL_H_
|
||||
#define SLJIT_CONFIG_INTERNAL_H_
|
||||
|
||||
#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \
|
||||
|| (defined SLJIT_DEBUG && SLJIT_DEBUG && (!defined(SLJIT_ASSERT) || !defined(SLJIT_UNREACHABLE)))
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
#if (defined SLJIT_DEBUG && SLJIT_DEBUG \
|
||||
&& (!defined(SLJIT_ASSERT) || !defined(SLJIT_UNREACHABLE) || !defined(SLJIT_HALT_PROCESS)))
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
SLJIT defines the following architecture dependent types and macros:
|
||||
|
||||
Types:
|
||||
sljit_s8, sljit_u8 : signed and unsigned 8 bit integer type
|
||||
sljit_s16, sljit_u16 : signed and unsigned 16 bit integer type
|
||||
sljit_s32, sljit_u32 : signed and unsigned 32 bit integer type
|
||||
sljit_sw, sljit_uw : signed and unsigned machine word, enough to store a pointer
|
||||
sljit_sp, sljit_up : signed and unsigned pointer value (usually the same as
|
||||
sljit_uw, but some 64 bit ABIs may use 32 bit pointers)
|
||||
sljit_f32 : 32 bit single precision floating point value
|
||||
sljit_f64 : 64 bit double precision floating point value
|
||||
|
||||
Macros for feature detection (boolean):
|
||||
SLJIT_32BIT_ARCHITECTURE : 32 bit architecture
|
||||
SLJIT_64BIT_ARCHITECTURE : 64 bit architecture
|
||||
SLJIT_LITTLE_ENDIAN : little endian architecture
|
||||
SLJIT_BIG_ENDIAN : big endian architecture
|
||||
SLJIT_UNALIGNED : unaligned memory accesses for non-fpu operations are supported
|
||||
SLJIT_FPU_UNALIGNED : unaligned memory accesses for fpu operations are supported
|
||||
SLJIT_MASKED_SHIFT : all word shifts are always masked
|
||||
SLJIT_MASKED_SHIFT32 : all 32 bit shifts are always masked
|
||||
SLJIT_INDIRECT_CALL : see SLJIT_FUNC_ADDR() for more information
|
||||
|
||||
Constants:
|
||||
SLJIT_NUMBER_OF_REGISTERS : number of available registers
|
||||
SLJIT_NUMBER_OF_SCRATCH_REGISTERS : number of available scratch registers
|
||||
SLJIT_NUMBER_OF_SAVED_REGISTERS : number of available saved registers
|
||||
SLJIT_NUMBER_OF_FLOAT_REGISTERS : number of available floating point registers
|
||||
SLJIT_NUMBER_OF_SCRATCH_FLOAT_REGISTERS : number of available scratch floating point registers
|
||||
SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS : number of available saved floating point registers
|
||||
SLJIT_NUMBER_OF_VECTOR_REGISTERS : number of available vector registers
|
||||
SLJIT_NUMBER_OF_SCRATCH_VECTOR_REGISTERS : number of available scratch vector registers
|
||||
SLJIT_NUMBER_OF_SAVED_VECTOR_REGISTERS : number of available saved vector registers
|
||||
SLJIT_NUMBER_OF_TEMPORARY_REGISTERS : number of available temporary registers
|
||||
SLJIT_NUMBER_OF_TEMPORARY_FLOAT_REGISTERS : number of available temporary floating point registers
|
||||
SLJIT_NUMBER_OF_TEMPORARY_VECTOR_REGISTERS : number of available temporary vector registers
|
||||
SLJIT_SEPARATE_VECTOR_REGISTERS : if this macro is defined, the vector registers do not
|
||||
overlap with floating point registers
|
||||
SLJIT_WORD_SHIFT : the shift required to apply when accessing a sljit_sw/sljit_uw array by index
|
||||
SLJIT_F32_SHIFT : the shift required to apply when accessing
|
||||
a single precision floating point array by index
|
||||
SLJIT_F64_SHIFT : the shift required to apply when accessing
|
||||
a double precision floating point array by index
|
||||
SLJIT_PREF_SHIFT_REG : x86 systems prefers ecx for shifting by register
|
||||
the scratch register index of ecx is stored in this variable
|
||||
SLJIT_LOCALS_OFFSET : local space starting offset (SLJIT_SP + SLJIT_LOCALS_OFFSET)
|
||||
SLJIT_RETURN_ADDRESS_OFFSET : a return instruction always adds this offset to the return address
|
||||
SLJIT_CONV_MAX_FLOAT : result when a floating point value is converted to integer
|
||||
and the floating point value is higher than the maximum integer value
|
||||
(possible values: SLJIT_CONV_RESULT_MAX_INT or SLJIT_CONV_RESULT_MIN_INT)
|
||||
SLJIT_CONV_MIN_FLOAT : result when a floating point value is converted to integer
|
||||
and the floating point value is lower than the minimum integer value
|
||||
(possible values: SLJIT_CONV_RESULT_MAX_INT or SLJIT_CONV_RESULT_MIN_INT)
|
||||
SLJIT_CONV_NAN_FLOAT : result when a NaN floating point value is converted to integer
|
||||
(possible values: SLJIT_CONV_RESULT_MAX_INT, SLJIT_CONV_RESULT_MIN_INT,
|
||||
or SLJIT_CONV_RESULT_ZERO)
|
||||
|
||||
Other macros:
|
||||
SLJIT_TMP_R0 .. R9 : accessing temporary registers
|
||||
SLJIT_TMP_R(i) : accessing temporary registers
|
||||
SLJIT_TMP_FR0 .. FR9 : accessing temporary floating point registers
|
||||
SLJIT_TMP_FR(i) : accessing temporary floating point registers
|
||||
SLJIT_TMP_VR0 .. VR9 : accessing temporary vector registers
|
||||
SLJIT_TMP_VR(i) : accessing temporary vector registers
|
||||
SLJIT_TMP_DEST_REG : a temporary register for results
|
||||
SLJIT_TMP_MEM_REG : a temporary base register for accessing memory
|
||||
(can be the same as SLJIT_TMP_DEST_REG)
|
||||
SLJIT_TMP_DEST_FREG : a temporary register for float results
|
||||
SLJIT_TMP_DEST_VREG : a temporary register for vector results
|
||||
SLJIT_FUNC : calling convention attribute for both calling JIT from C and C calling back from JIT
|
||||
SLJIT_W(number) : defining 64 bit constants on 64 bit architectures (platform independent helper)
|
||||
SLJIT_F64_SECOND(reg) : provides the register index of the second 32 bit part of a 64 bit
|
||||
floating point register when SLJIT_HAS_F64_AS_F32_PAIR returns non-zero
|
||||
*/
|
||||
|
||||
/***********************************************************/
|
||||
/* Intel Control-flow Enforcement Technology (CET) spport. */
|
||||
/***********************************************************/
|
||||
|
||||
#ifdef SLJIT_CONFIG_X86
|
||||
|
||||
#if defined(__CET__) && !(defined SLJIT_CONFIG_X86_CET && SLJIT_CONFIG_X86_CET)
|
||||
#define SLJIT_CONFIG_X86_CET 1
|
||||
#endif
|
||||
|
||||
#if (defined SLJIT_CONFIG_X86_CET && SLJIT_CONFIG_X86_CET) && defined(__GNUC__)
|
||||
#include <x86intrin.h>
|
||||
#endif
|
||||
|
||||
#endif /* SLJIT_CONFIG_X86 */
|
||||
|
||||
/**********************************/
|
||||
/* External function definitions. */
|
||||
/**********************************/
|
||||
|
||||
/* General macros:
|
||||
Note: SLJIT is designed to be independent from them as possible.
|
||||
|
||||
In release mode (SLJIT_DEBUG is not defined) only the following
|
||||
external functions are needed:
|
||||
*/
|
||||
|
||||
#ifndef SLJIT_MALLOC
|
||||
#define SLJIT_MALLOC(size, allocator_data) (malloc(size))
|
||||
#endif
|
||||
|
||||
#ifndef SLJIT_FREE
|
||||
#define SLJIT_FREE(ptr, allocator_data) (free(ptr))
|
||||
#endif
|
||||
|
||||
#ifndef SLJIT_MEMCPY
|
||||
#define SLJIT_MEMCPY(dest, src, len) (memcpy(dest, src, len))
|
||||
#endif
|
||||
|
||||
#ifndef SLJIT_MEMMOVE
|
||||
#define SLJIT_MEMMOVE(dest, src, len) (memmove(dest, src, len))
|
||||
#endif
|
||||
|
||||
#ifndef SLJIT_ZEROMEM
|
||||
#define SLJIT_ZEROMEM(dest, len) (memset(dest, 0, len))
|
||||
#endif
|
||||
|
||||
/***************************/
|
||||
/* Compiler helper macros. */
|
||||
/***************************/
|
||||
|
||||
#if !defined(SLJIT_LIKELY) && !defined(SLJIT_UNLIKELY)
|
||||
|
||||
#if defined(__GNUC__) && (__GNUC__ >= 3)
|
||||
#define SLJIT_LIKELY(x) __builtin_expect((x), 1)
|
||||
#define SLJIT_UNLIKELY(x) __builtin_expect((x), 0)
|
||||
#else
|
||||
#define SLJIT_LIKELY(x) (x)
|
||||
#define SLJIT_UNLIKELY(x) (x)
|
||||
#endif
|
||||
|
||||
#endif /* !defined(SLJIT_LIKELY) && !defined(SLJIT_UNLIKELY) */
|
||||
|
||||
#ifndef SLJIT_INLINE
|
||||
/* Inline functions. Some old compilers do not support them. */
|
||||
#ifdef __SUNPRO_C
|
||||
#if __SUNPRO_C < 0x560
|
||||
#define SLJIT_INLINE
|
||||
#else
|
||||
#define SLJIT_INLINE inline
|
||||
#endif /* __SUNPRO_C */
|
||||
#else
|
||||
#define SLJIT_INLINE __inline
|
||||
#endif
|
||||
#endif /* !SLJIT_INLINE */
|
||||
|
||||
#ifndef SLJIT_NOINLINE
|
||||
/* Not inline functions. */
|
||||
#if defined(__GNUC__)
|
||||
#define SLJIT_NOINLINE __attribute__ ((noinline))
|
||||
#else
|
||||
#define SLJIT_NOINLINE
|
||||
#endif
|
||||
#endif /* !SLJIT_INLINE */
|
||||
|
||||
#ifndef SLJIT_UNUSED_ARG
|
||||
/* Unused arguments. */
|
||||
#define SLJIT_UNUSED_ARG(arg) (void)arg
|
||||
#endif
|
||||
|
||||
/*********************************/
|
||||
/* Type of public API functions. */
|
||||
/*********************************/
|
||||
|
||||
#ifndef SLJIT_API_FUNC_ATTRIBUTE
|
||||
#if (defined SLJIT_CONFIG_STATIC && SLJIT_CONFIG_STATIC)
|
||||
/* Static ABI functions. For all-in-one programs. */
|
||||
|
||||
#if defined(__GNUC__)
|
||||
/* Disable unused warnings in gcc. */
|
||||
#define SLJIT_API_FUNC_ATTRIBUTE static __attribute__((unused))
|
||||
#else
|
||||
#define SLJIT_API_FUNC_ATTRIBUTE static
|
||||
#endif
|
||||
|
||||
#else
|
||||
#define SLJIT_API_FUNC_ATTRIBUTE
|
||||
#endif /* (defined SLJIT_CONFIG_STATIC && SLJIT_CONFIG_STATIC) */
|
||||
#endif /* defined SLJIT_API_FUNC_ATTRIBUTE */
|
||||
|
||||
/****************************/
|
||||
/* Instruction cache flush. */
|
||||
/****************************/
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <AvailabilityMacros.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
* TODO:
|
||||
*
|
||||
* clang >= 15 could be safe to enable below
|
||||
* older versions are known to abort in some targets
|
||||
* https://github.com/PhilipHazel/pcre2/issues/92
|
||||
*
|
||||
* beware some vendors (ex: Microsoft, Apple) are known to have
|
||||
* removed the code to support this builtin even if the call for
|
||||
* __has_builtin reports it is available.
|
||||
*
|
||||
* make sure linking doesn't fail because __clear_cache() is
|
||||
* missing before changing it or add an exception so that the
|
||||
* system provided method that should be defined below is used
|
||||
* instead.
|
||||
*/
|
||||
#if (!defined SLJIT_CACHE_FLUSH && defined __has_builtin)
|
||||
#if __has_builtin(__builtin___clear_cache) && !defined(__clang__)
|
||||
|
||||
/*
|
||||
* https://gcc.gnu.org/bugzilla//show_bug.cgi?id=91248
|
||||
* https://gcc.gnu.org/bugzilla//show_bug.cgi?id=93811
|
||||
* gcc's clear_cache builtin for power is broken
|
||||
*/
|
||||
#if !defined(SLJIT_CONFIG_PPC)
|
||||
#define SLJIT_CACHE_FLUSH(from, to) \
|
||||
__builtin___clear_cache((char*)(from), (char*)(to))
|
||||
#endif
|
||||
|
||||
#endif /* gcc >= 10 */
|
||||
#endif /* (!defined SLJIT_CACHE_FLUSH && defined __has_builtin) */
|
||||
|
||||
#ifndef SLJIT_CACHE_FLUSH
|
||||
|
||||
#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86) \
|
||||
|| (defined SLJIT_CONFIG_S390X && SLJIT_CONFIG_S390X)
|
||||
|
||||
/* Not required to implement on archs with unified caches. */
|
||||
#define SLJIT_CACHE_FLUSH(from, to)
|
||||
|
||||
#elif defined(__APPLE__) && MAC_OS_X_VERSION_MIN_REQUIRED >= 1050
|
||||
|
||||
/* Supported by all macs since Mac OS 10.5.
|
||||
However, it does not work on non-jailbroken iOS devices,
|
||||
although the compilation is successful. */
|
||||
#include <libkern/OSCacheControl.h>
|
||||
#define SLJIT_CACHE_FLUSH(from, to) \
|
||||
sys_icache_invalidate((void*)(from), (size_t)((char*)(to) - (char*)(from)))
|
||||
|
||||
#elif (defined SLJIT_CONFIG_PPC && SLJIT_CONFIG_PPC)
|
||||
|
||||
/* The __clear_cache() implementation of GCC is a dummy function on PowerPC. */
|
||||
#define SLJIT_CACHE_FLUSH(from, to) \
|
||||
ppc_cache_flush((from), (to))
|
||||
#define SLJIT_CACHE_FLUSH_OWN_IMPL 1
|
||||
|
||||
#elif defined(_WIN32)
|
||||
|
||||
#define SLJIT_CACHE_FLUSH(from, to) \
|
||||
FlushInstructionCache(GetCurrentProcess(), (void*)(from), (size_t)((char*)(to) - (char*)(from)))
|
||||
|
||||
#elif (defined(__GNUC__) && (__GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))) || defined(__clang__)
|
||||
|
||||
#define SLJIT_CACHE_FLUSH(from, to) \
|
||||
__builtin___clear_cache((char*)(from), (char*)(to))
|
||||
|
||||
#elif defined __ANDROID__
|
||||
|
||||
/* Android ARMv7 with gcc lacks __clear_cache; use cacheflush instead. */
|
||||
#include <sys/cachectl.h>
|
||||
#define SLJIT_CACHE_FLUSH(from, to) \
|
||||
cacheflush((long)(from), (long)(to), 0)
|
||||
|
||||
#else
|
||||
|
||||
/* Call __ARM_NR_cacheflush on ARM-Linux or the corresponding MIPS syscall. */
|
||||
#define SLJIT_CACHE_FLUSH(from, to) \
|
||||
__clear_cache((char*)(from), (char*)(to))
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* !SLJIT_CACHE_FLUSH */
|
||||
|
||||
/******************************************************/
|
||||
/* Integer and floating point type definitions. */
|
||||
/******************************************************/
|
||||
|
||||
/* 8 bit byte type. */
|
||||
typedef unsigned char sljit_u8;
|
||||
typedef signed char sljit_s8;
|
||||
|
||||
/* 16 bit half-word type. */
|
||||
typedef unsigned short int sljit_u16;
|
||||
typedef signed short int sljit_s16;
|
||||
|
||||
/* 32 bit integer type. */
|
||||
typedef unsigned int sljit_u32;
|
||||
typedef signed int sljit_s32;
|
||||
|
||||
/* Machine word type. Enough for storing a pointer.
|
||||
32 bit for 32 bit machines.
|
||||
64 bit for 64 bit machines. */
|
||||
#if (defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED)
|
||||
/* Just to have something. */
|
||||
#define SLJIT_WORD_SHIFT 0
|
||||
typedef unsigned int sljit_uw;
|
||||
typedef int sljit_sw;
|
||||
#elif !(defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) \
|
||||
&& !(defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64) \
|
||||
&& !(defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) \
|
||||
&& !(defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64) \
|
||||
&& !(defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64) \
|
||||
&& !(defined SLJIT_CONFIG_S390X && SLJIT_CONFIG_S390X) \
|
||||
&& !(defined SLJIT_CONFIG_LOONGARCH_64 && SLJIT_CONFIG_LOONGARCH_64)
|
||||
#define SLJIT_32BIT_ARCHITECTURE 1
|
||||
#define SLJIT_WORD_SHIFT 2
|
||||
typedef unsigned int sljit_uw;
|
||||
typedef int sljit_sw;
|
||||
#else
|
||||
#define SLJIT_64BIT_ARCHITECTURE 1
|
||||
#define SLJIT_WORD_SHIFT 3
|
||||
#ifdef _WIN32
|
||||
#ifdef __GNUC__
|
||||
/* These types do not require windows.h */
|
||||
typedef unsigned long long sljit_uw;
|
||||
typedef long long sljit_sw;
|
||||
#else
|
||||
typedef unsigned __int64 sljit_uw;
|
||||
typedef __int64 sljit_sw;
|
||||
#endif
|
||||
#else /* !_WIN32 */
|
||||
typedef unsigned long int sljit_uw;
|
||||
typedef long int sljit_sw;
|
||||
#endif /* _WIN32 */
|
||||
#endif
|
||||
|
||||
typedef sljit_sw sljit_sp;
|
||||
typedef sljit_uw sljit_up;
|
||||
|
||||
/* Floating point types. */
|
||||
typedef float sljit_f32;
|
||||
typedef double sljit_f64;
|
||||
|
||||
/* Shift for pointer sized data. */
|
||||
#define SLJIT_POINTER_SHIFT SLJIT_WORD_SHIFT
|
||||
|
||||
/* Shift for double precision sized data. */
|
||||
#define SLJIT_F32_SHIFT 2
|
||||
#define SLJIT_F64_SHIFT 3
|
||||
|
||||
#define SLJIT_CONV_RESULT_MAX_INT 0
|
||||
#define SLJIT_CONV_RESULT_MIN_INT 1
|
||||
#define SLJIT_CONV_RESULT_ZERO 2
|
||||
|
||||
#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86)
|
||||
#define SLJIT_CONV_MAX_FLOAT SLJIT_CONV_RESULT_MIN_INT
|
||||
#define SLJIT_CONV_MIN_FLOAT SLJIT_CONV_RESULT_MIN_INT
|
||||
#define SLJIT_CONV_NAN_FLOAT SLJIT_CONV_RESULT_MIN_INT
|
||||
#elif (defined SLJIT_CONFIG_ARM && SLJIT_CONFIG_ARM)
|
||||
#define SLJIT_CONV_MAX_FLOAT SLJIT_CONV_RESULT_MAX_INT
|
||||
#define SLJIT_CONV_MIN_FLOAT SLJIT_CONV_RESULT_MIN_INT
|
||||
#define SLJIT_CONV_NAN_FLOAT SLJIT_CONV_RESULT_ZERO
|
||||
#elif (defined SLJIT_CONFIG_MIPS && SLJIT_CONFIG_MIPS)
|
||||
#define SLJIT_CONV_MAX_FLOAT SLJIT_CONV_RESULT_MAX_INT
|
||||
#define SLJIT_CONV_MIN_FLOAT SLJIT_CONV_RESULT_MAX_INT
|
||||
#define SLJIT_CONV_NAN_FLOAT SLJIT_CONV_RESULT_MAX_INT
|
||||
#elif (defined SLJIT_CONFIG_PPC && SLJIT_CONFIG_PPC)
|
||||
#define SLJIT_CONV_MAX_FLOAT SLJIT_CONV_RESULT_MAX_INT
|
||||
#define SLJIT_CONV_MIN_FLOAT SLJIT_CONV_RESULT_MIN_INT
|
||||
#define SLJIT_CONV_NAN_FLOAT SLJIT_CONV_RESULT_MIN_INT
|
||||
#elif (defined SLJIT_CONFIG_RISCV && SLJIT_CONFIG_RISCV)
|
||||
#define SLJIT_CONV_MAX_FLOAT SLJIT_CONV_RESULT_MAX_INT
|
||||
#define SLJIT_CONV_MIN_FLOAT SLJIT_CONV_RESULT_MIN_INT
|
||||
#define SLJIT_CONV_NAN_FLOAT SLJIT_CONV_RESULT_MAX_INT
|
||||
#elif (defined SLJIT_CONFIG_S390X && SLJIT_CONFIG_S390X)
|
||||
#define SLJIT_CONV_MAX_FLOAT SLJIT_CONV_RESULT_MAX_INT
|
||||
#define SLJIT_CONV_MIN_FLOAT SLJIT_CONV_RESULT_MIN_INT
|
||||
#define SLJIT_CONV_NAN_FLOAT SLJIT_CONV_RESULT_MIN_INT
|
||||
#elif (defined SLJIT_CONFIG_LOONGARCH && SLJIT_CONFIG_LOONGARCH)
|
||||
#define SLJIT_CONV_MAX_FLOAT SLJIT_CONV_RESULT_MAX_INT
|
||||
#define SLJIT_CONV_MIN_FLOAT SLJIT_CONV_RESULT_MIN_INT
|
||||
#define SLJIT_CONV_NAN_FLOAT SLJIT_CONV_RESULT_ZERO
|
||||
#else
|
||||
#error "Result for float to integer conversion is not defined"
|
||||
#endif
|
||||
|
||||
#ifndef SLJIT_W
|
||||
|
||||
/* Defining long constants. */
|
||||
#if (defined SLJIT_64BIT_ARCHITECTURE && SLJIT_64BIT_ARCHITECTURE)
|
||||
#ifdef _WIN64
|
||||
#define SLJIT_W(w) (w##ll)
|
||||
#else /* !windows */
|
||||
#define SLJIT_W(w) (w##l)
|
||||
#endif /* windows */
|
||||
#else /* 32 bit */
|
||||
#define SLJIT_W(w) (w)
|
||||
#endif /* unknown */
|
||||
|
||||
#endif /* !SLJIT_W */
|
||||
|
||||
/*************************/
|
||||
/* Endianness detection. */
|
||||
/*************************/
|
||||
|
||||
#if !defined(SLJIT_BIG_ENDIAN) && !defined(SLJIT_LITTLE_ENDIAN)
|
||||
|
||||
/* These macros are mostly useful for the applications. */
|
||||
#if (defined SLJIT_CONFIG_PPC && SLJIT_CONFIG_PPC)
|
||||
|
||||
#ifdef __LITTLE_ENDIAN__
|
||||
#define SLJIT_LITTLE_ENDIAN 1
|
||||
#else
|
||||
#define SLJIT_BIG_ENDIAN 1
|
||||
#endif
|
||||
|
||||
#elif (defined SLJIT_CONFIG_MIPS && SLJIT_CONFIG_MIPS)
|
||||
|
||||
#ifdef __MIPSEL__
|
||||
#define SLJIT_LITTLE_ENDIAN 1
|
||||
#else
|
||||
#define SLJIT_BIG_ENDIAN 1
|
||||
#endif
|
||||
|
||||
#ifndef SLJIT_MIPS_REV
|
||||
|
||||
/* Auto detecting mips revision. */
|
||||
#if (defined __mips_isa_rev) && (__mips_isa_rev >= 6)
|
||||
#define SLJIT_MIPS_REV 6
|
||||
#elif defined(__mips_isa_rev) && __mips_isa_rev >= 1
|
||||
#define SLJIT_MIPS_REV __mips_isa_rev
|
||||
#elif defined(__clang__) \
|
||||
&& (defined(_MIPS_ARCH_OCTEON) || defined(_MIPS_ARCH_P5600))
|
||||
/* clang either forgets to define (clang-7) __mips_isa_rev at all
|
||||
* or sets it to zero (clang-8,-9) for -march=octeon (MIPS64 R2+)
|
||||
* and -march=p5600 (MIPS32 R5).
|
||||
* It also sets the __mips macro to 64 or 32 for -mipsN when N <= 5
|
||||
* (should be set to N exactly) so we cannot rely on this too.
|
||||
*/
|
||||
#define SLJIT_MIPS_REV 1
|
||||
#endif
|
||||
|
||||
#endif /* !SLJIT_MIPS_REV */
|
||||
|
||||
#elif (defined SLJIT_CONFIG_S390X && SLJIT_CONFIG_S390X)
|
||||
|
||||
#define SLJIT_BIG_ENDIAN 1
|
||||
|
||||
#else
|
||||
#define SLJIT_LITTLE_ENDIAN 1
|
||||
#endif
|
||||
|
||||
#endif /* !defined(SLJIT_BIG_ENDIAN) && !defined(SLJIT_LITTLE_ENDIAN) */
|
||||
|
||||
/* Sanity check. */
|
||||
#if (defined SLJIT_BIG_ENDIAN && SLJIT_BIG_ENDIAN) && (defined SLJIT_LITTLE_ENDIAN && SLJIT_LITTLE_ENDIAN)
|
||||
#error "Exactly one endianness must be selected"
|
||||
#endif
|
||||
|
||||
#if !(defined SLJIT_BIG_ENDIAN && SLJIT_BIG_ENDIAN) && !(defined SLJIT_LITTLE_ENDIAN && SLJIT_LITTLE_ENDIAN)
|
||||
#error "Exactly one endianness must be selected"
|
||||
#endif
|
||||
|
||||
#ifndef SLJIT_UNALIGNED
|
||||
|
||||
#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86) \
|
||||
|| (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7) \
|
||||
|| (defined SLJIT_CONFIG_ARM_THUMB2 && SLJIT_CONFIG_ARM_THUMB2) \
|
||||
|| (defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64) \
|
||||
|| (defined SLJIT_CONFIG_PPC && SLJIT_CONFIG_PPC) \
|
||||
|| (defined SLJIT_CONFIG_RISCV && SLJIT_CONFIG_RISCV) \
|
||||
|| (defined SLJIT_CONFIG_S390X && SLJIT_CONFIG_S390X) \
|
||||
|| (defined SLJIT_CONFIG_LOONGARCH && SLJIT_CONFIG_LOONGARCH)
|
||||
#define SLJIT_UNALIGNED 1
|
||||
#endif
|
||||
|
||||
#endif /* !SLJIT_UNALIGNED */
|
||||
|
||||
#ifndef SLJIT_FPU_UNALIGNED
|
||||
|
||||
#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86) \
|
||||
|| (defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64) \
|
||||
|| (defined SLJIT_CONFIG_PPC && SLJIT_CONFIG_PPC) \
|
||||
|| (defined SLJIT_CONFIG_RISCV && SLJIT_CONFIG_RISCV) \
|
||||
|| (defined SLJIT_CONFIG_S390X && SLJIT_CONFIG_S390X) \
|
||||
|| (defined SLJIT_CONFIG_LOONGARCH && SLJIT_CONFIG_LOONGARCH)
|
||||
#define SLJIT_FPU_UNALIGNED 1
|
||||
#endif
|
||||
|
||||
#endif /* !SLJIT_FPU_UNALIGNED */
|
||||
|
||||
#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
|
||||
/* Auto detect SSE2 support using CPUID.
|
||||
On 64 bit x86 cpus, sse2 must be present. */
|
||||
#define SLJIT_DETECT_SSE2 1
|
||||
#endif
|
||||
|
||||
/*****************************************************************************************/
|
||||
/* Calling convention of functions generated by SLJIT or called from the generated code. */
|
||||
/*****************************************************************************************/
|
||||
|
||||
#ifndef SLJIT_FUNC
|
||||
#define SLJIT_FUNC
|
||||
#endif /* !SLJIT_FUNC */
|
||||
|
||||
#ifndef SLJIT_INDIRECT_CALL
|
||||
#if ((defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) && (!defined _CALL_ELF || _CALL_ELF == 1)) \
|
||||
|| ((defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) && defined _AIX)
|
||||
/* It seems certain ppc compilers use an indirect addressing for functions
|
||||
which makes things complicated. */
|
||||
#define SLJIT_INDIRECT_CALL 1
|
||||
#endif
|
||||
#endif /* SLJIT_INDIRECT_CALL */
|
||||
|
||||
/* The offset which needs to be subtracted from the return address to
|
||||
determine the next executed instruction after return. */
|
||||
#ifndef SLJIT_RETURN_ADDRESS_OFFSET
|
||||
#define SLJIT_RETURN_ADDRESS_OFFSET 0
|
||||
#endif /* SLJIT_RETURN_ADDRESS_OFFSET */
|
||||
|
||||
/***************************************************/
|
||||
/* Functions of the built-in executable allocator. */
|
||||
/***************************************************/
|
||||
|
||||
#if (defined SLJIT_EXECUTABLE_ALLOCATOR && SLJIT_EXECUTABLE_ALLOCATOR)
|
||||
SLJIT_API_FUNC_ATTRIBUTE void* sljit_malloc_exec(sljit_uw size);
|
||||
SLJIT_API_FUNC_ATTRIBUTE void sljit_free_exec(void* ptr);
|
||||
/* Note: sljitLir.h also defines sljit_free_unused_memory_exec() function. */
|
||||
#define SLJIT_BUILTIN_MALLOC_EXEC(size, exec_allocator_data) sljit_malloc_exec(size)
|
||||
#define SLJIT_BUILTIN_FREE_EXEC(ptr, exec_allocator_data) sljit_free_exec(ptr)
|
||||
|
||||
#ifndef SLJIT_MALLOC_EXEC
|
||||
#define SLJIT_MALLOC_EXEC(size, exec_allocator_data) SLJIT_BUILTIN_MALLOC_EXEC((size), (exec_allocator_data))
|
||||
#endif /* SLJIT_MALLOC_EXEC */
|
||||
|
||||
#ifndef SLJIT_FREE_EXEC
|
||||
#define SLJIT_FREE_EXEC(ptr, exec_allocator_data) SLJIT_BUILTIN_FREE_EXEC((ptr), (exec_allocator_data))
|
||||
#endif /* SLJIT_FREE_EXEC */
|
||||
|
||||
#if (defined SLJIT_PROT_EXECUTABLE_ALLOCATOR && SLJIT_PROT_EXECUTABLE_ALLOCATOR)
|
||||
SLJIT_API_FUNC_ATTRIBUTE sljit_sw sljit_exec_offset(void *code);
|
||||
#define SLJIT_EXEC_OFFSET(code) sljit_exec_offset(code)
|
||||
#endif /* SLJIT_PROT_EXECUTABLE_ALLOCATOR */
|
||||
|
||||
#endif /* SLJIT_EXECUTABLE_ALLOCATOR */
|
||||
|
||||
#ifndef SLJIT_EXEC_OFFSET
|
||||
#define SLJIT_EXEC_OFFSET(ptr) 0
|
||||
#endif
|
||||
|
||||
/**********************************************/
|
||||
/* Registers and locals offset determination. */
|
||||
/**********************************************/
|
||||
|
||||
#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
|
||||
|
||||
#define SLJIT_NUMBER_OF_REGISTERS 12
|
||||
#define SLJIT_NUMBER_OF_SAVED_REGISTERS 7
|
||||
#define SLJIT_NUMBER_OF_TEMPORARY_REGISTERS 1
|
||||
#define SLJIT_NUMBER_OF_FLOAT_REGISTERS 7
|
||||
#define SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS 0
|
||||
#define SLJIT_NUMBER_OF_TEMPORARY_FLOAT_REGISTERS 1
|
||||
#define SLJIT_TMP_DEST_REG SLJIT_TMP_R0
|
||||
#define SLJIT_TMP_MEM_REG SLJIT_TMP_R0
|
||||
#define SLJIT_TMP_DEST_FREG SLJIT_TMP_FR0
|
||||
#define SLJIT_LOCALS_OFFSET_BASE (8 * (sljit_s32)sizeof(sljit_sw))
|
||||
#define SLJIT_PREF_SHIFT_REG SLJIT_R2
|
||||
#define SLJIT_MASKED_SHIFT 1
|
||||
#define SLJIT_MASKED_SHIFT32 1
|
||||
|
||||
#elif (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
|
||||
|
||||
#define SLJIT_NUMBER_OF_REGISTERS 13
|
||||
#define SLJIT_NUMBER_OF_TEMPORARY_REGISTERS 2
|
||||
#define SLJIT_NUMBER_OF_FLOAT_REGISTERS 15
|
||||
#define SLJIT_NUMBER_OF_TEMPORARY_FLOAT_REGISTERS 1
|
||||
#ifndef _WIN64
|
||||
#define SLJIT_NUMBER_OF_SAVED_REGISTERS 6
|
||||
#define SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS 0
|
||||
#define SLJIT_LOCALS_OFFSET_BASE 0
|
||||
#else /* _WIN64 */
|
||||
#define SLJIT_NUMBER_OF_SAVED_REGISTERS 8
|
||||
#define SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS 10
|
||||
#define SLJIT_LOCALS_OFFSET_BASE (4 * (sljit_s32)sizeof(sljit_sw))
|
||||
#endif /* !_WIN64 */
|
||||
#define SLJIT_TMP_DEST_REG SLJIT_TMP_R0
|
||||
#define SLJIT_TMP_MEM_REG SLJIT_TMP_R0
|
||||
#define SLJIT_TMP_DEST_FREG SLJIT_TMP_FR0
|
||||
#define SLJIT_PREF_SHIFT_REG SLJIT_R3
|
||||
#define SLJIT_MASKED_SHIFT 1
|
||||
#define SLJIT_MASKED_SHIFT32 1
|
||||
|
||||
#elif (defined SLJIT_CONFIG_ARM_32 && SLJIT_CONFIG_ARM_32)
|
||||
|
||||
#define SLJIT_NUMBER_OF_REGISTERS 12
|
||||
#define SLJIT_NUMBER_OF_SAVED_REGISTERS 8
|
||||
#define SLJIT_NUMBER_OF_TEMPORARY_REGISTERS 2
|
||||
#define SLJIT_NUMBER_OF_FLOAT_REGISTERS 14
|
||||
#define SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS 8
|
||||
#define SLJIT_NUMBER_OF_TEMPORARY_FLOAT_REGISTERS 2
|
||||
#define SLJIT_TMP_DEST_REG SLJIT_TMP_R1
|
||||
#define SLJIT_TMP_MEM_REG SLJIT_TMP_R1
|
||||
#define SLJIT_TMP_DEST_FREG SLJIT_TMP_FR0
|
||||
#define SLJIT_LOCALS_OFFSET_BASE 0
|
||||
|
||||
#elif (defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64)
|
||||
|
||||
#define SLJIT_NUMBER_OF_REGISTERS 26
|
||||
#define SLJIT_NUMBER_OF_SAVED_REGISTERS 10
|
||||
#define SLJIT_NUMBER_OF_TEMPORARY_REGISTERS 3
|
||||
#define SLJIT_NUMBER_OF_FLOAT_REGISTERS 30
|
||||
#define SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS 8
|
||||
#define SLJIT_NUMBER_OF_TEMPORARY_FLOAT_REGISTERS 2
|
||||
#define SLJIT_TMP_DEST_REG SLJIT_TMP_R0
|
||||
#define SLJIT_TMP_MEM_REG SLJIT_TMP_R0
|
||||
#define SLJIT_TMP_DEST_FREG SLJIT_TMP_FR0
|
||||
#define SLJIT_LOCALS_OFFSET_BASE (2 * (sljit_s32)sizeof(sljit_sw))
|
||||
#define SLJIT_MASKED_SHIFT 1
|
||||
#define SLJIT_MASKED_SHIFT32 1
|
||||
|
||||
#elif (defined SLJIT_CONFIG_PPC && SLJIT_CONFIG_PPC)
|
||||
|
||||
#define SLJIT_NUMBER_OF_REGISTERS 23
|
||||
#define SLJIT_NUMBER_OF_SAVED_REGISTERS 17
|
||||
#define SLJIT_NUMBER_OF_TEMPORARY_REGISTERS 3
|
||||
#define SLJIT_NUMBER_OF_FLOAT_REGISTERS 30
|
||||
#define SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS 18
|
||||
#define SLJIT_NUMBER_OF_TEMPORARY_FLOAT_REGISTERS 2
|
||||
#define SLJIT_TMP_DEST_REG SLJIT_TMP_R1
|
||||
#define SLJIT_TMP_MEM_REG SLJIT_TMP_R1
|
||||
#define SLJIT_TMP_DEST_FREG SLJIT_TMP_FR0
|
||||
#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) || (defined _AIX)
|
||||
#define SLJIT_LOCALS_OFFSET_BASE ((6 + 8) * (sljit_s32)sizeof(sljit_sw))
|
||||
#elif (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32)
|
||||
/* Add +1 for double alignment. */
|
||||
#define SLJIT_LOCALS_OFFSET_BASE ((3 + 1) * (sljit_s32)sizeof(sljit_sw))
|
||||
#else
|
||||
#define SLJIT_LOCALS_OFFSET_BASE (3 * (sljit_s32)sizeof(sljit_sw))
|
||||
#endif /* SLJIT_CONFIG_PPC_64 || _AIX */
|
||||
|
||||
#elif (defined SLJIT_CONFIG_MIPS && SLJIT_CONFIG_MIPS)
|
||||
|
||||
#define SLJIT_NUMBER_OF_REGISTERS 21
|
||||
#define SLJIT_NUMBER_OF_SAVED_REGISTERS 8
|
||||
#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
|
||||
#define SLJIT_LOCALS_OFFSET_BASE (4 * (sljit_s32)sizeof(sljit_sw))
|
||||
#define SLJIT_NUMBER_OF_FLOAT_REGISTERS 13
|
||||
#define SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS 6
|
||||
#else
|
||||
#define SLJIT_LOCALS_OFFSET_BASE 0
|
||||
#define SLJIT_NUMBER_OF_FLOAT_REGISTERS 29
|
||||
#define SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS 8
|
||||
#endif
|
||||
#define SLJIT_NUMBER_OF_TEMPORARY_REGISTERS 5
|
||||
#define SLJIT_NUMBER_OF_TEMPORARY_FLOAT_REGISTERS 3
|
||||
#define SLJIT_TMP_DEST_REG SLJIT_TMP_R1
|
||||
#define SLJIT_TMP_MEM_REG SLJIT_TMP_R1
|
||||
#define SLJIT_TMP_DEST_FREG SLJIT_TMP_FR0
|
||||
#define SLJIT_MASKED_SHIFT 1
|
||||
#define SLJIT_MASKED_SHIFT32 1
|
||||
|
||||
#elif (defined SLJIT_CONFIG_RISCV && SLJIT_CONFIG_RISCV)
|
||||
|
||||
#define SLJIT_NUMBER_OF_REGISTERS 23
|
||||
#define SLJIT_NUMBER_OF_SAVED_REGISTERS 12
|
||||
#define SLJIT_NUMBER_OF_TEMPORARY_REGISTERS 5
|
||||
#define SLJIT_NUMBER_OF_FLOAT_REGISTERS 30
|
||||
#define SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS 12
|
||||
#define SLJIT_NUMBER_OF_TEMPORARY_FLOAT_REGISTERS 2
|
||||
#define SLJIT_SEPARATE_VECTOR_REGISTERS 1
|
||||
#define SLJIT_NUMBER_OF_VECTOR_REGISTERS 30
|
||||
#define SLJIT_NUMBER_OF_SAVED_VECTOR_REGISTERS 0
|
||||
#define SLJIT_NUMBER_OF_TEMPORARY_VECTOR_REGISTERS 2
|
||||
#define SLJIT_TMP_DEST_REG SLJIT_TMP_R1
|
||||
#define SLJIT_TMP_MEM_REG SLJIT_TMP_R1
|
||||
#define SLJIT_TMP_DEST_FREG SLJIT_TMP_FR0
|
||||
#define SLJIT_TMP_DEST_VREG SLJIT_TMP_VR0
|
||||
#define SLJIT_LOCALS_OFFSET_BASE 0
|
||||
#define SLJIT_MASKED_SHIFT 1
|
||||
#define SLJIT_MASKED_SHIFT32 1
|
||||
|
||||
#elif (defined SLJIT_CONFIG_S390X && SLJIT_CONFIG_S390X)
|
||||
|
||||
/*
|
||||
* https://refspecs.linuxbase.org/ELF/zSeries/lzsabi0_zSeries.html#STACKFRAME
|
||||
*
|
||||
* 160
|
||||
* .. FR6
|
||||
* .. FR4
|
||||
* .. FR2
|
||||
* 128 FR0
|
||||
* 120 R15 (used for SP)
|
||||
* 112 R14
|
||||
* 104 R13
|
||||
* 96 R12
|
||||
* ..
|
||||
* 48 R6
|
||||
* ..
|
||||
* 16 R2
|
||||
* 8 RESERVED
|
||||
* 0 SP
|
||||
*/
|
||||
#define SLJIT_S390X_DEFAULT_STACK_FRAME_SIZE 160
|
||||
|
||||
#define SLJIT_NUMBER_OF_REGISTERS 12
|
||||
#define SLJIT_NUMBER_OF_SAVED_REGISTERS 8
|
||||
#define SLJIT_NUMBER_OF_TEMPORARY_REGISTERS 3
|
||||
#define SLJIT_NUMBER_OF_FLOAT_REGISTERS 15
|
||||
#define SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS 8
|
||||
#define SLJIT_NUMBER_OF_TEMPORARY_FLOAT_REGISTERS 1
|
||||
#define SLJIT_TMP_DEST_REG SLJIT_TMP_R0
|
||||
#define SLJIT_TMP_MEM_REG SLJIT_TMP_R2
|
||||
#define SLJIT_TMP_DEST_FREG SLJIT_TMP_FR0
|
||||
#define SLJIT_LOCALS_OFFSET_BASE SLJIT_S390X_DEFAULT_STACK_FRAME_SIZE
|
||||
#define SLJIT_MASKED_SHIFT 1
|
||||
|
||||
#elif (defined SLJIT_CONFIG_LOONGARCH && SLJIT_CONFIG_LOONGARCH)
|
||||
|
||||
#define SLJIT_NUMBER_OF_REGISTERS 23
|
||||
#define SLJIT_NUMBER_OF_SAVED_REGISTERS 10
|
||||
#define SLJIT_NUMBER_OF_TEMPORARY_REGISTERS 5
|
||||
#define SLJIT_NUMBER_OF_FLOAT_REGISTERS 30
|
||||
#define SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS 12
|
||||
#define SLJIT_NUMBER_OF_TEMPORARY_FLOAT_REGISTERS 2
|
||||
#define SLJIT_TMP_DEST_REG SLJIT_TMP_R1
|
||||
#define SLJIT_TMP_MEM_REG SLJIT_TMP_R1
|
||||
#define SLJIT_TMP_DEST_FREG SLJIT_TMP_FR0
|
||||
#define SLJIT_LOCALS_OFFSET_BASE 0
|
||||
#define SLJIT_MASKED_SHIFT 1
|
||||
#define SLJIT_MASKED_SHIFT32 1
|
||||
|
||||
#elif (defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED)
|
||||
|
||||
/* Just to have something. */
|
||||
#define SLJIT_NUMBER_OF_REGISTERS 0
|
||||
#define SLJIT_NUMBER_OF_SAVED_REGISTERS 0
|
||||
#define SLJIT_NUMBER_OF_TEMPORARY_REGISTERS 0
|
||||
#define SLJIT_NUMBER_OF_FLOAT_REGISTERS 0
|
||||
#define SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS 0
|
||||
#define SLJIT_NUMBER_OF_TEMPORARY_FLOAT_REGISTERS 0
|
||||
#define SLJIT_TMP_DEST_REG 0
|
||||
#define SLJIT_TMP_MEM_REG 0
|
||||
#define SLJIT_TMP_DEST_FREG 0
|
||||
#define SLJIT_LOCALS_OFFSET_BASE 0
|
||||
|
||||
#endif
|
||||
|
||||
#if !(defined SLJIT_SEPARATE_VECTOR_REGISTERS && SLJIT_SEPARATE_VECTOR_REGISTERS)
|
||||
#define SLJIT_NUMBER_OF_VECTOR_REGISTERS (SLJIT_NUMBER_OF_FLOAT_REGISTERS)
|
||||
#define SLJIT_NUMBER_OF_SAVED_VECTOR_REGISTERS (SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS)
|
||||
#define SLJIT_NUMBER_OF_TEMPORARY_VECTOR_REGISTERS (SLJIT_NUMBER_OF_TEMPORARY_FLOAT_REGISTERS)
|
||||
#define SLJIT_TMP_DEST_VREG (SLJIT_TMP_DEST_FREG)
|
||||
#endif /* !SLJIT_SEPARATE_VECTOR_REGISTERS */
|
||||
|
||||
#define SLJIT_LOCALS_OFFSET (SLJIT_LOCALS_OFFSET_BASE)
|
||||
|
||||
#define SLJIT_NUMBER_OF_SCRATCH_REGISTERS \
|
||||
(SLJIT_NUMBER_OF_REGISTERS - SLJIT_NUMBER_OF_SAVED_REGISTERS)
|
||||
|
||||
#define SLJIT_NUMBER_OF_SCRATCH_FLOAT_REGISTERS \
|
||||
(SLJIT_NUMBER_OF_FLOAT_REGISTERS - SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS)
|
||||
|
||||
#define SLJIT_NUMBER_OF_SCRATCH_VECTOR_REGISTERS \
|
||||
(SLJIT_NUMBER_OF_VECTOR_REGISTERS - SLJIT_NUMBER_OF_SAVED_VECTOR_REGISTERS)
|
||||
|
||||
/**********************************/
|
||||
/* Temporary register management. */
|
||||
/**********************************/
|
||||
|
||||
#define SLJIT_TMP_REGISTER_BASE (SLJIT_NUMBER_OF_REGISTERS + 2)
|
||||
#define SLJIT_TMP_FREGISTER_BASE (SLJIT_NUMBER_OF_FLOAT_REGISTERS + 1)
|
||||
#define SLJIT_TMP_VREGISTER_BASE (SLJIT_NUMBER_OF_VECTOR_REGISTERS + 1)
|
||||
|
||||
/* WARNING: Accessing temporary registers is not recommended, because they
|
||||
are also used by the JIT compiler for various computations. Using them
|
||||
might have any side effects including incorrect operations and crashes,
|
||||
so use them at your own risk. The machine registers themselves might have
|
||||
limitations, e.g. the r0 register on s390x / ppc cannot be used as
|
||||
base address for memory operations. */
|
||||
|
||||
/* Temporary registers */
|
||||
#define SLJIT_TMP_R0 (SLJIT_TMP_REGISTER_BASE + 0)
|
||||
#define SLJIT_TMP_R1 (SLJIT_TMP_REGISTER_BASE + 1)
|
||||
#define SLJIT_TMP_R2 (SLJIT_TMP_REGISTER_BASE + 2)
|
||||
#define SLJIT_TMP_R3 (SLJIT_TMP_REGISTER_BASE + 3)
|
||||
#define SLJIT_TMP_R4 (SLJIT_TMP_REGISTER_BASE + 4)
|
||||
#define SLJIT_TMP_R5 (SLJIT_TMP_REGISTER_BASE + 5)
|
||||
#define SLJIT_TMP_R6 (SLJIT_TMP_REGISTER_BASE + 6)
|
||||
#define SLJIT_TMP_R7 (SLJIT_TMP_REGISTER_BASE + 7)
|
||||
#define SLJIT_TMP_R8 (SLJIT_TMP_REGISTER_BASE + 8)
|
||||
#define SLJIT_TMP_R9 (SLJIT_TMP_REGISTER_BASE + 9)
|
||||
#define SLJIT_TMP_R(i) (SLJIT_TMP_REGISTER_BASE + (i))
|
||||
|
||||
#define SLJIT_TMP_FR0 (SLJIT_TMP_FREGISTER_BASE + 0)
|
||||
#define SLJIT_TMP_FR1 (SLJIT_TMP_FREGISTER_BASE + 1)
|
||||
#define SLJIT_TMP_FR2 (SLJIT_TMP_FREGISTER_BASE + 2)
|
||||
#define SLJIT_TMP_FR3 (SLJIT_TMP_FREGISTER_BASE + 3)
|
||||
#define SLJIT_TMP_FR4 (SLJIT_TMP_FREGISTER_BASE + 4)
|
||||
#define SLJIT_TMP_FR5 (SLJIT_TMP_FREGISTER_BASE + 5)
|
||||
#define SLJIT_TMP_FR6 (SLJIT_TMP_FREGISTER_BASE + 6)
|
||||
#define SLJIT_TMP_FR7 (SLJIT_TMP_FREGISTER_BASE + 7)
|
||||
#define SLJIT_TMP_FR8 (SLJIT_TMP_FREGISTER_BASE + 8)
|
||||
#define SLJIT_TMP_FR9 (SLJIT_TMP_FREGISTER_BASE + 9)
|
||||
#define SLJIT_TMP_FR(i) (SLJIT_TMP_FREGISTER_BASE + (i))
|
||||
|
||||
#define SLJIT_TMP_VR0 (SLJIT_TMP_VREGISTER_BASE + 0)
|
||||
#define SLJIT_TMP_VR1 (SLJIT_TMP_VREGISTER_BASE + 1)
|
||||
#define SLJIT_TMP_VR2 (SLJIT_TMP_VREGISTER_BASE + 2)
|
||||
#define SLJIT_TMP_VR3 (SLJIT_TMP_VREGISTER_BASE + 3)
|
||||
#define SLJIT_TMP_VR4 (SLJIT_TMP_VREGISTER_BASE + 4)
|
||||
#define SLJIT_TMP_VR5 (SLJIT_TMP_VREGISTER_BASE + 5)
|
||||
#define SLJIT_TMP_VR6 (SLJIT_TMP_VREGISTER_BASE + 6)
|
||||
#define SLJIT_TMP_VR7 (SLJIT_TMP_VREGISTER_BASE + 7)
|
||||
#define SLJIT_TMP_VR8 (SLJIT_TMP_VREGISTER_BASE + 8)
|
||||
#define SLJIT_TMP_VR9 (SLJIT_TMP_VREGISTER_BASE + 9)
|
||||
#define SLJIT_TMP_VR(i) (SLJIT_TMP_VREGISTER_BASE + (i))
|
||||
|
||||
/********************************/
|
||||
/* CPU status flags management. */
|
||||
/********************************/
|
||||
|
||||
#if (defined SLJIT_CONFIG_ARM && SLJIT_CONFIG_ARM) \
|
||||
|| (defined SLJIT_CONFIG_PPC && SLJIT_CONFIG_PPC) \
|
||||
|| (defined SLJIT_CONFIG_MIPS && SLJIT_CONFIG_MIPS) \
|
||||
|| (defined SLJIT_CONFIG_RISCV && SLJIT_CONFIG_RISCV) \
|
||||
|| (defined SLJIT_CONFIG_S390X && SLJIT_CONFIG_S390X) \
|
||||
|| (defined SLJIT_CONFIG_LOONGARCH && SLJIT_CONFIG_LOONGARCH)
|
||||
#define SLJIT_HAS_STATUS_FLAGS_STATE 1
|
||||
#endif
|
||||
|
||||
/***************************************/
|
||||
/* Floating point register management. */
|
||||
/***************************************/
|
||||
|
||||
#if (defined SLJIT_CONFIG_ARM_32 && SLJIT_CONFIG_ARM_32) \
|
||||
|| (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
|
||||
#define SLJIT_F64_SECOND(reg) \
|
||||
((reg) + SLJIT_FS0 + SLJIT_NUMBER_OF_TEMPORARY_FLOAT_REGISTERS)
|
||||
#else /* !SLJIT_CONFIG_ARM_32 && !SLJIT_CONFIG_MIPS_32 */
|
||||
#define SLJIT_F64_SECOND(reg) \
|
||||
(reg)
|
||||
#endif /* SLJIT_CONFIG_ARM_32 || SLJIT_CONFIG_MIPS_32 */
|
||||
|
||||
/*************************************/
|
||||
/* Debug and verbose related macros. */
|
||||
/*************************************/
|
||||
|
||||
#if (defined SLJIT_DEBUG && SLJIT_DEBUG)
|
||||
|
||||
#if !defined(SLJIT_ASSERT) || !defined(SLJIT_UNREACHABLE)
|
||||
|
||||
/* SLJIT_HALT_PROCESS must halt the process. */
|
||||
#ifndef SLJIT_HALT_PROCESS
|
||||
#define SLJIT_HALT_PROCESS() \
|
||||
abort();
|
||||
#endif /* !SLJIT_HALT_PROCESS */
|
||||
|
||||
#endif /* !SLJIT_ASSERT || !SLJIT_UNREACHABLE */
|
||||
|
||||
/* Feel free to redefine these two macros. */
|
||||
#ifndef SLJIT_ASSERT
|
||||
|
||||
#define SLJIT_ASSERT(x) \
|
||||
do { \
|
||||
if (SLJIT_UNLIKELY(!(x))) { \
|
||||
printf("Assertion failed at " __FILE__ ":%d\n", __LINE__); \
|
||||
SLJIT_HALT_PROCESS(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#endif /* !SLJIT_ASSERT */
|
||||
|
||||
#ifndef SLJIT_UNREACHABLE
|
||||
|
||||
#define SLJIT_UNREACHABLE() \
|
||||
do { \
|
||||
printf("Should never been reached " __FILE__ ":%d\n", __LINE__); \
|
||||
SLJIT_HALT_PROCESS(); \
|
||||
} while (0)
|
||||
|
||||
#endif /* !SLJIT_UNREACHABLE */
|
||||
|
||||
#else /* (defined SLJIT_DEBUG && SLJIT_DEBUG) */
|
||||
|
||||
/* Forcing empty, but valid statements. */
|
||||
#undef SLJIT_ASSERT
|
||||
#undef SLJIT_UNREACHABLE
|
||||
|
||||
#define SLJIT_ASSERT(x) \
|
||||
do { } while (0)
|
||||
#define SLJIT_UNREACHABLE() \
|
||||
do { } while (0)
|
||||
|
||||
#endif /* (defined SLJIT_DEBUG && SLJIT_DEBUG) */
|
||||
|
||||
#ifndef SLJIT_COMPILE_ASSERT
|
||||
|
||||
#define SLJIT_COMPILE_ASSERT(x, description) \
|
||||
switch(0) { case 0: case ((x) ? 1 : 0): break; }
|
||||
|
||||
#endif /* !SLJIT_COMPILE_ASSERT */
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* SLJIT_CONFIG_INTERNAL_H_ */
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,472 +0,0 @@
|
||||
/*
|
||||
* Stack-less Just-In-Time compiler
|
||||
*
|
||||
* Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* mips 32-bit arch dependent functions. */
|
||||
|
||||
static sljit_s32 emit_copysign(struct sljit_compiler *compiler, sljit_s32 op,
|
||||
sljit_sw src1, sljit_sw src2, sljit_sw dst)
|
||||
{
|
||||
int is_32 = (op & SLJIT_32);
|
||||
sljit_ins mfhc = MFC1, mthc = MTC1;
|
||||
sljit_ins src1_r = FS(src1), src2_r = FS(src2), dst_r = FS(dst);
|
||||
|
||||
if (!is_32) {
|
||||
switch (cpu_feature_list & CPU_FEATURE_FR) {
|
||||
#if defined(SLJIT_MIPS_REV) && SLJIT_MIPS_REV >= 2
|
||||
case CPU_FEATURE_FR:
|
||||
mfhc = MFHC1;
|
||||
mthc = MTHC1;
|
||||
break;
|
||||
#endif /* SLJIT_MIPS_REV >= 2 */
|
||||
default:
|
||||
src1_r |= (1 << 11);
|
||||
src2_r |= (1 << 11);
|
||||
dst_r |= (1 << 11);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
FAIL_IF(push_inst(compiler, mfhc | T(TMP_REG1) | src1_r, DR(TMP_REG1)));
|
||||
FAIL_IF(push_inst(compiler, mfhc | T(TMP_REG2) | src2_r, DR(TMP_REG2)));
|
||||
if (!is_32 && src1 != dst)
|
||||
FAIL_IF(push_inst(compiler, MOV_fmt(FMT_S) | FS(src1) | FD(dst), MOVABLE_INS));
|
||||
#if !defined(SLJIT_MIPS_REV) || SLJIT_MIPS_REV <= 1
|
||||
else
|
||||
FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS));
|
||||
#endif /* MIPS III */
|
||||
FAIL_IF(push_inst(compiler, XOR | T(TMP_REG1) | D(TMP_REG2) | S(TMP_REG2), DR(TMP_REG2)));
|
||||
FAIL_IF(push_inst(compiler, SRL | T(TMP_REG2) | D(TMP_REG2) | SH_IMM(31), DR(TMP_REG2)));
|
||||
FAIL_IF(push_inst(compiler, SLL | T(TMP_REG2) | D(TMP_REG2) | SH_IMM(31), DR(TMP_REG2)));
|
||||
FAIL_IF(push_inst(compiler, XOR | T(TMP_REG2) | D(TMP_REG1) | S(TMP_REG1), DR(TMP_REG1)));
|
||||
FAIL_IF(push_inst(compiler, mthc | T(TMP_REG1) | dst_r, MOVABLE_INS));
|
||||
#if !defined(SLJIT_MIPS_REV) || SLJIT_MIPS_REV <= 1
|
||||
if (mthc == MTC1)
|
||||
return push_inst(compiler, NOP, UNMOVABLE_INS);
|
||||
#endif /* MIPS III */
|
||||
return SLJIT_SUCCESS;
|
||||
}
|
||||
|
||||
static sljit_s32 load_immediate(struct sljit_compiler *compiler, sljit_s32 dst_ar, sljit_sw imm)
|
||||
{
|
||||
if (!(imm & ~0xffff))
|
||||
return push_inst(compiler, ORI | SA(0) | TA(dst_ar) | IMM(imm), dst_ar);
|
||||
|
||||
if (imm < 0 && imm >= SIMM_MIN)
|
||||
return push_inst(compiler, ADDIU | SA(0) | TA(dst_ar) | IMM(imm), dst_ar);
|
||||
|
||||
FAIL_IF(push_inst(compiler, LUI | TA(dst_ar) | IMM(imm >> 16), dst_ar));
|
||||
return (imm & 0xffff) ? push_inst(compiler, ORI | SA(dst_ar) | TA(dst_ar) | IMM(imm), dst_ar) : SLJIT_SUCCESS;
|
||||
}
|
||||
|
||||
static SLJIT_INLINE sljit_s32 emit_const(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw init_value)
|
||||
{
|
||||
FAIL_IF(push_inst(compiler, LUI | T(dst) | IMM(init_value >> 16), DR(dst)));
|
||||
return push_inst(compiler, ORI | S(dst) | T(dst) | IMM(init_value), DR(dst));
|
||||
}
|
||||
|
||||
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fset64(struct sljit_compiler *compiler,
|
||||
sljit_s32 freg, sljit_f64 value)
|
||||
{
|
||||
union {
|
||||
struct {
|
||||
#if defined(SLJIT_LITTLE_ENDIAN) && SLJIT_LITTLE_ENDIAN
|
||||
sljit_s32 lo;
|
||||
sljit_s32 hi;
|
||||
#else /* !SLJIT_LITTLE_ENDIAN */
|
||||
sljit_s32 hi;
|
||||
sljit_s32 lo;
|
||||
#endif /* SLJIT_LITTLE_ENDIAN */
|
||||
} bin;
|
||||
sljit_f64 value;
|
||||
} u;
|
||||
|
||||
CHECK_ERROR();
|
||||
CHECK(check_sljit_emit_fset64(compiler, freg, value));
|
||||
|
||||
u.value = value;
|
||||
|
||||
if (u.bin.lo != 0)
|
||||
FAIL_IF(load_immediate(compiler, DR(TMP_REG1), u.bin.lo));
|
||||
if (u.bin.hi != 0)
|
||||
FAIL_IF(load_immediate(compiler, DR(TMP_REG2), u.bin.hi));
|
||||
|
||||
FAIL_IF(push_inst(compiler, MTC1 | (u.bin.lo != 0 ? T(TMP_REG1) : TA(0)) | FS(freg), MOVABLE_INS));
|
||||
switch (cpu_feature_list & CPU_FEATURE_FR) {
|
||||
#if defined(SLJIT_MIPS_REV) && SLJIT_MIPS_REV >= 2
|
||||
case CPU_FEATURE_FR:
|
||||
return push_inst(compiler, MTHC1 | (u.bin.hi != 0 ? T(TMP_REG2) : TA(0)) | FS(freg), MOVABLE_INS);
|
||||
#endif /* SLJIT_MIPS_REV >= 2 */
|
||||
default:
|
||||
FAIL_IF(push_inst(compiler, MTC1 | (u.bin.hi != 0 ? T(TMP_REG2) : TA(0)) | FS(freg) | (1 << 11), MOVABLE_INS));
|
||||
break;
|
||||
}
|
||||
#if !defined(SLJIT_MIPS_REV) || SLJIT_MIPS_REV <= 1
|
||||
FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS));
|
||||
#endif /* MIPS III */
|
||||
return SLJIT_SUCCESS;
|
||||
}
|
||||
|
||||
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fcopy(struct sljit_compiler *compiler, sljit_s32 op,
|
||||
sljit_s32 freg, sljit_s32 reg)
|
||||
{
|
||||
sljit_s32 reg2 = 0;
|
||||
sljit_ins inst = FS(freg);
|
||||
sljit_ins mthc = MTC1, mfhc = MFC1;
|
||||
int is_32 = (op & SLJIT_32);
|
||||
|
||||
CHECK_ERROR();
|
||||
CHECK(check_sljit_emit_fcopy(compiler, op, freg, reg));
|
||||
|
||||
op = GET_OPCODE(op);
|
||||
if (reg & REG_PAIR_MASK) {
|
||||
reg2 = REG_PAIR_SECOND(reg);
|
||||
reg = REG_PAIR_FIRST(reg);
|
||||
|
||||
inst |= T(reg2);
|
||||
|
||||
if (op == SLJIT_COPY_TO_F64)
|
||||
FAIL_IF(push_inst(compiler, MTC1 | inst, MOVABLE_INS));
|
||||
else
|
||||
FAIL_IF(push_inst(compiler, MFC1 | inst, DR(reg2)));
|
||||
|
||||
inst = FS(freg) | (1 << 11);
|
||||
#if defined(SLJIT_MIPS_REV) && SLJIT_MIPS_REV >= 2
|
||||
if (cpu_feature_list & CPU_FEATURE_FR) {
|
||||
mthc = MTHC1;
|
||||
mfhc = MFHC1;
|
||||
inst = FS(freg);
|
||||
}
|
||||
#endif /* SLJIT_MIPS_REV >= 2 */
|
||||
}
|
||||
|
||||
inst |= T(reg);
|
||||
if (!is_32 && !reg2) {
|
||||
switch (cpu_feature_list & CPU_FEATURE_FR) {
|
||||
#if defined(SLJIT_MIPS_REV) && SLJIT_MIPS_REV >= 2
|
||||
case CPU_FEATURE_FR:
|
||||
mthc = MTHC1;
|
||||
mfhc = MFHC1;
|
||||
break;
|
||||
#endif /* SLJIT_MIPS_REV >= 2 */
|
||||
default:
|
||||
inst |= (1 << 11);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (op == SLJIT_COPY_TO_F64)
|
||||
FAIL_IF(push_inst(compiler, mthc | inst, MOVABLE_INS));
|
||||
else
|
||||
FAIL_IF(push_inst(compiler, mfhc | inst, DR(reg)));
|
||||
|
||||
#if !defined(SLJIT_MIPS_REV) || SLJIT_MIPS_REV <= 1
|
||||
if (mthc == MTC1 || mfhc == MFC1)
|
||||
return push_inst(compiler, NOP, UNMOVABLE_INS);
|
||||
#endif /* MIPS III */
|
||||
return SLJIT_SUCCESS;
|
||||
}
|
||||
|
||||
SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_target, sljit_sw executable_offset)
|
||||
{
|
||||
sljit_ins *inst = (sljit_ins *)addr;
|
||||
SLJIT_UNUSED_ARG(executable_offset);
|
||||
|
||||
SLJIT_UPDATE_WX_FLAGS(inst, inst + 2, 0);
|
||||
SLJIT_ASSERT((inst[0] & 0xffe00000) == LUI && (inst[1] & 0xfc000000) == ORI);
|
||||
inst[0] = (inst[0] & 0xffff0000) | ((new_target >> 16) & 0xffff);
|
||||
inst[1] = (inst[1] & 0xffff0000) | (new_target & 0xffff);
|
||||
SLJIT_UPDATE_WX_FLAGS(inst, inst + 2, 1);
|
||||
inst = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset);
|
||||
SLJIT_CACHE_FLUSH(inst, inst + 2);
|
||||
}
|
||||
|
||||
SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_sw new_constant, sljit_sw executable_offset)
|
||||
{
|
||||
sljit_set_jump_addr(addr, (sljit_uw)new_constant, executable_offset);
|
||||
}
|
||||
|
||||
static sljit_s32 call_with_args(struct sljit_compiler *compiler, sljit_s32 arg_types, sljit_ins *ins_ptr, sljit_u32 *extra_space)
|
||||
{
|
||||
sljit_u32 is_tail_call = *extra_space & SLJIT_CALL_RETURN;
|
||||
sljit_u32 offset = 0;
|
||||
sljit_s32 float_arg_count = 0;
|
||||
sljit_s32 word_arg_count = 0;
|
||||
sljit_s32 types = 0;
|
||||
sljit_ins prev_ins = NOP;
|
||||
sljit_ins ins = NOP;
|
||||
sljit_u8 offsets[4];
|
||||
sljit_u8 *offsets_ptr = offsets;
|
||||
#if defined(SLJIT_LITTLE_ENDIAN) && SLJIT_LITTLE_ENDIAN
|
||||
sljit_ins f64_hi = TA(7), f64_lo = TA(6);
|
||||
#else
|
||||
sljit_ins f64_hi = TA(6), f64_lo = TA(7);
|
||||
#endif /* SLJIT_LITTLE_ENDIAN */
|
||||
|
||||
SLJIT_ASSERT(reg_map[TMP_REG2] == 4 && freg_map[TMP_FREG1] == 12);
|
||||
|
||||
arg_types >>= SLJIT_ARG_SHIFT;
|
||||
|
||||
/* See ABI description in sljit_emit_enter. */
|
||||
|
||||
while (arg_types) {
|
||||
types = (types << SLJIT_ARG_SHIFT) | (arg_types & SLJIT_ARG_MASK);
|
||||
*offsets_ptr = (sljit_u8)offset;
|
||||
|
||||
switch (arg_types & SLJIT_ARG_MASK) {
|
||||
case SLJIT_ARG_TYPE_F64:
|
||||
if (offset & 0x7) {
|
||||
offset += sizeof(sljit_sw);
|
||||
*offsets_ptr = (sljit_u8)offset;
|
||||
}
|
||||
|
||||
if (word_arg_count == 0 && float_arg_count <= 1)
|
||||
*offsets_ptr = (sljit_u8)(254 + float_arg_count);
|
||||
|
||||
offset += sizeof(sljit_f64);
|
||||
float_arg_count++;
|
||||
break;
|
||||
case SLJIT_ARG_TYPE_F32:
|
||||
if (word_arg_count == 0 && float_arg_count <= 1)
|
||||
*offsets_ptr = (sljit_u8)(254 + float_arg_count);
|
||||
|
||||
offset += sizeof(sljit_f32);
|
||||
float_arg_count++;
|
||||
break;
|
||||
default:
|
||||
offset += sizeof(sljit_sw);
|
||||
word_arg_count++;
|
||||
break;
|
||||
}
|
||||
|
||||
arg_types >>= SLJIT_ARG_SHIFT;
|
||||
offsets_ptr++;
|
||||
}
|
||||
|
||||
/* Stack is aligned to 16 bytes. */
|
||||
SLJIT_ASSERT(offset <= 8 * sizeof(sljit_sw));
|
||||
|
||||
if (offset > 4 * sizeof(sljit_sw) && (!is_tail_call || offset > compiler->args_size)) {
|
||||
if (is_tail_call) {
|
||||
offset = (offset + sizeof(sljit_sw) + 15) & ~(sljit_uw)0xf;
|
||||
FAIL_IF(emit_stack_frame_release(compiler, (sljit_s32)offset, &prev_ins));
|
||||
*extra_space = offset;
|
||||
} else {
|
||||
FAIL_IF(push_inst(compiler, ADDIU | S(SLJIT_SP) | T(SLJIT_SP) | IMM(-16), DR(SLJIT_SP)));
|
||||
*extra_space = 16;
|
||||
}
|
||||
} else {
|
||||
if (is_tail_call)
|
||||
FAIL_IF(emit_stack_frame_release(compiler, 0, &prev_ins));
|
||||
*extra_space = 0;
|
||||
}
|
||||
|
||||
while (types) {
|
||||
--offsets_ptr;
|
||||
|
||||
switch (types & SLJIT_ARG_MASK) {
|
||||
case SLJIT_ARG_TYPE_F64:
|
||||
if (*offsets_ptr < 4 * sizeof(sljit_sw)) {
|
||||
if (prev_ins != NOP)
|
||||
FAIL_IF(push_inst(compiler, prev_ins, MOVABLE_INS));
|
||||
|
||||
/* Must be preceded by at least one other argument,
|
||||
* and its starting offset must be 8 because of alignment. */
|
||||
SLJIT_ASSERT((*offsets_ptr >> 2) == 2);
|
||||
switch (cpu_feature_list & CPU_FEATURE_FR) {
|
||||
#if defined(SLJIT_MIPS_REV) && SLJIT_MIPS_REV >= 2
|
||||
case CPU_FEATURE_FR:
|
||||
prev_ins = MFHC1 | f64_hi | FS(float_arg_count);
|
||||
break;
|
||||
#endif /* SLJIT_MIPS_REV >= 2 */
|
||||
default:
|
||||
prev_ins = MFC1 | f64_hi | FS(float_arg_count) | (1 << 11);
|
||||
break;
|
||||
}
|
||||
ins = MFC1 | f64_lo | FS(float_arg_count);
|
||||
} else if (*offsets_ptr < 254)
|
||||
ins = SDC1 | S(SLJIT_SP) | FT(float_arg_count) | IMM(*offsets_ptr);
|
||||
else if (*offsets_ptr == 254)
|
||||
ins = MOV_fmt(FMT_D) | FS(SLJIT_FR0) | FD(TMP_FREG1);
|
||||
|
||||
float_arg_count--;
|
||||
break;
|
||||
case SLJIT_ARG_TYPE_F32:
|
||||
if (*offsets_ptr < 4 * sizeof (sljit_sw))
|
||||
ins = MFC1 | TA(4 + (*offsets_ptr >> 2)) | FS(float_arg_count);
|
||||
else if (*offsets_ptr < 254)
|
||||
ins = SWC1 | S(SLJIT_SP) | FT(float_arg_count) | IMM(*offsets_ptr);
|
||||
else if (*offsets_ptr == 254)
|
||||
ins = MOV_fmt(FMT_S) | FS(SLJIT_FR0) | FD(TMP_FREG1);
|
||||
|
||||
float_arg_count--;
|
||||
break;
|
||||
default:
|
||||
if (*offsets_ptr >= 4 * sizeof (sljit_sw))
|
||||
ins = SW | S(SLJIT_SP) | T(word_arg_count) | IMM(*offsets_ptr);
|
||||
else if ((*offsets_ptr >> 2) != word_arg_count - 1)
|
||||
ins = ADDU | S(word_arg_count) | TA(0) | DA(4 + (*offsets_ptr >> 2));
|
||||
else if (*offsets_ptr == 0)
|
||||
ins = ADDU | S(SLJIT_R0) | TA(0) | DA(4);
|
||||
|
||||
word_arg_count--;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ins != NOP) {
|
||||
if (prev_ins != NOP)
|
||||
FAIL_IF(push_inst(compiler, prev_ins, MOVABLE_INS));
|
||||
prev_ins = ins;
|
||||
ins = NOP;
|
||||
}
|
||||
|
||||
types >>= SLJIT_ARG_SHIFT;
|
||||
}
|
||||
|
||||
*ins_ptr = prev_ins;
|
||||
|
||||
return SLJIT_SUCCESS;
|
||||
}
|
||||
|
||||
SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_call(struct sljit_compiler *compiler, sljit_s32 type,
|
||||
sljit_s32 arg_types)
|
||||
{
|
||||
struct sljit_jump *jump;
|
||||
sljit_u32 extra_space = 0;
|
||||
sljit_ins ins = NOP;
|
||||
|
||||
CHECK_ERROR_PTR();
|
||||
CHECK_PTR(check_sljit_emit_call(compiler, type, arg_types));
|
||||
|
||||
jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump));
|
||||
PTR_FAIL_IF(!jump);
|
||||
set_jump(jump, compiler, type & SLJIT_REWRITABLE_JUMP);
|
||||
|
||||
if ((type & 0xff) != SLJIT_CALL_REG_ARG) {
|
||||
extra_space = (sljit_u32)type;
|
||||
PTR_FAIL_IF(call_with_args(compiler, arg_types, &ins, &extra_space));
|
||||
} else if (type & SLJIT_CALL_RETURN)
|
||||
PTR_FAIL_IF(emit_stack_frame_release(compiler, 0, &ins));
|
||||
|
||||
SLJIT_ASSERT(DR(PIC_ADDR_REG) == 25);
|
||||
|
||||
if (ins == NOP && compiler->delay_slot != UNMOVABLE_INS)
|
||||
jump->flags |= IS_MOVABLE;
|
||||
|
||||
if (!(type & SLJIT_CALL_RETURN) || extra_space > 0) {
|
||||
jump->flags |= IS_JAL;
|
||||
|
||||
if ((type & 0xff) != SLJIT_CALL_REG_ARG)
|
||||
jump->flags |= IS_CALL;
|
||||
|
||||
PTR_FAIL_IF(push_inst(compiler, JALR | S(PIC_ADDR_REG) | DA(RETURN_ADDR_REG), UNMOVABLE_INS));
|
||||
} else
|
||||
PTR_FAIL_IF(push_inst(compiler, JR | S(PIC_ADDR_REG), UNMOVABLE_INS));
|
||||
|
||||
jump->addr = compiler->size;
|
||||
PTR_FAIL_IF(push_inst(compiler, ins, UNMOVABLE_INS));
|
||||
|
||||
/* Maximum number of instructions required for generating a constant. */
|
||||
compiler->size += 2;
|
||||
|
||||
if (extra_space == 0)
|
||||
return jump;
|
||||
|
||||
if (type & SLJIT_CALL_RETURN)
|
||||
PTR_FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, RETURN_ADDR_REG,
|
||||
SLJIT_MEM1(SLJIT_SP), (sljit_sw)(extra_space - sizeof(sljit_sw))));
|
||||
|
||||
if (type & SLJIT_CALL_RETURN)
|
||||
PTR_FAIL_IF(push_inst(compiler, JR | SA(RETURN_ADDR_REG), UNMOVABLE_INS));
|
||||
|
||||
PTR_FAIL_IF(push_inst(compiler, ADDIU | S(SLJIT_SP) | T(SLJIT_SP) | IMM(extra_space),
|
||||
(type & SLJIT_CALL_RETURN) ? UNMOVABLE_INS : DR(SLJIT_SP)));
|
||||
return jump;
|
||||
}
|
||||
|
||||
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_icall(struct sljit_compiler *compiler, sljit_s32 type,
|
||||
sljit_s32 arg_types,
|
||||
sljit_s32 src, sljit_sw srcw)
|
||||
{
|
||||
sljit_u32 extra_space = (sljit_u32)type;
|
||||
sljit_ins ins;
|
||||
|
||||
CHECK_ERROR();
|
||||
CHECK(check_sljit_emit_icall(compiler, type, arg_types, src, srcw));
|
||||
|
||||
if (src & SLJIT_MEM) {
|
||||
ADJUST_LOCAL_OFFSET(src, srcw);
|
||||
FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, DR(PIC_ADDR_REG), src, srcw));
|
||||
src = PIC_ADDR_REG;
|
||||
srcw = 0;
|
||||
}
|
||||
|
||||
if ((type & 0xff) == SLJIT_CALL_REG_ARG) {
|
||||
if (type & SLJIT_CALL_RETURN) {
|
||||
if (src >= SLJIT_FIRST_SAVED_REG && src <= (SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options))) {
|
||||
FAIL_IF(push_inst(compiler, ADDU | S(src) | TA(0) | D(PIC_ADDR_REG), DR(PIC_ADDR_REG)));
|
||||
src = PIC_ADDR_REG;
|
||||
srcw = 0;
|
||||
}
|
||||
|
||||
FAIL_IF(emit_stack_frame_release(compiler, 0, &ins));
|
||||
|
||||
if (ins != NOP)
|
||||
FAIL_IF(push_inst(compiler, ins, MOVABLE_INS));
|
||||
}
|
||||
|
||||
SLJIT_SKIP_CHECKS(compiler);
|
||||
return sljit_emit_ijump(compiler, type, src, srcw);
|
||||
}
|
||||
|
||||
SLJIT_ASSERT(DR(PIC_ADDR_REG) == 25);
|
||||
|
||||
if (src == SLJIT_IMM)
|
||||
FAIL_IF(load_immediate(compiler, DR(PIC_ADDR_REG), srcw));
|
||||
else if (src != PIC_ADDR_REG)
|
||||
FAIL_IF(push_inst(compiler, ADDU | S(src) | TA(0) | D(PIC_ADDR_REG), DR(PIC_ADDR_REG)));
|
||||
|
||||
FAIL_IF(call_with_args(compiler, arg_types, &ins, &extra_space));
|
||||
|
||||
/* Register input. */
|
||||
if (!(type & SLJIT_CALL_RETURN) || extra_space > 0)
|
||||
FAIL_IF(push_inst(compiler, JALR | S(PIC_ADDR_REG) | DA(RETURN_ADDR_REG), UNMOVABLE_INS));
|
||||
else
|
||||
FAIL_IF(push_inst(compiler, JR | S(PIC_ADDR_REG), UNMOVABLE_INS));
|
||||
FAIL_IF(push_inst(compiler, ins, UNMOVABLE_INS));
|
||||
|
||||
if (extra_space == 0)
|
||||
return SLJIT_SUCCESS;
|
||||
|
||||
if (type & SLJIT_CALL_RETURN)
|
||||
FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, RETURN_ADDR_REG,
|
||||
SLJIT_MEM1(SLJIT_SP), (sljit_sw)(extra_space - sizeof(sljit_sw))));
|
||||
|
||||
if (type & SLJIT_CALL_RETURN)
|
||||
FAIL_IF(push_inst(compiler, JR | SA(RETURN_ADDR_REG), UNMOVABLE_INS));
|
||||
|
||||
return push_inst(compiler, ADDIU | S(SLJIT_SP) | T(SLJIT_SP) | IMM(extra_space),
|
||||
(type & SLJIT_CALL_RETURN) ? UNMOVABLE_INS : DR(SLJIT_SP));
|
||||
}
|
||||
@@ -1,387 +0,0 @@
|
||||
/*
|
||||
* Stack-less Just-In-Time compiler
|
||||
*
|
||||
* Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* mips 64-bit arch dependent functions. */
|
||||
|
||||
static sljit_s32 emit_copysign(struct sljit_compiler *compiler, sljit_s32 op,
|
||||
sljit_s32 src1, sljit_s32 src2, sljit_s32 dst)
|
||||
{
|
||||
FAIL_IF(push_inst(compiler, SELECT_OP(DMFC1, MFC1) | T(TMP_REG1) | FS(src1), DR(TMP_REG1)));
|
||||
FAIL_IF(push_inst(compiler, SELECT_OP(DMFC1, MFC1) | T(TMP_REG2) | FS(src2), DR(TMP_REG2)));
|
||||
FAIL_IF(push_inst(compiler, XOR | S(TMP_REG2) | T(TMP_REG1) | D(TMP_REG2), DR(TMP_REG2)));
|
||||
FAIL_IF(push_inst(compiler, SELECT_OP(DSRL32, SRL) | T(TMP_REG2) | D(TMP_REG2) | SH_IMM(31), DR(TMP_REG2)));
|
||||
FAIL_IF(push_inst(compiler, SELECT_OP(DSLL32, SLL) | T(TMP_REG2) | D(TMP_REG2) | SH_IMM(31), DR(TMP_REG2)));
|
||||
FAIL_IF(push_inst(compiler, XOR | S(TMP_REG1) | T(TMP_REG2) | D(TMP_REG1), DR(TMP_REG1)));
|
||||
FAIL_IF(push_inst(compiler, SELECT_OP(DMTC1, MTC1) | T(TMP_REG1) | FS(dst), MOVABLE_INS));
|
||||
#if !defined(SLJIT_MIPS_REV) || SLJIT_MIPS_REV <= 1
|
||||
if (!(op & SLJIT_32))
|
||||
return push_inst(compiler, NOP, UNMOVABLE_INS);
|
||||
#endif /* MIPS III */
|
||||
return SLJIT_SUCCESS;
|
||||
}
|
||||
|
||||
static sljit_s32 load_immediate(struct sljit_compiler *compiler, sljit_s32 dst_ar, sljit_sw imm)
|
||||
{
|
||||
sljit_s32 shift = 32;
|
||||
sljit_s32 shift2;
|
||||
sljit_s32 inv = 0;
|
||||
sljit_ins ins;
|
||||
sljit_uw uimm;
|
||||
|
||||
if (!(imm & ~0xffff))
|
||||
return push_inst(compiler, ORI | SA(0) | TA(dst_ar) | IMM(imm), dst_ar);
|
||||
|
||||
if (imm < 0 && imm >= SIMM_MIN)
|
||||
return push_inst(compiler, ADDIU | SA(0) | TA(dst_ar) | IMM(imm), dst_ar);
|
||||
|
||||
if (imm <= 0x7fffffffl && imm >= -0x80000000l) {
|
||||
FAIL_IF(push_inst(compiler, LUI | TA(dst_ar) | IMM(imm >> 16), dst_ar));
|
||||
return (imm & 0xffff) ? push_inst(compiler, ORI | SA(dst_ar) | TA(dst_ar) | IMM(imm), dst_ar) : SLJIT_SUCCESS;
|
||||
}
|
||||
|
||||
/* Zero extended number. */
|
||||
uimm = (sljit_uw)imm;
|
||||
if (imm < 0) {
|
||||
uimm = ~(sljit_uw)imm;
|
||||
inv = 1;
|
||||
}
|
||||
|
||||
while (!(uimm & 0xff00000000000000l)) {
|
||||
shift -= 8;
|
||||
uimm <<= 8;
|
||||
}
|
||||
|
||||
if (!(uimm & 0xf000000000000000l)) {
|
||||
shift -= 4;
|
||||
uimm <<= 4;
|
||||
}
|
||||
|
||||
if (!(uimm & 0xc000000000000000l)) {
|
||||
shift -= 2;
|
||||
uimm <<= 2;
|
||||
}
|
||||
|
||||
if ((sljit_sw)uimm < 0) {
|
||||
uimm >>= 1;
|
||||
shift += 1;
|
||||
}
|
||||
SLJIT_ASSERT(((uimm & 0xc000000000000000l) == 0x4000000000000000l) && (shift > 0) && (shift <= 32));
|
||||
|
||||
if (inv)
|
||||
uimm = ~uimm;
|
||||
|
||||
FAIL_IF(push_inst(compiler, LUI | TA(dst_ar) | IMM(uimm >> 48), dst_ar));
|
||||
if (uimm & 0x0000ffff00000000l)
|
||||
FAIL_IF(push_inst(compiler, ORI | SA(dst_ar) | TA(dst_ar) | IMM(uimm >> 32), dst_ar));
|
||||
|
||||
imm &= (1l << shift) - 1;
|
||||
if (!(imm & ~0xffff)) {
|
||||
ins = (shift == 32) ? DSLL32 : DSLL;
|
||||
if (shift < 32)
|
||||
ins |= SH_IMM(shift);
|
||||
FAIL_IF(push_inst(compiler, ins | TA(dst_ar) | DA(dst_ar), dst_ar));
|
||||
return !(imm & 0xffff) ? SLJIT_SUCCESS : push_inst(compiler, ORI | SA(dst_ar) | TA(dst_ar) | IMM(imm), dst_ar);
|
||||
}
|
||||
|
||||
/* Double shifts needs to be performed. */
|
||||
uimm <<= 32;
|
||||
shift2 = shift - 16;
|
||||
|
||||
while (!(uimm & 0xf000000000000000l)) {
|
||||
shift2 -= 4;
|
||||
uimm <<= 4;
|
||||
}
|
||||
|
||||
if (!(uimm & 0xc000000000000000l)) {
|
||||
shift2 -= 2;
|
||||
uimm <<= 2;
|
||||
}
|
||||
|
||||
if (!(uimm & 0x8000000000000000l)) {
|
||||
shift2--;
|
||||
uimm <<= 1;
|
||||
}
|
||||
|
||||
SLJIT_ASSERT((uimm & 0x8000000000000000l) && (shift2 > 0) && (shift2 <= 16));
|
||||
|
||||
FAIL_IF(push_inst(compiler, DSLL | TA(dst_ar) | DA(dst_ar) | SH_IMM(shift - shift2), dst_ar));
|
||||
FAIL_IF(push_inst(compiler, ORI | SA(dst_ar) | TA(dst_ar) | IMM(uimm >> 48), dst_ar));
|
||||
FAIL_IF(push_inst(compiler, DSLL | TA(dst_ar) | DA(dst_ar) | SH_IMM(shift2), dst_ar));
|
||||
|
||||
imm &= (1l << shift2) - 1;
|
||||
return !(imm & 0xffff) ? SLJIT_SUCCESS : push_inst(compiler, ORI | SA(dst_ar) | TA(dst_ar) | IMM(imm), dst_ar);
|
||||
}
|
||||
|
||||
static SLJIT_INLINE sljit_s32 emit_const(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw init_value)
|
||||
{
|
||||
FAIL_IF(push_inst(compiler, LUI | T(dst) | IMM(init_value >> 48), DR(dst)));
|
||||
FAIL_IF(push_inst(compiler, ORI | S(dst) | T(dst) | IMM(init_value >> 32), DR(dst)));
|
||||
FAIL_IF(push_inst(compiler, DSLL | T(dst) | D(dst) | SH_IMM(16), DR(dst)));
|
||||
FAIL_IF(push_inst(compiler, ORI | S(dst) | T(dst) | IMM(init_value >> 16), DR(dst)));
|
||||
FAIL_IF(push_inst(compiler, DSLL | T(dst) | D(dst) | SH_IMM(16), DR(dst)));
|
||||
return push_inst(compiler, ORI | S(dst) | T(dst) | IMM(init_value), DR(dst));
|
||||
}
|
||||
|
||||
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fset64(struct sljit_compiler *compiler,
|
||||
sljit_s32 freg, sljit_f64 value)
|
||||
{
|
||||
union {
|
||||
sljit_sw imm;
|
||||
sljit_f64 value;
|
||||
} u;
|
||||
|
||||
CHECK_ERROR();
|
||||
CHECK(check_sljit_emit_fset64(compiler, freg, value));
|
||||
|
||||
u.value = value;
|
||||
|
||||
if (u.imm == 0) {
|
||||
FAIL_IF(push_inst(compiler, DMTC1 | TA(0) | FS(freg), MOVABLE_INS));
|
||||
#if !defined(SLJIT_MIPS_REV) || SLJIT_MIPS_REV <= 1
|
||||
FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS));
|
||||
#endif /* MIPS III */
|
||||
return SLJIT_SUCCESS;
|
||||
}
|
||||
|
||||
FAIL_IF(load_immediate(compiler, DR(TMP_REG1), u.imm));
|
||||
FAIL_IF(push_inst(compiler, DMTC1 | T(TMP_REG1) | FS(freg), MOVABLE_INS));
|
||||
#if !defined(SLJIT_MIPS_REV) || SLJIT_MIPS_REV <= 1
|
||||
FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS));
|
||||
#endif /* MIPS III */
|
||||
return SLJIT_SUCCESS;
|
||||
}
|
||||
|
||||
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fcopy(struct sljit_compiler *compiler, sljit_s32 op,
|
||||
sljit_s32 freg, sljit_s32 reg)
|
||||
{
|
||||
sljit_ins inst;
|
||||
|
||||
CHECK_ERROR();
|
||||
CHECK(check_sljit_emit_fcopy(compiler, op, freg, reg));
|
||||
|
||||
inst = T(reg) | FS(freg);
|
||||
|
||||
if (GET_OPCODE(op) == SLJIT_COPY_TO_F64)
|
||||
FAIL_IF(push_inst(compiler, SELECT_OP(DMTC1, MTC1) | inst, MOVABLE_INS));
|
||||
else
|
||||
FAIL_IF(push_inst(compiler, SELECT_OP(DMFC1, MFC1) | inst, DR(reg)));
|
||||
|
||||
#if !defined(SLJIT_MIPS_REV) || SLJIT_MIPS_REV <= 1
|
||||
if (!(op & SLJIT_32))
|
||||
return push_inst(compiler, NOP, UNMOVABLE_INS);
|
||||
#endif /* MIPS III */
|
||||
return SLJIT_SUCCESS;
|
||||
}
|
||||
|
||||
SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_target, sljit_sw executable_offset)
|
||||
{
|
||||
sljit_ins *inst = (sljit_ins *)addr;
|
||||
SLJIT_UNUSED_ARG(executable_offset);
|
||||
|
||||
SLJIT_UPDATE_WX_FLAGS(inst, inst + 6, 0);
|
||||
inst[0] = (inst[0] & 0xffff0000) | ((sljit_ins)(new_target >> 48) & 0xffff);
|
||||
inst[1] = (inst[1] & 0xffff0000) | ((sljit_ins)(new_target >> 32) & 0xffff);
|
||||
inst[3] = (inst[3] & 0xffff0000) | ((sljit_ins)(new_target >> 16) & 0xffff);
|
||||
inst[5] = (inst[5] & 0xffff0000) | ((sljit_ins)new_target & 0xffff);
|
||||
SLJIT_UPDATE_WX_FLAGS(inst, inst + 6, 1);
|
||||
inst = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset);
|
||||
SLJIT_CACHE_FLUSH(inst, inst + 6);
|
||||
}
|
||||
|
||||
SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_sw new_constant, sljit_sw executable_offset)
|
||||
{
|
||||
sljit_set_jump_addr(addr, (sljit_uw)new_constant, executable_offset);
|
||||
}
|
||||
|
||||
static sljit_s32 call_with_args(struct sljit_compiler *compiler, sljit_s32 arg_types, sljit_ins *ins_ptr)
|
||||
{
|
||||
sljit_s32 arg_count = 0;
|
||||
sljit_s32 word_arg_count = 0;
|
||||
sljit_s32 float_arg_count = 0;
|
||||
sljit_s32 types = 0;
|
||||
sljit_ins prev_ins = *ins_ptr;
|
||||
sljit_ins ins = NOP;
|
||||
|
||||
SLJIT_ASSERT(reg_map[TMP_REG2] == 4 && freg_map[TMP_FREG1] == 12);
|
||||
|
||||
arg_types >>= SLJIT_ARG_SHIFT;
|
||||
|
||||
while (arg_types) {
|
||||
types = (types << SLJIT_ARG_SHIFT) | (arg_types & SLJIT_ARG_MASK);
|
||||
|
||||
switch (arg_types & SLJIT_ARG_MASK) {
|
||||
case SLJIT_ARG_TYPE_F64:
|
||||
case SLJIT_ARG_TYPE_F32:
|
||||
arg_count++;
|
||||
float_arg_count++;
|
||||
break;
|
||||
default:
|
||||
arg_count++;
|
||||
word_arg_count++;
|
||||
break;
|
||||
}
|
||||
|
||||
arg_types >>= SLJIT_ARG_SHIFT;
|
||||
}
|
||||
|
||||
while (types) {
|
||||
switch (types & SLJIT_ARG_MASK) {
|
||||
case SLJIT_ARG_TYPE_F64:
|
||||
if (arg_count != float_arg_count)
|
||||
ins = MOV_fmt(FMT_D) | FS(float_arg_count) | FD(arg_count);
|
||||
else if (arg_count == 1)
|
||||
ins = MOV_fmt(FMT_D) | FS(SLJIT_FR0) | FD(TMP_FREG1);
|
||||
arg_count--;
|
||||
float_arg_count--;
|
||||
break;
|
||||
case SLJIT_ARG_TYPE_F32:
|
||||
if (arg_count != float_arg_count)
|
||||
ins = MOV_fmt(FMT_S) | FS(float_arg_count) | FD(arg_count);
|
||||
else if (arg_count == 1)
|
||||
ins = MOV_fmt(FMT_S) | FS(SLJIT_FR0) | FD(TMP_FREG1);
|
||||
arg_count--;
|
||||
float_arg_count--;
|
||||
break;
|
||||
default:
|
||||
if (arg_count != word_arg_count)
|
||||
ins = DADDU | S(word_arg_count) | TA(0) | D(arg_count);
|
||||
else if (arg_count == 1)
|
||||
ins = DADDU | S(SLJIT_R0) | TA(0) | DA(4);
|
||||
arg_count--;
|
||||
word_arg_count--;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ins != NOP) {
|
||||
if (prev_ins != NOP)
|
||||
FAIL_IF(push_inst(compiler, prev_ins, MOVABLE_INS));
|
||||
prev_ins = ins;
|
||||
ins = NOP;
|
||||
}
|
||||
|
||||
types >>= SLJIT_ARG_SHIFT;
|
||||
}
|
||||
|
||||
*ins_ptr = prev_ins;
|
||||
|
||||
return SLJIT_SUCCESS;
|
||||
}
|
||||
|
||||
SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_call(struct sljit_compiler *compiler, sljit_s32 type,
|
||||
sljit_s32 arg_types)
|
||||
{
|
||||
struct sljit_jump *jump;
|
||||
sljit_ins ins = NOP;
|
||||
|
||||
CHECK_ERROR_PTR();
|
||||
CHECK_PTR(check_sljit_emit_call(compiler, type, arg_types));
|
||||
|
||||
jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump));
|
||||
PTR_FAIL_IF(!jump);
|
||||
set_jump(jump, compiler, type & SLJIT_REWRITABLE_JUMP);
|
||||
|
||||
if (type & SLJIT_CALL_RETURN)
|
||||
PTR_FAIL_IF(emit_stack_frame_release(compiler, 0, &ins));
|
||||
|
||||
if ((type & 0xff) != SLJIT_CALL_REG_ARG)
|
||||
PTR_FAIL_IF(call_with_args(compiler, arg_types, &ins));
|
||||
|
||||
SLJIT_ASSERT(DR(PIC_ADDR_REG) == 25);
|
||||
|
||||
if (ins == NOP && compiler->delay_slot != UNMOVABLE_INS)
|
||||
jump->flags |= IS_MOVABLE;
|
||||
|
||||
if (!(type & SLJIT_CALL_RETURN)) {
|
||||
jump->flags |= IS_JAL;
|
||||
|
||||
if ((type & 0xff) != SLJIT_CALL_REG_ARG)
|
||||
jump->flags |= IS_CALL;
|
||||
|
||||
PTR_FAIL_IF(push_inst(compiler, JALR | S(PIC_ADDR_REG) | DA(RETURN_ADDR_REG), UNMOVABLE_INS));
|
||||
} else
|
||||
PTR_FAIL_IF(push_inst(compiler, JR | S(PIC_ADDR_REG), UNMOVABLE_INS));
|
||||
|
||||
jump->addr = compiler->size;
|
||||
PTR_FAIL_IF(push_inst(compiler, ins, UNMOVABLE_INS));
|
||||
|
||||
/* Maximum number of instructions required for generating a constant. */
|
||||
compiler->size += 6;
|
||||
return jump;
|
||||
}
|
||||
|
||||
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_icall(struct sljit_compiler *compiler, sljit_s32 type,
|
||||
sljit_s32 arg_types,
|
||||
sljit_s32 src, sljit_sw srcw)
|
||||
{
|
||||
sljit_ins ins = NOP;
|
||||
|
||||
CHECK_ERROR();
|
||||
CHECK(check_sljit_emit_icall(compiler, type, arg_types, src, srcw));
|
||||
|
||||
if (src & SLJIT_MEM) {
|
||||
ADJUST_LOCAL_OFFSET(src, srcw);
|
||||
FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, DR(PIC_ADDR_REG), src, srcw));
|
||||
src = PIC_ADDR_REG;
|
||||
srcw = 0;
|
||||
}
|
||||
|
||||
if ((type & 0xff) == SLJIT_CALL_REG_ARG) {
|
||||
if (type & SLJIT_CALL_RETURN) {
|
||||
if (src >= SLJIT_FIRST_SAVED_REG && src <= (SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options))) {
|
||||
FAIL_IF(push_inst(compiler, DADDU | S(src) | TA(0) | D(PIC_ADDR_REG), DR(PIC_ADDR_REG)));
|
||||
src = PIC_ADDR_REG;
|
||||
srcw = 0;
|
||||
}
|
||||
|
||||
FAIL_IF(emit_stack_frame_release(compiler, 0, &ins));
|
||||
|
||||
if (ins != NOP)
|
||||
FAIL_IF(push_inst(compiler, ins, MOVABLE_INS));
|
||||
}
|
||||
|
||||
SLJIT_SKIP_CHECKS(compiler);
|
||||
return sljit_emit_ijump(compiler, type, src, srcw);
|
||||
}
|
||||
|
||||
SLJIT_ASSERT(DR(PIC_ADDR_REG) == 25 && PIC_ADDR_REG == TMP_REG1);
|
||||
|
||||
if (src == SLJIT_IMM)
|
||||
FAIL_IF(load_immediate(compiler, DR(PIC_ADDR_REG), srcw));
|
||||
else if (src != PIC_ADDR_REG)
|
||||
FAIL_IF(push_inst(compiler, DADDU | S(src) | TA(0) | D(PIC_ADDR_REG), DR(PIC_ADDR_REG)));
|
||||
|
||||
if (type & SLJIT_CALL_RETURN)
|
||||
FAIL_IF(emit_stack_frame_release(compiler, 0, &ins));
|
||||
|
||||
FAIL_IF(call_with_args(compiler, arg_types, &ins));
|
||||
|
||||
/* Register input. */
|
||||
if (!(type & SLJIT_CALL_RETURN))
|
||||
FAIL_IF(push_inst(compiler, JALR | S(PIC_ADDR_REG) | DA(RETURN_ADDR_REG), UNMOVABLE_INS));
|
||||
else
|
||||
FAIL_IF(push_inst(compiler, JR | S(PIC_ADDR_REG), UNMOVABLE_INS));
|
||||
return push_inst(compiler, ins, UNMOVABLE_INS);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,485 +0,0 @@
|
||||
/*
|
||||
* Stack-less Just-In-Time compiler
|
||||
*
|
||||
* Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* ppc 32-bit arch dependent functions. */
|
||||
|
||||
static sljit_s32 load_immediate(struct sljit_compiler *compiler, sljit_s32 reg, sljit_sw imm)
|
||||
{
|
||||
if (imm <= SIMM_MAX && imm >= SIMM_MIN)
|
||||
return push_inst(compiler, ADDI | D(reg) | A(0) | IMM(imm));
|
||||
|
||||
if (!(imm & ~0xffff))
|
||||
return push_inst(compiler, ORI | S(TMP_ZERO) | A(reg) | IMM(imm));
|
||||
|
||||
FAIL_IF(push_inst(compiler, ADDIS | D(reg) | A(0) | IMM(imm >> 16)));
|
||||
return (imm & 0xffff) ? push_inst(compiler, ORI | S(reg) | A(reg) | IMM(imm)) : SLJIT_SUCCESS;
|
||||
}
|
||||
|
||||
/* Simplified mnemonics: clrlwi. */
|
||||
#define INS_CLEAR_LEFT(dst, src, from) \
|
||||
(RLWINM | S(src) | A(dst) | RLWI_MBE(from, 31))
|
||||
|
||||
static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 flags,
|
||||
sljit_s32 dst, sljit_s32 src1, sljit_s32 src2)
|
||||
{
|
||||
sljit_u32 imm;
|
||||
|
||||
switch (op) {
|
||||
case SLJIT_MOV:
|
||||
case SLJIT_MOV_U32:
|
||||
case SLJIT_MOV_S32:
|
||||
case SLJIT_MOV_P:
|
||||
SLJIT_ASSERT(src1 == TMP_REG1);
|
||||
if (dst != src2)
|
||||
return push_inst(compiler, OR | S(src2) | A(dst) | B(src2));
|
||||
return SLJIT_SUCCESS;
|
||||
|
||||
case SLJIT_MOV_U8:
|
||||
case SLJIT_MOV_S8:
|
||||
SLJIT_ASSERT(src1 == TMP_REG1);
|
||||
if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) {
|
||||
if (op == SLJIT_MOV_S8)
|
||||
return push_inst(compiler, EXTSB | S(src2) | A(dst));
|
||||
return push_inst(compiler, INS_CLEAR_LEFT(dst, src2, 24));
|
||||
}
|
||||
else if ((flags & REG_DEST) && op == SLJIT_MOV_S8)
|
||||
return push_inst(compiler, EXTSB | S(src2) | A(dst));
|
||||
else {
|
||||
SLJIT_ASSERT(dst == src2);
|
||||
}
|
||||
return SLJIT_SUCCESS;
|
||||
|
||||
case SLJIT_MOV_U16:
|
||||
case SLJIT_MOV_S16:
|
||||
SLJIT_ASSERT(src1 == TMP_REG1);
|
||||
if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) {
|
||||
if (op == SLJIT_MOV_S16)
|
||||
return push_inst(compiler, EXTSH | S(src2) | A(dst));
|
||||
return push_inst(compiler, INS_CLEAR_LEFT(dst, src2, 16));
|
||||
}
|
||||
else {
|
||||
SLJIT_ASSERT(dst == src2);
|
||||
}
|
||||
return SLJIT_SUCCESS;
|
||||
|
||||
case SLJIT_CLZ:
|
||||
SLJIT_ASSERT(src1 == TMP_REG1);
|
||||
return push_inst(compiler, CNTLZW | S(src2) | A(dst));
|
||||
|
||||
case SLJIT_CTZ:
|
||||
SLJIT_ASSERT(src1 == TMP_REG1);
|
||||
FAIL_IF(push_inst(compiler, NEG | D(TMP_REG1) | A(src2)));
|
||||
FAIL_IF(push_inst(compiler, AND | S(src2) | A(dst) | B(TMP_REG1)));
|
||||
FAIL_IF(push_inst(compiler, CNTLZW | S(dst) | A(dst)));
|
||||
FAIL_IF(push_inst(compiler, ADDI | D(TMP_REG1) | A(dst) | IMM(-32)));
|
||||
/* The highest bits are set, if dst < 32, zero otherwise. */
|
||||
FAIL_IF(push_inst(compiler, SRWI(27) | S(TMP_REG1) | A(TMP_REG1)));
|
||||
return push_inst(compiler, XOR | S(dst) | A(dst) | B(TMP_REG1));
|
||||
|
||||
case SLJIT_ADD:
|
||||
if (flags & ALT_FORM1) {
|
||||
/* Setting XER SO is not enough, CR SO is also needed. */
|
||||
return push_inst(compiler, ADD | OE(ALT_SET_FLAGS) | RC(ALT_SET_FLAGS) | D(dst) | A(src1) | B(src2));
|
||||
}
|
||||
|
||||
if (flags & ALT_FORM2) {
|
||||
/* Flags does not set: BIN_IMM_EXTS unnecessary. */
|
||||
SLJIT_ASSERT(src2 == TMP_REG2);
|
||||
|
||||
if (flags & ALT_FORM3)
|
||||
return push_inst(compiler, ADDIS | D(dst) | A(src1) | compiler->imm);
|
||||
|
||||
imm = compiler->imm;
|
||||
|
||||
if (flags & ALT_FORM4) {
|
||||
FAIL_IF(push_inst(compiler, ADDIS | D(dst) | A(src1) | (((imm >> 16) & 0xffff) + ((imm >> 15) & 0x1))));
|
||||
src1 = dst;
|
||||
}
|
||||
|
||||
return push_inst(compiler, ADDI | D(dst) | A(src1) | (imm & 0xffff));
|
||||
}
|
||||
if (flags & ALT_FORM3) {
|
||||
SLJIT_ASSERT(src2 == TMP_REG2);
|
||||
return push_inst(compiler, ADDIC | D(dst) | A(src1) | compiler->imm);
|
||||
}
|
||||
SLJIT_ASSERT(!(flags & ALT_FORM4));
|
||||
if (!(flags & ALT_SET_FLAGS))
|
||||
return push_inst(compiler, ADD | D(dst) | A(src1) | B(src2));
|
||||
if (flags & ALT_FORM5)
|
||||
return push_inst(compiler, ADDC | RC(ALT_SET_FLAGS) | D(dst) | A(src1) | B(src2));
|
||||
return push_inst(compiler, ADD | RC(flags) | D(dst) | A(src1) | B(src2));
|
||||
|
||||
case SLJIT_ADDC:
|
||||
return push_inst(compiler, ADDE | D(dst) | A(src1) | B(src2));
|
||||
|
||||
case SLJIT_SUB:
|
||||
if (flags & ALT_FORM1) {
|
||||
if (flags & ALT_FORM2) {
|
||||
FAIL_IF(push_inst(compiler, CMPLI | CRD(0) | A(src1) | compiler->imm));
|
||||
if (!(flags & ALT_FORM3))
|
||||
return SLJIT_SUCCESS;
|
||||
return push_inst(compiler, ADDI | D(dst) | A(src1) | (-compiler->imm & 0xffff));
|
||||
}
|
||||
FAIL_IF(push_inst(compiler, CMPL | CRD(0) | A(src1) | B(src2)));
|
||||
if (!(flags & ALT_FORM3))
|
||||
return SLJIT_SUCCESS;
|
||||
return push_inst(compiler, SUBF | D(dst) | A(src2) | B(src1));
|
||||
}
|
||||
|
||||
if (flags & ALT_FORM2) {
|
||||
if (flags & ALT_FORM3) {
|
||||
FAIL_IF(push_inst(compiler, CMPI | CRD(0) | A(src1) | compiler->imm));
|
||||
if (!(flags & ALT_FORM4))
|
||||
return SLJIT_SUCCESS;
|
||||
return push_inst(compiler, ADDI | D(dst) | A(src1) | (-compiler->imm & 0xffff));
|
||||
}
|
||||
FAIL_IF(push_inst(compiler, CMP | CRD(0) | A(src1) | B(src2)));
|
||||
if (!(flags & ALT_FORM4))
|
||||
return SLJIT_SUCCESS;
|
||||
return push_inst(compiler, SUBF | D(dst) | A(src2) | B(src1));
|
||||
}
|
||||
|
||||
if (flags & ALT_FORM3) {
|
||||
/* Setting XER SO is not enough, CR SO is also needed. */
|
||||
if (src1 != TMP_ZERO)
|
||||
return push_inst(compiler, SUBF | OE(ALT_SET_FLAGS) | RC(ALT_SET_FLAGS) | D(dst) | A(src2) | B(src1));
|
||||
return push_inst(compiler, NEG | OE(ALT_SET_FLAGS) | RC(ALT_SET_FLAGS) | D(dst) | A(src2));
|
||||
}
|
||||
|
||||
if (flags & ALT_FORM4) {
|
||||
/* Flags does not set: BIN_IMM_EXTS unnecessary. */
|
||||
SLJIT_ASSERT(src2 == TMP_REG2);
|
||||
return push_inst(compiler, SUBFIC | D(dst) | A(src1) | compiler->imm);
|
||||
}
|
||||
|
||||
if (!(flags & ALT_SET_FLAGS)) {
|
||||
SLJIT_ASSERT(src1 != TMP_ZERO);
|
||||
return push_inst(compiler, SUBF | D(dst) | A(src2) | B(src1));
|
||||
}
|
||||
|
||||
if (flags & ALT_FORM5)
|
||||
return push_inst(compiler, SUBFC | RC(ALT_SET_FLAGS) | D(dst) | A(src2) | B(src1));
|
||||
|
||||
if (src1 != TMP_ZERO)
|
||||
return push_inst(compiler, SUBF | RC(ALT_SET_FLAGS) | D(dst) | A(src2) | B(src1));
|
||||
return push_inst(compiler, NEG | RC(ALT_SET_FLAGS) | D(dst) | A(src2));
|
||||
|
||||
case SLJIT_SUBC:
|
||||
return push_inst(compiler, SUBFE | D(dst) | A(src2) | B(src1));
|
||||
|
||||
case SLJIT_MUL:
|
||||
if (flags & ALT_FORM1) {
|
||||
SLJIT_ASSERT(src2 == TMP_REG2);
|
||||
return push_inst(compiler, MULLI | D(dst) | A(src1) | compiler->imm);
|
||||
}
|
||||
return push_inst(compiler, MULLW | OE(flags) | RC(flags) | D(dst) | A(src2) | B(src1));
|
||||
|
||||
case SLJIT_AND:
|
||||
if (flags & ALT_FORM1) {
|
||||
SLJIT_ASSERT(src2 == TMP_REG2);
|
||||
return push_inst(compiler, ANDI | S(src1) | A(dst) | compiler->imm);
|
||||
}
|
||||
if (flags & ALT_FORM2) {
|
||||
SLJIT_ASSERT(src2 == TMP_REG2);
|
||||
return push_inst(compiler, ANDIS | S(src1) | A(dst) | compiler->imm);
|
||||
}
|
||||
return push_inst(compiler, AND | RC(flags) | S(src1) | A(dst) | B(src2));
|
||||
|
||||
case SLJIT_OR:
|
||||
if (flags & ALT_FORM1) {
|
||||
SLJIT_ASSERT(src2 == TMP_REG2);
|
||||
return push_inst(compiler, ORI | S(src1) | A(dst) | compiler->imm);
|
||||
}
|
||||
if (flags & ALT_FORM2) {
|
||||
SLJIT_ASSERT(src2 == TMP_REG2);
|
||||
return push_inst(compiler, ORIS | S(src1) | A(dst) | compiler->imm);
|
||||
}
|
||||
if (flags & ALT_FORM3) {
|
||||
SLJIT_ASSERT(src2 == TMP_REG2);
|
||||
imm = compiler->imm;
|
||||
|
||||
FAIL_IF(push_inst(compiler, ORI | S(src1) | A(dst) | IMM(imm)));
|
||||
return push_inst(compiler, ORIS | S(dst) | A(dst) | IMM(imm >> 16));
|
||||
}
|
||||
return push_inst(compiler, OR | RC(flags) | S(src1) | A(dst) | B(src2));
|
||||
|
||||
case SLJIT_XOR:
|
||||
if (flags & ALT_FORM1) {
|
||||
SLJIT_ASSERT(src2 == TMP_REG2);
|
||||
return push_inst(compiler, XORI | S(src1) | A(dst) | compiler->imm);
|
||||
}
|
||||
if (flags & ALT_FORM2) {
|
||||
SLJIT_ASSERT(src2 == TMP_REG2);
|
||||
return push_inst(compiler, XORIS | S(src1) | A(dst) | compiler->imm);
|
||||
}
|
||||
if (flags & ALT_FORM3) {
|
||||
SLJIT_ASSERT(src2 == TMP_REG2);
|
||||
imm = compiler->imm;
|
||||
|
||||
FAIL_IF(push_inst(compiler, XORI | S(src1) | A(dst) | IMM(imm)));
|
||||
return push_inst(compiler, XORIS | S(dst) | A(dst) | IMM(imm >> 16));
|
||||
}
|
||||
if (flags & ALT_FORM4) {
|
||||
SLJIT_ASSERT(src1 == TMP_REG1);
|
||||
return push_inst(compiler, NOR | RC(flags) | S(src2) | A(dst) | B(src2));
|
||||
}
|
||||
return push_inst(compiler, XOR | RC(flags) | S(src1) | A(dst) | B(src2));
|
||||
|
||||
case SLJIT_SHL:
|
||||
case SLJIT_MSHL:
|
||||
if (flags & ALT_FORM1) {
|
||||
SLJIT_ASSERT(src2 == TMP_REG2);
|
||||
imm = compiler->imm & 0x1f;
|
||||
return push_inst(compiler, SLWI(imm) | RC(flags) | S(src1) | A(dst));
|
||||
}
|
||||
|
||||
if (op == SLJIT_MSHL) {
|
||||
FAIL_IF(push_inst(compiler, ANDI | S(src2) | A(TMP_REG2) | 0x1f));
|
||||
src2 = TMP_REG2;
|
||||
}
|
||||
|
||||
return push_inst(compiler, SLW | RC(flags) | S(src1) | A(dst) | B(src2));
|
||||
|
||||
case SLJIT_LSHR:
|
||||
case SLJIT_MLSHR:
|
||||
if (flags & ALT_FORM1) {
|
||||
SLJIT_ASSERT(src2 == TMP_REG2);
|
||||
imm = compiler->imm & 0x1f;
|
||||
/* Since imm can be 0, SRWI() cannot be used. */
|
||||
return push_inst(compiler, RLWINM | RC(flags) | S(src1) | A(dst) | RLWI_SH((32 - imm) & 0x1f) | RLWI_MBE(imm, 31));
|
||||
}
|
||||
|
||||
if (op == SLJIT_MLSHR) {
|
||||
FAIL_IF(push_inst(compiler, ANDI | S(src2) | A(TMP_REG2) | 0x1f));
|
||||
src2 = TMP_REG2;
|
||||
}
|
||||
|
||||
return push_inst(compiler, SRW | RC(flags) | S(src1) | A(dst) | B(src2));
|
||||
|
||||
case SLJIT_ASHR:
|
||||
case SLJIT_MASHR:
|
||||
if (flags & ALT_FORM1) {
|
||||
SLJIT_ASSERT(src2 == TMP_REG2);
|
||||
imm = compiler->imm & 0x1f;
|
||||
return push_inst(compiler, SRAWI | RC(flags) | S(src1) | A(dst) | (imm << 11));
|
||||
}
|
||||
|
||||
if (op == SLJIT_MASHR) {
|
||||
FAIL_IF(push_inst(compiler, ANDI | S(src2) | A(TMP_REG2) | 0x1f));
|
||||
src2 = TMP_REG2;
|
||||
}
|
||||
|
||||
return push_inst(compiler, SRAW | RC(flags) | S(src1) | A(dst) | B(src2));
|
||||
|
||||
case SLJIT_ROTL:
|
||||
case SLJIT_ROTR:
|
||||
if (flags & ALT_FORM1) {
|
||||
SLJIT_ASSERT(src2 == TMP_REG2);
|
||||
imm = compiler->imm;
|
||||
|
||||
if (op == SLJIT_ROTR)
|
||||
imm = (sljit_u32)(-(sljit_s32)imm);
|
||||
|
||||
imm &= 0x1f;
|
||||
return push_inst(compiler, RLWINM | S(src1) | A(dst) | RLWI_SH(imm) | RLWI_MBE(0, 31));
|
||||
}
|
||||
|
||||
if (op == SLJIT_ROTR) {
|
||||
FAIL_IF(push_inst(compiler, SUBFIC | D(TMP_REG2) | A(src2) | 0));
|
||||
src2 = TMP_REG2;
|
||||
}
|
||||
|
||||
return push_inst(compiler, RLWNM | S(src1) | A(dst) | B(src2) | RLWI_MBE(0, 31));
|
||||
}
|
||||
|
||||
SLJIT_UNREACHABLE();
|
||||
return SLJIT_SUCCESS;
|
||||
}
|
||||
|
||||
static SLJIT_INLINE sljit_s32 emit_const(struct sljit_compiler *compiler, sljit_s32 reg, sljit_sw init_value)
|
||||
{
|
||||
FAIL_IF(push_inst(compiler, ADDIS | D(reg) | A(0) | IMM(init_value >> 16)));
|
||||
return push_inst(compiler, ORI | S(reg) | A(reg) | IMM(init_value));
|
||||
}
|
||||
|
||||
static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_f64_from_sw(struct sljit_compiler *compiler, sljit_s32 op,
|
||||
sljit_s32 dst, sljit_sw dstw,
|
||||
sljit_s32 src, sljit_sw srcw)
|
||||
{
|
||||
sljit_s32 dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1;
|
||||
sljit_s32 invert_sign = 1;
|
||||
|
||||
if (src == SLJIT_IMM) {
|
||||
FAIL_IF(load_immediate(compiler, TMP_REG1, srcw ^ (sljit_sw)0x80000000));
|
||||
src = TMP_REG1;
|
||||
invert_sign = 0;
|
||||
} else if (!FAST_IS_REG(src)) {
|
||||
FAIL_IF(emit_op_mem(compiler, WORD_DATA | SIGNED_DATA | LOAD_DATA, TMP_REG1, src, srcw, TMP_REG1));
|
||||
src = TMP_REG1;
|
||||
}
|
||||
|
||||
/* First, a special double precision floating point value is constructed:
|
||||
(2^53 + (src xor (2^31)))
|
||||
The upper 32 bits of this number is a constant, and the lower 32 bits
|
||||
is simply the value of the source argument. The xor 2^31 operation adds
|
||||
0x80000000 to the source argument, which moves it into the 0 - 0xffffffff
|
||||
range. Finally we substract 2^53 + 2^31 to get the converted value. */
|
||||
FAIL_IF(push_inst(compiler, ADDIS | D(TMP_REG2) | A(0) | 0x4330));
|
||||
if (invert_sign)
|
||||
FAIL_IF(push_inst(compiler, XORIS | S(src) | A(TMP_REG1) | 0x8000));
|
||||
FAIL_IF(push_inst(compiler, STW | S(TMP_REG2) | A(SLJIT_SP) | TMP_MEM_OFFSET_HI));
|
||||
FAIL_IF(push_inst(compiler, STW | S(TMP_REG1) | A(SLJIT_SP) | TMP_MEM_OFFSET_LO));
|
||||
FAIL_IF(push_inst(compiler, ADDIS | D(TMP_REG1) | A(0) | 0x8000));
|
||||
FAIL_IF(push_inst(compiler, LFD | FS(TMP_FREG1) | A(SLJIT_SP) | TMP_MEM_OFFSET));
|
||||
FAIL_IF(push_inst(compiler, STW | S(TMP_REG1) | A(SLJIT_SP) | TMP_MEM_OFFSET_LO));
|
||||
FAIL_IF(push_inst(compiler, LFD | FS(TMP_FREG2) | A(SLJIT_SP) | TMP_MEM_OFFSET));
|
||||
|
||||
FAIL_IF(push_inst(compiler, FSUB | FD(dst_r) | FA(TMP_FREG1) | FB(TMP_FREG2)));
|
||||
|
||||
if (op & SLJIT_32)
|
||||
FAIL_IF(push_inst(compiler, FRSP | FD(dst_r) | FB(dst_r)));
|
||||
|
||||
if (dst & SLJIT_MEM)
|
||||
return emit_op_mem(compiler, FLOAT_DATA(op), TMP_FREG1, dst, dstw, TMP_REG1);
|
||||
return SLJIT_SUCCESS;
|
||||
}
|
||||
|
||||
static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_f64_from_uw(struct sljit_compiler *compiler, sljit_s32 op,
|
||||
sljit_s32 dst, sljit_sw dstw,
|
||||
sljit_s32 src, sljit_sw srcw)
|
||||
{
|
||||
sljit_s32 dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1;
|
||||
|
||||
if (src == SLJIT_IMM) {
|
||||
FAIL_IF(load_immediate(compiler, TMP_REG1, srcw));
|
||||
src = TMP_REG1;
|
||||
} else if (!FAST_IS_REG(src)) {
|
||||
FAIL_IF(emit_op_mem(compiler, WORD_DATA | SIGNED_DATA | LOAD_DATA, TMP_REG1, src, srcw, TMP_REG1));
|
||||
src = TMP_REG1;
|
||||
}
|
||||
|
||||
/* First, a special double precision floating point value is constructed:
|
||||
(2^53 + src)
|
||||
The upper 32 bits of this number is a constant, and the lower 32 bits
|
||||
is simply the value of the source argument. Finally we substract 2^53
|
||||
to get the converted value. */
|
||||
FAIL_IF(push_inst(compiler, ADDIS | D(TMP_REG2) | A(0) | 0x4330));
|
||||
FAIL_IF(push_inst(compiler, STW | S(src) | A(SLJIT_SP) | TMP_MEM_OFFSET_LO));
|
||||
FAIL_IF(push_inst(compiler, STW | S(TMP_REG2) | A(SLJIT_SP) | TMP_MEM_OFFSET_HI));
|
||||
|
||||
FAIL_IF(push_inst(compiler, LFD | FS(TMP_FREG1) | A(SLJIT_SP) | TMP_MEM_OFFSET));
|
||||
FAIL_IF(push_inst(compiler, STW | S(TMP_ZERO) | A(SLJIT_SP) | TMP_MEM_OFFSET_LO));
|
||||
FAIL_IF(push_inst(compiler, LFD | FS(TMP_FREG2) | A(SLJIT_SP) | TMP_MEM_OFFSET));
|
||||
|
||||
FAIL_IF(push_inst(compiler, FSUB | FD(dst_r) | FA(TMP_FREG1) | FB(TMP_FREG2)));
|
||||
|
||||
if (op & SLJIT_32)
|
||||
FAIL_IF(push_inst(compiler, FRSP | FD(dst_r) | FB(dst_r)));
|
||||
|
||||
if (dst & SLJIT_MEM)
|
||||
return emit_op_mem(compiler, FLOAT_DATA(op), TMP_FREG1, dst, dstw, TMP_REG1);
|
||||
return SLJIT_SUCCESS;
|
||||
}
|
||||
|
||||
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fset64(struct sljit_compiler *compiler,
|
||||
sljit_s32 freg, sljit_f64 value)
|
||||
{
|
||||
union {
|
||||
sljit_s32 imm[2];
|
||||
sljit_f64 value;
|
||||
} u;
|
||||
|
||||
CHECK_ERROR();
|
||||
CHECK(check_sljit_emit_fset64(compiler, freg, value));
|
||||
|
||||
u.value = value;
|
||||
|
||||
if (u.imm[0] != 0)
|
||||
FAIL_IF(load_immediate(compiler, TMP_REG1, u.imm[0]));
|
||||
if (u.imm[1] != 0)
|
||||
FAIL_IF(load_immediate(compiler, TMP_REG2, u.imm[1]));
|
||||
|
||||
/* Saved in the same endianness. */
|
||||
FAIL_IF(push_inst(compiler, STW | S(u.imm[0] != 0 ? TMP_REG1 : TMP_ZERO) | A(SLJIT_SP) | TMP_MEM_OFFSET));
|
||||
FAIL_IF(push_inst(compiler, STW | S(u.imm[1] != 0 ? TMP_REG2 : TMP_ZERO) | A(SLJIT_SP) | (TMP_MEM_OFFSET + sizeof(sljit_s32))));
|
||||
return push_inst(compiler, LFD | FS(freg) | A(SLJIT_SP) | TMP_MEM_OFFSET);
|
||||
}
|
||||
|
||||
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fcopy(struct sljit_compiler *compiler, sljit_s32 op,
|
||||
sljit_s32 freg, sljit_s32 reg)
|
||||
{
|
||||
sljit_s32 reg2 = 0;
|
||||
|
||||
CHECK_ERROR();
|
||||
CHECK(check_sljit_emit_fcopy(compiler, op, freg, reg));
|
||||
|
||||
if (op & SLJIT_32) {
|
||||
if (op == SLJIT_COPY32_TO_F32) {
|
||||
FAIL_IF(push_inst(compiler, STW | S(reg) | A(SLJIT_SP) | TMP_MEM_OFFSET));
|
||||
return push_inst(compiler, LFS | FS(freg) | A(SLJIT_SP) | TMP_MEM_OFFSET);
|
||||
}
|
||||
|
||||
FAIL_IF(push_inst(compiler, STFS | FS(freg) | A(SLJIT_SP) | TMP_MEM_OFFSET));
|
||||
return push_inst(compiler, LWZ | S(reg) | A(SLJIT_SP) | TMP_MEM_OFFSET);
|
||||
}
|
||||
|
||||
if (reg & REG_PAIR_MASK) {
|
||||
reg2 = REG_PAIR_SECOND(reg);
|
||||
reg = REG_PAIR_FIRST(reg);
|
||||
}
|
||||
|
||||
if (op == SLJIT_COPY_TO_F64) {
|
||||
FAIL_IF(push_inst(compiler, STW | S(reg) | A(SLJIT_SP) | TMP_MEM_OFFSET_HI));
|
||||
|
||||
if (reg2 != 0)
|
||||
FAIL_IF(push_inst(compiler, STW | S(reg2) | A(SLJIT_SP) | TMP_MEM_OFFSET_LO));
|
||||
else
|
||||
FAIL_IF(push_inst(compiler, STFD | FS(freg) | A(SLJIT_SP) | TMP_MEM_OFFSET_LO));
|
||||
|
||||
return push_inst(compiler, LFD | FS(freg) | A(SLJIT_SP) | TMP_MEM_OFFSET);
|
||||
}
|
||||
|
||||
FAIL_IF(push_inst(compiler, STFD | FS(freg) | A(SLJIT_SP) | TMP_MEM_OFFSET));
|
||||
|
||||
if (reg2 != 0)
|
||||
FAIL_IF(push_inst(compiler, LWZ | S(reg2) | A(SLJIT_SP) | TMP_MEM_OFFSET_LO));
|
||||
|
||||
return push_inst(compiler, LWZ | S(reg) | A(SLJIT_SP) | TMP_MEM_OFFSET_HI);
|
||||
}
|
||||
|
||||
SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_target, sljit_sw executable_offset)
|
||||
{
|
||||
sljit_ins *inst = (sljit_ins *)addr;
|
||||
SLJIT_UNUSED_ARG(executable_offset);
|
||||
|
||||
SLJIT_UPDATE_WX_FLAGS(inst, inst + 2, 0);
|
||||
SLJIT_ASSERT((inst[0] & 0xfc1f0000) == ADDIS && (inst[1] & 0xfc000000) == ORI);
|
||||
inst[0] = (inst[0] & 0xffff0000) | ((new_target >> 16) & 0xffff);
|
||||
inst[1] = (inst[1] & 0xffff0000) | (new_target & 0xffff);
|
||||
SLJIT_UPDATE_WX_FLAGS(inst, inst + 2, 1);
|
||||
inst = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset);
|
||||
SLJIT_CACHE_FLUSH(inst, inst + 2);
|
||||
}
|
||||
@@ -1,719 +0,0 @@
|
||||
/*
|
||||
* Stack-less Just-In-Time compiler
|
||||
*
|
||||
* Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* ppc 64-bit arch dependent functions. */
|
||||
|
||||
#if defined(__GNUC__) || (defined(__IBM_GCC_ASM) && __IBM_GCC_ASM)
|
||||
#define ASM_SLJIT_CLZ(src, dst) \
|
||||
__asm__ volatile ( "cntlzd %0, %1" : "=r"(dst) : "r"(src) )
|
||||
#elif defined(__xlc__)
|
||||
#error "Please enable GCC syntax for inline assembly statements"
|
||||
#else
|
||||
#error "Must implement count leading zeroes"
|
||||
#endif
|
||||
|
||||
/* Computes SLDI(63 - shift). */
|
||||
#define PUSH_SLDI_NEG(reg, shift) \
|
||||
push_inst(compiler, RLDICR | S(reg) | A(reg) | RLDI_SH(63 - shift) | RLDI_ME(shift))
|
||||
|
||||
static sljit_s32 load_immediate(struct sljit_compiler *compiler, sljit_s32 reg, sljit_sw imm)
|
||||
{
|
||||
sljit_uw tmp;
|
||||
sljit_uw shift;
|
||||
sljit_uw tmp2;
|
||||
sljit_uw shift2;
|
||||
|
||||
if (imm <= SIMM_MAX && imm >= SIMM_MIN)
|
||||
return push_inst(compiler, ADDI | D(reg) | A(0) | IMM(imm));
|
||||
|
||||
if (((sljit_uw)imm >> 16) == 0)
|
||||
return push_inst(compiler, ORI | S(TMP_ZERO) | A(reg) | IMM(imm));
|
||||
|
||||
if (imm <= 0x7fffffffl && imm >= -0x80000000l) {
|
||||
FAIL_IF(push_inst(compiler, ADDIS | D(reg) | A(0) | IMM(imm >> 16)));
|
||||
return (imm & 0xffff) ? push_inst(compiler, ORI | S(reg) | A(reg) | IMM(imm)) : SLJIT_SUCCESS;
|
||||
}
|
||||
|
||||
if (((sljit_uw)imm >> 32) == 0) {
|
||||
FAIL_IF(push_inst(compiler, ORIS | S(TMP_ZERO) | A(reg) | IMM(imm >> 16)));
|
||||
return (imm & 0xffff) ? push_inst(compiler, ORI | S(reg) | A(reg) | IMM(imm)) : SLJIT_SUCCESS;
|
||||
}
|
||||
|
||||
/* Count leading zeroes. */
|
||||
tmp = (sljit_uw)((imm >= 0) ? imm : ~imm);
|
||||
ASM_SLJIT_CLZ(tmp, shift);
|
||||
SLJIT_ASSERT(shift > 0);
|
||||
shift--;
|
||||
tmp = ((sljit_uw)imm << shift);
|
||||
|
||||
if ((tmp & ~0xffff000000000000ul) == 0) {
|
||||
FAIL_IF(push_inst(compiler, ADDI | D(reg) | A(0) | (sljit_ins)(tmp >> 48)));
|
||||
shift += 15;
|
||||
return PUSH_SLDI_NEG(reg, shift);
|
||||
}
|
||||
|
||||
if ((tmp & ~0xffffffff00000000ul) == 0) {
|
||||
FAIL_IF(push_inst(compiler, ADDIS | D(reg) | A(0) | (sljit_ins)(tmp >> 48)));
|
||||
FAIL_IF(push_inst(compiler, ORI | S(reg) | A(reg) | IMM(tmp >> 32)));
|
||||
shift += 31;
|
||||
return PUSH_SLDI_NEG(reg, shift);
|
||||
}
|
||||
|
||||
/* Cut out the 16 bit from immediate. */
|
||||
shift += 15;
|
||||
tmp2 = (sljit_uw)imm & (((sljit_uw)1 << (63 - shift)) - 1);
|
||||
|
||||
if (tmp2 <= 0xffff) {
|
||||
FAIL_IF(push_inst(compiler, ADDI | D(reg) | A(0) | (sljit_ins)(tmp >> 48)));
|
||||
FAIL_IF(PUSH_SLDI_NEG(reg, shift));
|
||||
return push_inst(compiler, ORI | S(reg) | A(reg) | (sljit_ins)tmp2);
|
||||
}
|
||||
|
||||
if (tmp2 <= 0xffffffff) {
|
||||
FAIL_IF(push_inst(compiler, ADDI | D(reg) | A(0) | IMM(tmp >> 48)));
|
||||
FAIL_IF(PUSH_SLDI_NEG(reg, shift));
|
||||
FAIL_IF(push_inst(compiler, ORIS | S(reg) | A(reg) | (sljit_ins)(tmp2 >> 16)));
|
||||
return (imm & 0xffff) ? push_inst(compiler, ORI | S(reg) | A(reg) | IMM(tmp2)) : SLJIT_SUCCESS;
|
||||
}
|
||||
|
||||
ASM_SLJIT_CLZ(tmp2, shift2);
|
||||
tmp2 <<= shift2;
|
||||
|
||||
if ((tmp2 & ~0xffff000000000000ul) == 0) {
|
||||
FAIL_IF(push_inst(compiler, ADDI | D(reg) | A(0) | (sljit_ins)(tmp >> 48)));
|
||||
shift2 += 15;
|
||||
shift += (63 - shift2);
|
||||
FAIL_IF(PUSH_SLDI_NEG(reg, shift));
|
||||
FAIL_IF(push_inst(compiler, ORI | S(reg) | A(reg) | (sljit_ins)(tmp2 >> 48)));
|
||||
return PUSH_SLDI_NEG(reg, shift2);
|
||||
}
|
||||
|
||||
/* The general version. */
|
||||
FAIL_IF(push_inst(compiler, ADDIS | D(reg) | A(0) | (sljit_ins)((sljit_uw)imm >> 48)));
|
||||
FAIL_IF(push_inst(compiler, ORI | S(reg) | A(reg) | IMM(imm >> 32)));
|
||||
FAIL_IF(PUSH_SLDI_NEG(reg, 31));
|
||||
FAIL_IF(push_inst(compiler, ORIS | S(reg) | A(reg) | IMM(imm >> 16)));
|
||||
return push_inst(compiler, ORI | S(reg) | A(reg) | IMM(imm));
|
||||
}
|
||||
|
||||
#undef PUSH_SLDI_NEG
|
||||
|
||||
#define CLRLDI(dst, src, n) \
|
||||
(RLDICL | S(src) | A(dst) | RLDI_SH(0) | RLDI_MB(n))
|
||||
|
||||
/* Sign extension for integer operations. */
|
||||
#define UN_EXTS() \
|
||||
if ((flags & (ALT_SIGN_EXT | REG2_SOURCE)) == (ALT_SIGN_EXT | REG2_SOURCE)) { \
|
||||
FAIL_IF(push_inst(compiler, EXTSW | S(src2) | A(TMP_REG2))); \
|
||||
src2 = TMP_REG2; \
|
||||
}
|
||||
|
||||
#define BIN_EXTS() \
|
||||
if (flags & ALT_SIGN_EXT) { \
|
||||
if (flags & REG1_SOURCE) { \
|
||||
FAIL_IF(push_inst(compiler, EXTSW | S(src1) | A(TMP_REG1))); \
|
||||
src1 = TMP_REG1; \
|
||||
} \
|
||||
if (flags & REG2_SOURCE) { \
|
||||
FAIL_IF(push_inst(compiler, EXTSW | S(src2) | A(TMP_REG2))); \
|
||||
src2 = TMP_REG2; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define BIN_IMM_EXTS() \
|
||||
if ((flags & (ALT_SIGN_EXT | REG1_SOURCE)) == (ALT_SIGN_EXT | REG1_SOURCE)) { \
|
||||
FAIL_IF(push_inst(compiler, EXTSW | S(src1) | A(TMP_REG1))); \
|
||||
src1 = TMP_REG1; \
|
||||
}
|
||||
|
||||
static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 flags,
|
||||
sljit_s32 dst, sljit_s32 src1, sljit_s32 src2)
|
||||
{
|
||||
sljit_u32 imm;
|
||||
|
||||
switch (op) {
|
||||
case SLJIT_MOV:
|
||||
case SLJIT_MOV_P:
|
||||
SLJIT_ASSERT(src1 == TMP_REG1);
|
||||
if (dst != src2)
|
||||
return push_inst(compiler, OR | S(src2) | A(dst) | B(src2));
|
||||
return SLJIT_SUCCESS;
|
||||
|
||||
case SLJIT_MOV_U32:
|
||||
case SLJIT_MOV_S32:
|
||||
SLJIT_ASSERT(src1 == TMP_REG1);
|
||||
if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) {
|
||||
if (op == SLJIT_MOV_S32)
|
||||
return push_inst(compiler, EXTSW | S(src2) | A(dst));
|
||||
return push_inst(compiler, CLRLDI(dst, src2, 32));
|
||||
}
|
||||
else {
|
||||
SLJIT_ASSERT(dst == src2);
|
||||
}
|
||||
return SLJIT_SUCCESS;
|
||||
|
||||
case SLJIT_MOV_U8:
|
||||
case SLJIT_MOV_S8:
|
||||
SLJIT_ASSERT(src1 == TMP_REG1);
|
||||
if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) {
|
||||
if (op == SLJIT_MOV_S8)
|
||||
return push_inst(compiler, EXTSB | S(src2) | A(dst));
|
||||
return push_inst(compiler, CLRLDI(dst, src2, 56));
|
||||
}
|
||||
else if ((flags & REG_DEST) && op == SLJIT_MOV_S8)
|
||||
return push_inst(compiler, EXTSB | S(src2) | A(dst));
|
||||
else {
|
||||
SLJIT_ASSERT(dst == src2);
|
||||
}
|
||||
return SLJIT_SUCCESS;
|
||||
|
||||
case SLJIT_MOV_U16:
|
||||
case SLJIT_MOV_S16:
|
||||
SLJIT_ASSERT(src1 == TMP_REG1);
|
||||
if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) {
|
||||
if (op == SLJIT_MOV_S16)
|
||||
return push_inst(compiler, EXTSH | S(src2) | A(dst));
|
||||
return push_inst(compiler, CLRLDI(dst, src2, 48));
|
||||
}
|
||||
else {
|
||||
SLJIT_ASSERT(dst == src2);
|
||||
}
|
||||
return SLJIT_SUCCESS;
|
||||
|
||||
case SLJIT_CLZ:
|
||||
SLJIT_ASSERT(src1 == TMP_REG1);
|
||||
return push_inst(compiler, ((flags & ALT_FORM1) ? CNTLZW : CNTLZD) | S(src2) | A(dst));
|
||||
|
||||
case SLJIT_CTZ:
|
||||
SLJIT_ASSERT(src1 == TMP_REG1);
|
||||
FAIL_IF(push_inst(compiler, NEG | D(TMP_REG1) | A(src2)));
|
||||
FAIL_IF(push_inst(compiler, AND | S(src2) | A(dst) | B(TMP_REG1)));
|
||||
FAIL_IF(push_inst(compiler, ((flags & ALT_FORM1) ? CNTLZW : CNTLZD) | S(dst) | A(dst)));
|
||||
FAIL_IF(push_inst(compiler, ADDI | D(TMP_REG1) | A(dst) | IMM((flags & ALT_FORM1) ? -32 : -64)));
|
||||
/* The highest bits are set, if dst < bit width, zero otherwise. */
|
||||
FAIL_IF(push_inst(compiler, ((flags & ALT_FORM1) ? SRWI(27) : SRDI(58)) | S(TMP_REG1) | A(TMP_REG1)));
|
||||
return push_inst(compiler, XOR | S(dst) | A(dst) | B(TMP_REG1));
|
||||
|
||||
case SLJIT_ADD:
|
||||
if (flags & ALT_FORM1) {
|
||||
if (flags & ALT_SIGN_EXT) {
|
||||
FAIL_IF(push_inst(compiler, SLDI(32) | S(src1) | A(TMP_REG1)));
|
||||
src1 = TMP_REG1;
|
||||
FAIL_IF(push_inst(compiler, SLDI(32) | S(src2) | A(TMP_REG2)));
|
||||
src2 = TMP_REG2;
|
||||
}
|
||||
/* Setting XER SO is not enough, CR SO is also needed. */
|
||||
FAIL_IF(push_inst(compiler, ADD | OE(ALT_SET_FLAGS) | RC(ALT_SET_FLAGS) | D(dst) | A(src1) | B(src2)));
|
||||
if (flags & ALT_SIGN_EXT)
|
||||
return push_inst(compiler, SRDI(32) | S(dst) | A(dst));
|
||||
return SLJIT_SUCCESS;
|
||||
}
|
||||
|
||||
if (flags & ALT_FORM2) {
|
||||
/* Flags does not set: BIN_IMM_EXTS unnecessary. */
|
||||
SLJIT_ASSERT(src2 == TMP_REG2);
|
||||
|
||||
if (flags & ALT_FORM3)
|
||||
return push_inst(compiler, ADDIS | D(dst) | A(src1) | compiler->imm);
|
||||
|
||||
imm = compiler->imm;
|
||||
|
||||
if (flags & ALT_FORM4) {
|
||||
FAIL_IF(push_inst(compiler, ADDIS | D(dst) | A(src1) | (((imm >> 16) & 0xffff) + ((imm >> 15) & 0x1))));
|
||||
src1 = dst;
|
||||
}
|
||||
|
||||
return push_inst(compiler, ADDI | D(dst) | A(src1) | (imm & 0xffff));
|
||||
}
|
||||
if (flags & ALT_FORM3) {
|
||||
SLJIT_ASSERT(src2 == TMP_REG2);
|
||||
BIN_IMM_EXTS();
|
||||
return push_inst(compiler, ADDIC | D(dst) | A(src1) | compiler->imm);
|
||||
}
|
||||
if (flags & ALT_FORM4) {
|
||||
if (flags & ALT_FORM5)
|
||||
FAIL_IF(push_inst(compiler, ADDI | D(dst) | A(src1) | compiler->imm));
|
||||
else
|
||||
FAIL_IF(push_inst(compiler, ADD | D(dst) | A(src1) | B(src2)));
|
||||
return push_inst(compiler, CMPI | A(dst) | 0);
|
||||
}
|
||||
if (!(flags & ALT_SET_FLAGS))
|
||||
return push_inst(compiler, ADD | D(dst) | A(src1) | B(src2));
|
||||
BIN_EXTS();
|
||||
if (flags & ALT_FORM5)
|
||||
return push_inst(compiler, ADDC | RC(ALT_SET_FLAGS) | D(dst) | A(src1) | B(src2));
|
||||
return push_inst(compiler, ADD | RC(flags) | D(dst) | A(src1) | B(src2));
|
||||
|
||||
case SLJIT_ADDC:
|
||||
BIN_EXTS();
|
||||
return push_inst(compiler, ADDE | D(dst) | A(src1) | B(src2));
|
||||
|
||||
case SLJIT_SUB:
|
||||
if (flags & ALT_FORM1) {
|
||||
if (flags & ALT_FORM2) {
|
||||
FAIL_IF(push_inst(compiler, CMPLI | CRD(0 | ((flags & ALT_SIGN_EXT) ? 0 : 1)) | A(src1) | compiler->imm));
|
||||
if (!(flags & ALT_FORM3))
|
||||
return SLJIT_SUCCESS;
|
||||
return push_inst(compiler, ADDI | D(dst) | A(src1) | (-compiler->imm & 0xffff));
|
||||
}
|
||||
FAIL_IF(push_inst(compiler, CMPL | CRD(0 | ((flags & ALT_SIGN_EXT) ? 0 : 1)) | A(src1) | B(src2)));
|
||||
if (!(flags & ALT_FORM3))
|
||||
return SLJIT_SUCCESS;
|
||||
return push_inst(compiler, SUBF | D(dst) | A(src2) | B(src1));
|
||||
}
|
||||
|
||||
if (flags & ALT_FORM2) {
|
||||
if (flags & ALT_FORM3) {
|
||||
FAIL_IF(push_inst(compiler, CMPI | CRD(0 | ((flags & ALT_SIGN_EXT) ? 0 : 1)) | A(src1) | compiler->imm));
|
||||
if (!(flags & ALT_FORM4))
|
||||
return SLJIT_SUCCESS;
|
||||
return push_inst(compiler, ADDI | D(dst) | A(src1) | (-compiler->imm & 0xffff));
|
||||
}
|
||||
FAIL_IF(push_inst(compiler, CMP | CRD(0 | ((flags & ALT_SIGN_EXT) ? 0 : 1)) | A(src1) | B(src2)));
|
||||
if (!(flags & ALT_FORM4))
|
||||
return SLJIT_SUCCESS;
|
||||
return push_inst(compiler, SUBF | D(dst) | A(src2) | B(src1));
|
||||
}
|
||||
|
||||
if (flags & ALT_FORM3) {
|
||||
if (flags & ALT_SIGN_EXT) {
|
||||
if (src1 != TMP_ZERO) {
|
||||
FAIL_IF(push_inst(compiler, SLDI(32) | S(src1) | A(TMP_REG1)));
|
||||
src1 = TMP_REG1;
|
||||
}
|
||||
if (src2 != TMP_ZERO) {
|
||||
FAIL_IF(push_inst(compiler, SLDI(32) | S(src2) | A(TMP_REG2)));
|
||||
src2 = TMP_REG2;
|
||||
}
|
||||
}
|
||||
|
||||
/* Setting XER SO is not enough, CR SO is also needed. */
|
||||
if (src1 != TMP_ZERO)
|
||||
FAIL_IF(push_inst(compiler, SUBF | OE(ALT_SET_FLAGS) | RC(ALT_SET_FLAGS) | D(dst) | A(src2) | B(src1)));
|
||||
else
|
||||
FAIL_IF(push_inst(compiler, NEG | OE(ALT_SET_FLAGS) | RC(ALT_SET_FLAGS) | D(dst) | A(src2)));
|
||||
|
||||
if (flags & ALT_SIGN_EXT)
|
||||
return push_inst(compiler, SRDI(32) | S(dst) | A(dst));
|
||||
return SLJIT_SUCCESS;
|
||||
}
|
||||
|
||||
if (flags & ALT_FORM4) {
|
||||
/* Flags does not set: BIN_IMM_EXTS unnecessary. */
|
||||
SLJIT_ASSERT(src2 == TMP_REG2);
|
||||
return push_inst(compiler, SUBFIC | D(dst) | A(src1) | compiler->imm);
|
||||
}
|
||||
|
||||
if (!(flags & ALT_SET_FLAGS)) {
|
||||
SLJIT_ASSERT(src1 != TMP_ZERO);
|
||||
return push_inst(compiler, SUBF | D(dst) | A(src2) | B(src1));
|
||||
}
|
||||
|
||||
BIN_EXTS();
|
||||
if (flags & ALT_FORM5)
|
||||
return push_inst(compiler, SUBFC | RC(ALT_SET_FLAGS) | D(dst) | A(src2) | B(src1));
|
||||
|
||||
if (src1 != TMP_ZERO)
|
||||
return push_inst(compiler, SUBF | RC(ALT_SET_FLAGS) | D(dst) | A(src2) | B(src1));
|
||||
return push_inst(compiler, NEG | RC(ALT_SET_FLAGS) | D(dst) | A(src2));
|
||||
|
||||
case SLJIT_SUBC:
|
||||
BIN_EXTS();
|
||||
return push_inst(compiler, SUBFE | D(dst) | A(src2) | B(src1));
|
||||
|
||||
case SLJIT_MUL:
|
||||
if (flags & ALT_FORM1) {
|
||||
SLJIT_ASSERT(src2 == TMP_REG2);
|
||||
return push_inst(compiler, MULLI | D(dst) | A(src1) | compiler->imm);
|
||||
}
|
||||
BIN_EXTS();
|
||||
if (flags & ALT_FORM2)
|
||||
return push_inst(compiler, MULLW | OE(flags) | RC(flags) | D(dst) | A(src2) | B(src1));
|
||||
return push_inst(compiler, MULLD | OE(flags) | RC(flags) | D(dst) | A(src2) | B(src1));
|
||||
|
||||
case SLJIT_AND:
|
||||
if (flags & ALT_FORM1) {
|
||||
SLJIT_ASSERT(src2 == TMP_REG2);
|
||||
return push_inst(compiler, ANDI | S(src1) | A(dst) | compiler->imm);
|
||||
}
|
||||
if (flags & ALT_FORM2) {
|
||||
SLJIT_ASSERT(src2 == TMP_REG2);
|
||||
return push_inst(compiler, ANDIS | S(src1) | A(dst) | compiler->imm);
|
||||
}
|
||||
return push_inst(compiler, AND | RC(flags) | S(src1) | A(dst) | B(src2));
|
||||
|
||||
case SLJIT_OR:
|
||||
if (flags & ALT_FORM1) {
|
||||
SLJIT_ASSERT(src2 == TMP_REG2);
|
||||
return push_inst(compiler, ORI | S(src1) | A(dst) | compiler->imm);
|
||||
}
|
||||
if (flags & ALT_FORM2) {
|
||||
SLJIT_ASSERT(src2 == TMP_REG2);
|
||||
return push_inst(compiler, ORIS | S(src1) | A(dst) | compiler->imm);
|
||||
}
|
||||
if (flags & ALT_FORM3) {
|
||||
SLJIT_ASSERT(src2 == TMP_REG2);
|
||||
imm = compiler->imm;
|
||||
|
||||
FAIL_IF(push_inst(compiler, ORI | S(src1) | A(dst) | IMM(imm)));
|
||||
return push_inst(compiler, ORIS | S(dst) | A(dst) | IMM(imm >> 16));
|
||||
}
|
||||
return push_inst(compiler, OR | RC(flags) | S(src1) | A(dst) | B(src2));
|
||||
|
||||
case SLJIT_XOR:
|
||||
if (flags & ALT_FORM1) {
|
||||
SLJIT_ASSERT(src2 == TMP_REG2);
|
||||
return push_inst(compiler, XORI | S(src1) | A(dst) | compiler->imm);
|
||||
}
|
||||
if (flags & ALT_FORM2) {
|
||||
SLJIT_ASSERT(src2 == TMP_REG2);
|
||||
return push_inst(compiler, XORIS | S(src1) | A(dst) | compiler->imm);
|
||||
}
|
||||
if (flags & ALT_FORM3) {
|
||||
SLJIT_ASSERT(src2 == TMP_REG2);
|
||||
imm = compiler->imm;
|
||||
|
||||
FAIL_IF(push_inst(compiler, XORI | S(src1) | A(dst) | IMM(imm)));
|
||||
return push_inst(compiler, XORIS | S(dst) | A(dst) | IMM(imm >> 16));
|
||||
}
|
||||
if (flags & ALT_FORM4) {
|
||||
SLJIT_ASSERT(src1 == TMP_REG1);
|
||||
UN_EXTS();
|
||||
return push_inst(compiler, NOR | RC(flags) | S(src2) | A(dst) | B(src2));
|
||||
}
|
||||
return push_inst(compiler, XOR | RC(flags) | S(src1) | A(dst) | B(src2));
|
||||
|
||||
case SLJIT_SHL:
|
||||
case SLJIT_MSHL:
|
||||
if (flags & ALT_FORM1) {
|
||||
SLJIT_ASSERT(src2 == TMP_REG2);
|
||||
imm = compiler->imm;
|
||||
|
||||
if (flags & ALT_FORM2) {
|
||||
imm &= 0x1f;
|
||||
return push_inst(compiler, SLWI(imm) | RC(flags) | S(src1) | A(dst));
|
||||
}
|
||||
|
||||
imm &= 0x3f;
|
||||
return push_inst(compiler, SLDI(imm) | RC(flags) | S(src1) | A(dst));
|
||||
}
|
||||
|
||||
if (op == SLJIT_MSHL) {
|
||||
FAIL_IF(push_inst(compiler, ANDI | S(src2) | A(TMP_REG2) | ((flags & ALT_FORM2) ? 0x1f : 0x3f)));
|
||||
src2 = TMP_REG2;
|
||||
}
|
||||
|
||||
return push_inst(compiler, ((flags & ALT_FORM2) ? SLW : SLD) | RC(flags) | S(src1) | A(dst) | B(src2));
|
||||
|
||||
case SLJIT_LSHR:
|
||||
case SLJIT_MLSHR:
|
||||
if (flags & ALT_FORM1) {
|
||||
SLJIT_ASSERT(src2 == TMP_REG2);
|
||||
imm = compiler->imm;
|
||||
|
||||
if (flags & ALT_FORM2) {
|
||||
imm &= 0x1f;
|
||||
/* Since imm can be 0, SRWI() cannot be used. */
|
||||
return push_inst(compiler, RLWINM | RC(flags) | S(src1) | A(dst) | RLWI_SH((32 - imm) & 0x1f) | RLWI_MBE(imm, 31));
|
||||
}
|
||||
|
||||
imm &= 0x3f;
|
||||
/* Since imm can be 0, SRDI() cannot be used. */
|
||||
return push_inst(compiler, RLDICL | RC(flags) | S(src1) | A(dst) | RLDI_SH((64 - imm) & 0x3f) | RLDI_MB(imm));
|
||||
}
|
||||
|
||||
if (op == SLJIT_MLSHR) {
|
||||
FAIL_IF(push_inst(compiler, ANDI | S(src2) | A(TMP_REG2) | ((flags & ALT_FORM2) ? 0x1f : 0x3f)));
|
||||
src2 = TMP_REG2;
|
||||
}
|
||||
|
||||
return push_inst(compiler, ((flags & ALT_FORM2) ? SRW : SRD) | RC(flags) | S(src1) | A(dst) | B(src2));
|
||||
|
||||
case SLJIT_ASHR:
|
||||
case SLJIT_MASHR:
|
||||
if (flags & ALT_FORM1) {
|
||||
SLJIT_ASSERT(src2 == TMP_REG2);
|
||||
imm = compiler->imm;
|
||||
|
||||
if (flags & ALT_FORM2) {
|
||||
imm &= 0x1f;
|
||||
return push_inst(compiler, SRAWI | RC(flags) | S(src1) | A(dst) | (imm << 11));
|
||||
}
|
||||
|
||||
imm &= 0x3f;
|
||||
return push_inst(compiler, SRADI | RC(flags) | S(src1) | A(dst) | RLDI_SH(imm));
|
||||
}
|
||||
|
||||
if (op == SLJIT_MASHR) {
|
||||
FAIL_IF(push_inst(compiler, ANDI | S(src2) | A(TMP_REG2) | ((flags & ALT_FORM2) ? 0x1f : 0x3f)));
|
||||
src2 = TMP_REG2;
|
||||
}
|
||||
|
||||
return push_inst(compiler, ((flags & ALT_FORM2) ? SRAW : SRAD) | RC(flags) | S(src1) | A(dst) | B(src2));
|
||||
|
||||
case SLJIT_ROTL:
|
||||
case SLJIT_ROTR:
|
||||
if (flags & ALT_FORM1) {
|
||||
SLJIT_ASSERT(src2 == TMP_REG2);
|
||||
imm = compiler->imm;
|
||||
|
||||
if (op == SLJIT_ROTR)
|
||||
imm = (sljit_u32)(-(sljit_s32)imm);
|
||||
|
||||
if (flags & ALT_FORM2) {
|
||||
imm &= 0x1f;
|
||||
return push_inst(compiler, RLWINM | S(src1) | A(dst) | RLWI_SH(imm) | RLWI_MBE(0, 31));
|
||||
}
|
||||
|
||||
imm &= 0x3f;
|
||||
return push_inst(compiler, RLDICL | S(src1) | A(dst) | RLDI_SH(imm));
|
||||
}
|
||||
|
||||
if (op == SLJIT_ROTR) {
|
||||
FAIL_IF(push_inst(compiler, SUBFIC | D(TMP_REG2) | A(src2) | 0));
|
||||
src2 = TMP_REG2;
|
||||
}
|
||||
|
||||
return push_inst(compiler, ((flags & ALT_FORM2) ? (RLWNM | RLWI_MBE(0, 31)) : (RLDCL | RLDI_MB(0))) | S(src1) | A(dst) | B(src2));
|
||||
}
|
||||
|
||||
SLJIT_UNREACHABLE();
|
||||
return SLJIT_SUCCESS;
|
||||
}
|
||||
|
||||
static sljit_s32 call_with_args(struct sljit_compiler *compiler, sljit_s32 arg_types, sljit_s32 *src)
|
||||
{
|
||||
sljit_s32 arg_count = 0;
|
||||
sljit_s32 word_arg_count = 0;
|
||||
sljit_s32 types = 0;
|
||||
sljit_s32 reg = 0;
|
||||
|
||||
if (src)
|
||||
reg = *src & REG_MASK;
|
||||
|
||||
arg_types >>= SLJIT_ARG_SHIFT;
|
||||
|
||||
while (arg_types) {
|
||||
types = (types << SLJIT_ARG_SHIFT) | (arg_types & SLJIT_ARG_MASK);
|
||||
|
||||
switch (arg_types & SLJIT_ARG_MASK) {
|
||||
case SLJIT_ARG_TYPE_F64:
|
||||
case SLJIT_ARG_TYPE_F32:
|
||||
arg_count++;
|
||||
break;
|
||||
default:
|
||||
arg_count++;
|
||||
word_arg_count++;
|
||||
|
||||
if (arg_count != word_arg_count && arg_count == reg) {
|
||||
FAIL_IF(push_inst(compiler, OR | S(reg) | A(TMP_CALL_REG) | B(reg)));
|
||||
*src = TMP_CALL_REG;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
arg_types >>= SLJIT_ARG_SHIFT;
|
||||
}
|
||||
|
||||
while (types) {
|
||||
switch (types & SLJIT_ARG_MASK) {
|
||||
case SLJIT_ARG_TYPE_F64:
|
||||
case SLJIT_ARG_TYPE_F32:
|
||||
arg_count--;
|
||||
break;
|
||||
default:
|
||||
if (arg_count != word_arg_count)
|
||||
FAIL_IF(push_inst(compiler, OR | S(word_arg_count) | A(arg_count) | B(word_arg_count)));
|
||||
|
||||
arg_count--;
|
||||
word_arg_count--;
|
||||
break;
|
||||
}
|
||||
|
||||
types >>= SLJIT_ARG_SHIFT;
|
||||
}
|
||||
|
||||
return SLJIT_SUCCESS;
|
||||
}
|
||||
|
||||
static SLJIT_INLINE sljit_s32 emit_const(struct sljit_compiler *compiler, sljit_s32 reg, sljit_sw init_value)
|
||||
{
|
||||
FAIL_IF(push_inst(compiler, ADDIS | D(reg) | A(0) | IMM(init_value >> 48)));
|
||||
FAIL_IF(push_inst(compiler, ORI | S(reg) | A(reg) | IMM(init_value >> 32)));
|
||||
FAIL_IF(push_inst(compiler, SLDI(32) | S(reg) | A(reg)));
|
||||
FAIL_IF(push_inst(compiler, ORIS | S(reg) | A(reg) | IMM(init_value >> 16)));
|
||||
return push_inst(compiler, ORI | S(reg) | A(reg) | IMM(init_value));
|
||||
}
|
||||
|
||||
static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_f64_from_sw(struct sljit_compiler *compiler, sljit_s32 op,
|
||||
sljit_s32 dst, sljit_sw dstw,
|
||||
sljit_s32 src, sljit_sw srcw)
|
||||
{
|
||||
sljit_s32 dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1;
|
||||
|
||||
if (src == SLJIT_IMM) {
|
||||
if (GET_OPCODE(op) == SLJIT_CONV_F64_FROM_S32)
|
||||
srcw = (sljit_s32)srcw;
|
||||
|
||||
FAIL_IF(load_immediate(compiler, TMP_REG1, srcw));
|
||||
src = TMP_REG1;
|
||||
} else if (GET_OPCODE(op) == SLJIT_CONV_F64_FROM_S32) {
|
||||
if (FAST_IS_REG(src))
|
||||
FAIL_IF(push_inst(compiler, EXTSW | S(src) | A(TMP_REG1)));
|
||||
else
|
||||
FAIL_IF(emit_op_mem(compiler, INT_DATA | SIGNED_DATA | LOAD_DATA, TMP_REG1, src, srcw, TMP_REG1));
|
||||
src = TMP_REG1;
|
||||
}
|
||||
|
||||
if (FAST_IS_REG(src)) {
|
||||
FAIL_IF(push_inst(compiler, STD | S(src) | A(SLJIT_SP) | TMP_MEM_OFFSET));
|
||||
FAIL_IF(push_inst(compiler, LFD | FS(TMP_FREG1) | A(SLJIT_SP) | TMP_MEM_OFFSET));
|
||||
} else
|
||||
FAIL_IF(emit_op_mem(compiler, DOUBLE_DATA | LOAD_DATA, TMP_FREG1, src, srcw, TMP_REG1));
|
||||
|
||||
FAIL_IF(push_inst(compiler, FCFID | FD(dst_r) | FB(TMP_FREG1)));
|
||||
|
||||
if (op & SLJIT_32)
|
||||
FAIL_IF(push_inst(compiler, FRSP | FD(dst_r) | FB(dst_r)));
|
||||
|
||||
if (dst & SLJIT_MEM)
|
||||
return emit_op_mem(compiler, FLOAT_DATA(op), TMP_FREG1, dst, dstw, TMP_REG1);
|
||||
return SLJIT_SUCCESS;
|
||||
}
|
||||
|
||||
static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_f64_from_uw(struct sljit_compiler *compiler, sljit_s32 op,
|
||||
sljit_s32 dst, sljit_sw dstw,
|
||||
sljit_s32 src, sljit_sw srcw)
|
||||
{
|
||||
sljit_s32 dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1;
|
||||
|
||||
if (GET_OPCODE(op) == SLJIT_CONV_F64_FROM_U32) {
|
||||
if (src == SLJIT_IMM) {
|
||||
FAIL_IF(load_immediate(compiler, TMP_REG1, (sljit_u32)srcw));
|
||||
src = TMP_REG1;
|
||||
} else {
|
||||
if (FAST_IS_REG(src))
|
||||
FAIL_IF(push_inst(compiler, CLRLDI(TMP_REG1, src, 32)));
|
||||
else
|
||||
FAIL_IF(emit_op_mem(compiler, INT_DATA | LOAD_DATA, TMP_REG1, src, srcw, TMP_REG1));
|
||||
src = TMP_REG1;
|
||||
}
|
||||
|
||||
FAIL_IF(push_inst(compiler, STD | S(src) | A(SLJIT_SP) | TMP_MEM_OFFSET));
|
||||
FAIL_IF(push_inst(compiler, LFD | FS(TMP_FREG1) | A(SLJIT_SP) | TMP_MEM_OFFSET));
|
||||
FAIL_IF(push_inst(compiler, FCFID | FD(dst_r) | FB(TMP_FREG1)));
|
||||
} else {
|
||||
if (src == SLJIT_IMM) {
|
||||
FAIL_IF(load_immediate(compiler, TMP_REG1, srcw));
|
||||
src = TMP_REG1;
|
||||
} else if (src & SLJIT_MEM) {
|
||||
FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, TMP_REG1, src, srcw, TMP_REG1));
|
||||
src = TMP_REG1;
|
||||
}
|
||||
|
||||
FAIL_IF(push_inst(compiler, CMPI | CRD(0 | 1) | A(src) | 0));
|
||||
FAIL_IF(push_inst(compiler, BCx | (12 << 21) | (0 << 16) | 20));
|
||||
FAIL_IF(push_inst(compiler, STD | S(src) | A(SLJIT_SP) | TMP_MEM_OFFSET));
|
||||
FAIL_IF(push_inst(compiler, LFD | FS(TMP_FREG1) | A(SLJIT_SP) | TMP_MEM_OFFSET));
|
||||
FAIL_IF(push_inst(compiler, FCFID | FD(dst_r) | FB(TMP_FREG1)));
|
||||
FAIL_IF(push_inst(compiler, Bx | ((op & SLJIT_32) ? 36 : 32)));
|
||||
|
||||
if (op & SLJIT_32)
|
||||
FAIL_IF(push_inst(compiler, RLWINM | S(src) | A(TMP_REG2) | RLWI_SH(10) | RLWI_MBE(10, 21)));
|
||||
else
|
||||
FAIL_IF(push_inst(compiler, ANDI | S(src) | A(TMP_REG2) | 0x1));
|
||||
|
||||
/* Shift right. */
|
||||
FAIL_IF(push_inst(compiler, RLDICL | S(src) | A(TMP_REG1) | RLDI_SH(63) | RLDI_MB(1)));
|
||||
|
||||
if (op & SLJIT_32)
|
||||
FAIL_IF(push_inst(compiler, RLDICR | S(TMP_REG1) | A(TMP_REG1) | RLDI_SH(0) | RLDI_ME(53)));
|
||||
|
||||
FAIL_IF(push_inst(compiler, OR | S(TMP_REG1) | A(TMP_REG1) | B(TMP_REG2)));
|
||||
|
||||
FAIL_IF(push_inst(compiler, STD | S(TMP_REG1) | A(SLJIT_SP) | TMP_MEM_OFFSET));
|
||||
FAIL_IF(push_inst(compiler, LFD | FS(TMP_FREG1) | A(SLJIT_SP) | TMP_MEM_OFFSET));
|
||||
FAIL_IF(push_inst(compiler, FCFID | FD(dst_r) | FB(TMP_FREG1)));
|
||||
FAIL_IF(push_inst(compiler, FADD | FD(dst_r) | FA(dst_r) | FB(dst_r)));
|
||||
}
|
||||
|
||||
if (op & SLJIT_32)
|
||||
FAIL_IF(push_inst(compiler, FRSP | FD(dst_r) | FB(dst_r)));
|
||||
|
||||
if (dst & SLJIT_MEM)
|
||||
return emit_op_mem(compiler, FLOAT_DATA(op), TMP_FREG1, dst, dstw, TMP_REG1);
|
||||
return SLJIT_SUCCESS;
|
||||
}
|
||||
|
||||
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fset64(struct sljit_compiler *compiler,
|
||||
sljit_s32 freg, sljit_f64 value)
|
||||
{
|
||||
union {
|
||||
sljit_sw imm;
|
||||
sljit_f64 value;
|
||||
} u;
|
||||
|
||||
CHECK_ERROR();
|
||||
CHECK(check_sljit_emit_fset64(compiler, freg, value));
|
||||
|
||||
u.value = value;
|
||||
|
||||
if (u.imm != 0)
|
||||
FAIL_IF(load_immediate(compiler, TMP_REG1, u.imm));
|
||||
|
||||
FAIL_IF(push_inst(compiler, STD | S(u.imm != 0 ? TMP_REG1 : TMP_ZERO) | A(SLJIT_SP) | TMP_MEM_OFFSET));
|
||||
return push_inst(compiler, LFD | FS(freg) | A(SLJIT_SP) | TMP_MEM_OFFSET);
|
||||
}
|
||||
|
||||
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fcopy(struct sljit_compiler *compiler, sljit_s32 op,
|
||||
sljit_s32 freg, sljit_s32 reg)
|
||||
{
|
||||
CHECK_ERROR();
|
||||
CHECK(check_sljit_emit_fcopy(compiler, op, freg, reg));
|
||||
|
||||
if (GET_OPCODE(op) == SLJIT_COPY_TO_F64) {
|
||||
FAIL_IF(push_inst(compiler, ((op & SLJIT_32) ? STW : STD) | S(reg) | A(SLJIT_SP) | TMP_MEM_OFFSET));
|
||||
return push_inst(compiler, ((op & SLJIT_32) ? LFS : LFD) | FS(freg) | A(SLJIT_SP) | TMP_MEM_OFFSET);
|
||||
}
|
||||
|
||||
FAIL_IF(push_inst(compiler, ((op & SLJIT_32) ? STFS : STFD) | FS(freg) | A(SLJIT_SP) | TMP_MEM_OFFSET));
|
||||
return push_inst(compiler, ((op & SLJIT_32) ? LWZ : LD) | S(reg) | A(SLJIT_SP) | TMP_MEM_OFFSET);
|
||||
}
|
||||
|
||||
SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_target, sljit_sw executable_offset)
|
||||
{
|
||||
sljit_ins *inst = (sljit_ins*)addr;
|
||||
SLJIT_UNUSED_ARG(executable_offset);
|
||||
|
||||
SLJIT_UPDATE_WX_FLAGS(inst, inst + 5, 0);
|
||||
inst[0] = (inst[0] & 0xffff0000u) | ((sljit_ins)(new_target >> 48) & 0xffff);
|
||||
inst[1] = (inst[1] & 0xffff0000u) | ((sljit_ins)(new_target >> 32) & 0xffff);
|
||||
inst[3] = (inst[3] & 0xffff0000u) | ((sljit_ins)(new_target >> 16) & 0xffff);
|
||||
inst[4] = (inst[4] & 0xffff0000u) | ((sljit_ins)new_target & 0xffff);
|
||||
SLJIT_UPDATE_WX_FLAGS(inst, inst + 5, 1);
|
||||
inst = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset);
|
||||
SLJIT_CACHE_FLUSH(inst, inst + 5);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,142 +0,0 @@
|
||||
/*
|
||||
* Stack-less Just-In-Time compiler
|
||||
*
|
||||
* Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
static sljit_s32 load_immediate(struct sljit_compiler *compiler, sljit_s32 dst_r, sljit_sw imm, sljit_s32 tmp_r)
|
||||
{
|
||||
SLJIT_UNUSED_ARG(tmp_r);
|
||||
|
||||
if (imm <= SIMM_MAX && imm >= SIMM_MIN)
|
||||
return push_inst(compiler, ADDI | RD(dst_r) | RS1(TMP_ZERO) | IMM_I(imm));
|
||||
|
||||
if (imm & 0x800)
|
||||
imm += 0x1000;
|
||||
|
||||
FAIL_IF(push_inst(compiler, LUI | RD(dst_r) | (sljit_ins)(imm & ~0xfff)));
|
||||
|
||||
if ((imm & 0xfff) == 0)
|
||||
return SLJIT_SUCCESS;
|
||||
|
||||
return push_inst(compiler, ADDI | RD(dst_r) | RS1(dst_r) | IMM_I(imm));
|
||||
}
|
||||
|
||||
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fset64(struct sljit_compiler *compiler,
|
||||
sljit_s32 freg, sljit_f64 value)
|
||||
{
|
||||
union {
|
||||
sljit_s32 imm[2];
|
||||
sljit_f64 value;
|
||||
} u;
|
||||
|
||||
CHECK_ERROR();
|
||||
CHECK(check_sljit_emit_fset64(compiler, freg, value));
|
||||
|
||||
u.value = value;
|
||||
|
||||
if (u.imm[0] != 0)
|
||||
FAIL_IF(load_immediate(compiler, TMP_REG1, u.imm[0], TMP_REG3));
|
||||
if (u.imm[1] != 0)
|
||||
FAIL_IF(load_immediate(compiler, TMP_REG2, u.imm[1], TMP_REG3));
|
||||
|
||||
FAIL_IF(push_inst(compiler, ADDI | RD(SLJIT_SP) | RS1(SLJIT_SP) | IMM_I(-16)));
|
||||
FAIL_IF(push_inst(compiler, SW | RS1(SLJIT_SP) | RS2(u.imm[0] != 0 ? TMP_REG1 : TMP_ZERO) | (8 << 7)));
|
||||
FAIL_IF(push_inst(compiler, SW | RS1(SLJIT_SP) | RS2(u.imm[1] != 0 ? TMP_REG2 : TMP_ZERO) | (12 << 7)));
|
||||
FAIL_IF(push_inst(compiler, FLD | FRD(freg) | RS1(SLJIT_SP) | IMM_I(8)));
|
||||
return push_inst(compiler, ADDI | RD(SLJIT_SP) | RS1(SLJIT_SP) | IMM_I(16));
|
||||
}
|
||||
|
||||
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fcopy(struct sljit_compiler *compiler, sljit_s32 op,
|
||||
sljit_s32 freg, sljit_s32 reg)
|
||||
{
|
||||
sljit_ins inst;
|
||||
sljit_s32 reg2 = 0;
|
||||
|
||||
CHECK_ERROR();
|
||||
CHECK(check_sljit_emit_fcopy(compiler, op, freg, reg));
|
||||
|
||||
if (op & SLJIT_32) {
|
||||
if (op == SLJIT_COPY32_TO_F32)
|
||||
inst = FMV_W_X | RS1(reg) | FRD(freg);
|
||||
else
|
||||
inst = FMV_X_W | FRS1(freg) | RD(reg);
|
||||
|
||||
return push_inst(compiler, inst);
|
||||
}
|
||||
|
||||
FAIL_IF(push_inst(compiler, ADDI | RD(SLJIT_SP) | RS1(SLJIT_SP) | IMM_I(-16)));
|
||||
|
||||
if (reg & REG_PAIR_MASK) {
|
||||
reg2 = REG_PAIR_SECOND(reg);
|
||||
reg = REG_PAIR_FIRST(reg);
|
||||
}
|
||||
|
||||
if (op == SLJIT_COPY_TO_F64) {
|
||||
if (reg2 != 0)
|
||||
FAIL_IF(push_inst(compiler, SW | RS1(SLJIT_SP) | RS2(reg2) | (8 << 7)));
|
||||
else
|
||||
FAIL_IF(push_inst(compiler, FSW | RS1(SLJIT_SP) | FRS2(freg) | (8 << 7)));
|
||||
|
||||
FAIL_IF(push_inst(compiler, SW | RS1(SLJIT_SP) | RS2(reg) | (12 << 7)));
|
||||
FAIL_IF(push_inst(compiler, FLD | FRD(freg) | RS1(SLJIT_SP) | IMM_I(8)));
|
||||
} else {
|
||||
FAIL_IF(push_inst(compiler, FSD | RS1(SLJIT_SP) | FRS2(freg) | (8 << 7)));
|
||||
|
||||
if (reg2 != 0)
|
||||
FAIL_IF(push_inst(compiler, FMV_X_W | FRS1(freg) | RD(reg2)));
|
||||
|
||||
FAIL_IF(push_inst(compiler, LW | RD(reg) | RS1(SLJIT_SP) | IMM_I(12)));
|
||||
}
|
||||
|
||||
return push_inst(compiler, ADDI | RD(SLJIT_SP) | RS1(SLJIT_SP) | IMM_I(16));
|
||||
}
|
||||
|
||||
static SLJIT_INLINE sljit_s32 emit_const(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw init_value, sljit_ins last_ins)
|
||||
{
|
||||
if ((init_value & 0x800) != 0)
|
||||
init_value += 0x1000;
|
||||
|
||||
FAIL_IF(push_inst(compiler, LUI | RD(dst) | (sljit_ins)(init_value & ~0xfff)));
|
||||
return push_inst(compiler, last_ins | RS1(dst) | IMM_I(init_value));
|
||||
}
|
||||
|
||||
SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_target, sljit_sw executable_offset)
|
||||
{
|
||||
sljit_ins *inst = (sljit_ins*)addr;
|
||||
SLJIT_UNUSED_ARG(executable_offset);
|
||||
|
||||
if ((new_target & 0x800) != 0)
|
||||
new_target += 0x1000;
|
||||
|
||||
SLJIT_UPDATE_WX_FLAGS(inst, inst + 5, 0);
|
||||
|
||||
SLJIT_ASSERT((inst[0] & 0x7f) == LUI);
|
||||
inst[0] = (inst[0] & 0xfff) | (sljit_ins)((sljit_sw)new_target & ~0xfff);
|
||||
SLJIT_ASSERT((inst[1] & 0x707f) == ADDI || (inst[1] & 0x707f) == JALR);
|
||||
inst[1] = (inst[1] & 0xfffff) | IMM_I(new_target);
|
||||
|
||||
SLJIT_UPDATE_WX_FLAGS(inst, inst + 5, 1);
|
||||
inst = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset);
|
||||
SLJIT_CACHE_FLUSH(inst, inst + 5);
|
||||
}
|
||||
@@ -1,222 +0,0 @@
|
||||
/*
|
||||
* Stack-less Just-In-Time compiler
|
||||
*
|
||||
* Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
static sljit_s32 load_immediate(struct sljit_compiler *compiler, sljit_s32 dst_r, sljit_sw imm, sljit_s32 tmp_r)
|
||||
{
|
||||
sljit_sw high;
|
||||
|
||||
if (imm <= SIMM_MAX && imm >= SIMM_MIN)
|
||||
return push_inst(compiler, ADDI | RD(dst_r) | RS1(TMP_ZERO) | IMM_I(imm));
|
||||
|
||||
if (imm <= 0x7fffffffl && imm >= S32_MIN) {
|
||||
if (imm > S32_MAX) {
|
||||
SLJIT_ASSERT((imm & 0x800) != 0);
|
||||
FAIL_IF(push_inst(compiler, LUI | RD(dst_r) | (sljit_ins)0x80000000u));
|
||||
return push_inst(compiler, XORI | RD(dst_r) | RS1(dst_r) | IMM_I(imm));
|
||||
}
|
||||
|
||||
if ((imm & 0x800) != 0)
|
||||
imm += 0x1000;
|
||||
|
||||
FAIL_IF(push_inst(compiler, LUI | RD(dst_r) | (sljit_ins)(imm & ~0xfff)));
|
||||
|
||||
if ((imm & 0xfff) == 0)
|
||||
return SLJIT_SUCCESS;
|
||||
|
||||
return push_inst(compiler, ADDI | RD(dst_r) | RS1(dst_r) | IMM_I(imm));
|
||||
}
|
||||
|
||||
/* Trailing zeroes could be used to produce shifted immediates. */
|
||||
|
||||
if (imm <= 0x7ffffffffffl && imm >= -0x80000000000l) {
|
||||
high = imm >> 12;
|
||||
|
||||
if (imm & 0x800)
|
||||
high = ~high;
|
||||
|
||||
if (high > S32_MAX) {
|
||||
SLJIT_ASSERT((high & 0x800) != 0);
|
||||
FAIL_IF(push_inst(compiler, LUI | RD(dst_r) | (sljit_ins)0x80000000u));
|
||||
FAIL_IF(push_inst(compiler, XORI | RD(dst_r) | RS1(dst_r) | IMM_I(high)));
|
||||
} else {
|
||||
if ((high & 0x800) != 0)
|
||||
high += 0x1000;
|
||||
|
||||
FAIL_IF(push_inst(compiler, LUI | RD(dst_r) | (sljit_ins)(high & ~0xfff)));
|
||||
|
||||
if ((high & 0xfff) != 0)
|
||||
FAIL_IF(push_inst(compiler, ADDI | RD(dst_r) | RS1(dst_r) | IMM_I(high)));
|
||||
}
|
||||
|
||||
FAIL_IF(push_inst(compiler, SLLI | RD(dst_r) | RS1(dst_r) | IMM_I(12)));
|
||||
|
||||
if ((imm & 0xfff) != 0)
|
||||
return push_inst(compiler, XORI | RD(dst_r) | RS1(dst_r) | IMM_I(imm));
|
||||
|
||||
return SLJIT_SUCCESS;
|
||||
}
|
||||
|
||||
SLJIT_ASSERT(dst_r != tmp_r);
|
||||
|
||||
high = imm >> 32;
|
||||
imm = (sljit_s32)imm;
|
||||
|
||||
if ((imm & 0x80000000l) != 0)
|
||||
high = ~high;
|
||||
|
||||
if (high <= 0x7ffff && high >= -0x80000) {
|
||||
FAIL_IF(push_inst(compiler, LUI | RD(tmp_r) | (sljit_ins)(high << 12)));
|
||||
high = 0x1000;
|
||||
} else {
|
||||
if ((high & 0x800) != 0)
|
||||
high += 0x1000;
|
||||
|
||||
FAIL_IF(push_inst(compiler, LUI | RD(tmp_r) | (sljit_ins)(high & ~0xfff)));
|
||||
high &= 0xfff;
|
||||
}
|
||||
|
||||
if (imm <= SIMM_MAX && imm >= SIMM_MIN) {
|
||||
FAIL_IF(push_inst(compiler, ADDI | RD(dst_r) | RS1(TMP_ZERO) | IMM_I(imm)));
|
||||
imm = 0;
|
||||
} else if (imm > S32_MAX) {
|
||||
SLJIT_ASSERT((imm & 0x800) != 0);
|
||||
|
||||
FAIL_IF(push_inst(compiler, LUI | RD(dst_r) | (sljit_ins)0x80000000u));
|
||||
imm = 0x1000 | (imm & 0xfff);
|
||||
} else {
|
||||
if ((imm & 0x800) != 0)
|
||||
imm += 0x1000;
|
||||
|
||||
FAIL_IF(push_inst(compiler, LUI | RD(dst_r) | (sljit_ins)(imm & ~0xfff)));
|
||||
imm &= 0xfff;
|
||||
}
|
||||
|
||||
if ((high & 0xfff) != 0)
|
||||
FAIL_IF(push_inst(compiler, ADDI | RD(tmp_r) | RS1(tmp_r) | IMM_I(high)));
|
||||
|
||||
if (imm & 0x1000)
|
||||
FAIL_IF(push_inst(compiler, XORI | RD(dst_r) | RS1(dst_r) | IMM_I(imm)));
|
||||
else if (imm != 0)
|
||||
FAIL_IF(push_inst(compiler, ADDI | RD(dst_r) | RS1(dst_r) | IMM_I(imm)));
|
||||
|
||||
FAIL_IF(push_inst(compiler, SLLI | RD(tmp_r) | RS1(tmp_r) | IMM_I((high & 0x1000) ? 20 : 32)));
|
||||
return push_inst(compiler, XOR | RD(dst_r) | RS1(dst_r) | RS2(tmp_r));
|
||||
}
|
||||
|
||||
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fset64(struct sljit_compiler *compiler,
|
||||
sljit_s32 freg, sljit_f64 value)
|
||||
{
|
||||
union {
|
||||
sljit_sw imm;
|
||||
sljit_f64 value;
|
||||
} u;
|
||||
|
||||
CHECK_ERROR();
|
||||
CHECK(check_sljit_emit_fset64(compiler, freg, value));
|
||||
|
||||
u.value = value;
|
||||
|
||||
if (u.imm == 0)
|
||||
return push_inst(compiler, FMV_W_X | (1 << 25) | RS1(TMP_ZERO) | FRD(freg));
|
||||
|
||||
FAIL_IF(load_immediate(compiler, TMP_REG1, u.imm, TMP_REG3));
|
||||
return push_inst(compiler, FMV_W_X | (1 << 25) | RS1(TMP_REG1) | FRD(freg));
|
||||
}
|
||||
|
||||
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fcopy(struct sljit_compiler *compiler, sljit_s32 op,
|
||||
sljit_s32 freg, sljit_s32 reg)
|
||||
{
|
||||
sljit_ins inst;
|
||||
|
||||
CHECK_ERROR();
|
||||
CHECK(check_sljit_emit_fcopy(compiler, op, freg, reg));
|
||||
|
||||
if (GET_OPCODE(op) == SLJIT_COPY_TO_F64)
|
||||
inst = FMV_W_X | RS1(reg) | FRD(freg);
|
||||
else
|
||||
inst = FMV_X_W | FRS1(freg) | RD(reg);
|
||||
|
||||
if (!(op & SLJIT_32))
|
||||
inst |= (sljit_ins)1 << 25;
|
||||
|
||||
return push_inst(compiler, inst);
|
||||
}
|
||||
|
||||
static SLJIT_INLINE sljit_s32 emit_const(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw init_value, sljit_ins last_ins)
|
||||
{
|
||||
sljit_sw high;
|
||||
|
||||
if ((init_value & 0x800) != 0)
|
||||
init_value += 0x1000;
|
||||
|
||||
high = init_value >> 32;
|
||||
|
||||
if ((init_value & 0x80000000l) != 0)
|
||||
high = ~high;
|
||||
|
||||
if ((high & 0x800) != 0)
|
||||
high += 0x1000;
|
||||
|
||||
FAIL_IF(push_inst(compiler, LUI | RD(TMP_REG3) | (sljit_ins)(high & ~0xfff)));
|
||||
FAIL_IF(push_inst(compiler, ADDI | RD(TMP_REG3) | RS1(TMP_REG3) | IMM_I(high)));
|
||||
FAIL_IF(push_inst(compiler, LUI | RD(dst) | (sljit_ins)(init_value & ~0xfff)));
|
||||
FAIL_IF(push_inst(compiler, SLLI | RD(TMP_REG3) | RS1(TMP_REG3) | IMM_I(32)));
|
||||
FAIL_IF(push_inst(compiler, XOR | RD(dst) | RS1(dst) | RS2(TMP_REG3)));
|
||||
return push_inst(compiler, last_ins | RS1(dst) | IMM_I(init_value));
|
||||
}
|
||||
|
||||
SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_target, sljit_sw executable_offset)
|
||||
{
|
||||
sljit_ins *inst = (sljit_ins*)addr;
|
||||
sljit_sw high;
|
||||
SLJIT_UNUSED_ARG(executable_offset);
|
||||
|
||||
if ((new_target & 0x800) != 0)
|
||||
new_target += 0x1000;
|
||||
|
||||
high = (sljit_sw)new_target >> 32;
|
||||
|
||||
if ((new_target & 0x80000000l) != 0)
|
||||
high = ~high;
|
||||
|
||||
if ((high & 0x800) != 0)
|
||||
high += 0x1000;
|
||||
|
||||
SLJIT_UPDATE_WX_FLAGS(inst, inst + 5, 0);
|
||||
|
||||
SLJIT_ASSERT((inst[0] & 0x7f) == LUI);
|
||||
inst[0] = (inst[0] & 0xfff) | (sljit_ins)(high & ~0xfff);
|
||||
SLJIT_ASSERT((inst[1] & 0x707f) == ADDI);
|
||||
inst[1] = (inst[1] & 0xfffff) | IMM_I(high);
|
||||
SLJIT_ASSERT((inst[2] & 0x7f) == LUI);
|
||||
inst[2] = (inst[2] & 0xfff) | (sljit_ins)((sljit_sw)new_target & ~0xfff);
|
||||
SLJIT_ASSERT((inst[5] & 0x707f) == ADDI || (inst[5] & 0x707f) == JALR);
|
||||
inst[5] = (inst[5] & 0xfffff) | IMM_I(new_target);
|
||||
SLJIT_UPDATE_WX_FLAGS(inst, inst + 5, 1);
|
||||
|
||||
inst = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset);
|
||||
SLJIT_CACHE_FLUSH(inst, inst + 5);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,516 +0,0 @@
|
||||
/*
|
||||
* Stack-less Just-In-Time compiler
|
||||
*
|
||||
* Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_jump_has_label(struct sljit_jump *jump)
|
||||
{
|
||||
return !(jump->flags & JUMP_ADDR) && (jump->u.label != NULL);
|
||||
}
|
||||
|
||||
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_jump_has_target(struct sljit_jump *jump)
|
||||
{
|
||||
return (jump->flags & JUMP_ADDR) != 0;
|
||||
}
|
||||
|
||||
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_jump_is_mov_addr(struct sljit_jump *jump)
|
||||
{
|
||||
return (jump->flags & JUMP_MOV_ADDR) != 0;
|
||||
}
|
||||
|
||||
#define SLJIT_SERIALIZE_DEBUG ((sljit_u16)0x1)
|
||||
|
||||
struct sljit_serialized_compiler {
|
||||
sljit_u32 signature;
|
||||
sljit_u16 version;
|
||||
sljit_u16 cpu_type;
|
||||
|
||||
sljit_uw buf_segment_count;
|
||||
sljit_uw label_count;
|
||||
sljit_uw jump_count;
|
||||
sljit_uw const_count;
|
||||
|
||||
sljit_s32 options;
|
||||
sljit_s32 scratches;
|
||||
sljit_s32 saveds;
|
||||
sljit_s32 fscratches;
|
||||
sljit_s32 fsaveds;
|
||||
sljit_s32 local_size;
|
||||
sljit_uw size;
|
||||
|
||||
#if (defined SLJIT_HAS_STATUS_FLAGS_STATE && SLJIT_HAS_STATUS_FLAGS_STATE)
|
||||
sljit_s32 status_flags_state;
|
||||
#endif /* SLJIT_HAS_STATUS_FLAGS_STATE */
|
||||
|
||||
#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
|
||||
sljit_s32 args_size;
|
||||
#endif /* SLJIT_CONFIG_X86_32 */
|
||||
|
||||
#if ((defined SLJIT_CONFIG_ARM_32 && SLJIT_CONFIG_ARM_32) && (defined __SOFTFP__)) \
|
||||
|| (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
|
||||
sljit_uw args_size;
|
||||
#endif /* (SLJIT_CONFIG_ARM_32 && __SOFTFP__) || SLJIT_CONFIG_MIPS_32 */
|
||||
|
||||
#if (defined SLJIT_CONFIG_ARM_V6 && SLJIT_CONFIG_ARM_V6)
|
||||
sljit_uw cpool_diff;
|
||||
sljit_uw cpool_fill;
|
||||
sljit_uw patches;
|
||||
#endif /* SLJIT_CONFIG_ARM_V6 */
|
||||
|
||||
#if (defined SLJIT_CONFIG_MIPS && SLJIT_CONFIG_MIPS)
|
||||
sljit_s32 delay_slot;
|
||||
#endif /* SLJIT_CONFIG_MIPS */
|
||||
|
||||
};
|
||||
|
||||
struct sljit_serialized_debug_info {
|
||||
sljit_sw last_flags;
|
||||
sljit_s32 last_return;
|
||||
sljit_s32 logical_local_size;
|
||||
};
|
||||
|
||||
struct sljit_serialized_label {
|
||||
sljit_uw size;
|
||||
};
|
||||
|
||||
struct sljit_serialized_jump {
|
||||
sljit_uw addr;
|
||||
sljit_uw flags;
|
||||
sljit_uw value;
|
||||
};
|
||||
|
||||
struct sljit_serialized_const {
|
||||
sljit_uw addr;
|
||||
};
|
||||
|
||||
#define SLJIT_SERIALIZE_ALIGN(v) (((v) + sizeof(sljit_uw) - 1) & ~(sljit_uw)(sizeof(sljit_uw) - 1))
|
||||
#if (defined SLJIT_LITTLE_ENDIAN && SLJIT_LITTLE_ENDIAN)
|
||||
#define SLJIT_SERIALIZE_SIGNATURE 0x534c4a54
|
||||
#else /* !SLJIT_LITTLE_ENDIAN */
|
||||
#define SLJIT_SERIALIZE_SIGNATURE 0x544a4c53
|
||||
#endif /* SLJIT_LITTLE_ENDIAN */
|
||||
#define SLJIT_SERIALIZE_VERSION 1
|
||||
|
||||
SLJIT_API_FUNC_ATTRIBUTE sljit_uw* sljit_serialize_compiler(struct sljit_compiler *compiler,
|
||||
sljit_s32 options, sljit_uw *size)
|
||||
{
|
||||
sljit_uw serialized_size = sizeof(struct sljit_serialized_compiler);
|
||||
struct sljit_memory_fragment *buf;
|
||||
struct sljit_label *label;
|
||||
struct sljit_jump *jump;
|
||||
struct sljit_const *const_;
|
||||
struct sljit_serialized_compiler *serialized_compiler;
|
||||
struct sljit_serialized_label *serialized_label;
|
||||
struct sljit_serialized_jump *serialized_jump;
|
||||
struct sljit_serialized_const *serialized_const;
|
||||
#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) \
|
||||
|| (defined SLJIT_DEBUG && SLJIT_DEBUG)
|
||||
struct sljit_serialized_debug_info *serialized_debug_info;
|
||||
#endif /* SLJIT_ARGUMENT_CHECKS || SLJIT_DEBUG */
|
||||
sljit_uw counter, used_size;
|
||||
sljit_u8 *result;
|
||||
sljit_u8 *ptr;
|
||||
SLJIT_UNUSED_ARG(options);
|
||||
|
||||
if (size != NULL)
|
||||
*size = 0;
|
||||
|
||||
PTR_FAIL_IF(compiler->error);
|
||||
|
||||
#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) \
|
||||
|| (defined SLJIT_DEBUG && SLJIT_DEBUG)
|
||||
if (!(options & SLJIT_SERIALIZE_IGNORE_DEBUG))
|
||||
serialized_size += sizeof(struct sljit_serialized_debug_info);
|
||||
#endif /* SLJIT_ARGUMENT_CHECKS || SLJIT_DEBUG */
|
||||
|
||||
#if (defined SLJIT_CONFIG_ARM_V6 && SLJIT_CONFIG_ARM_V6)
|
||||
serialized_size += SLJIT_SERIALIZE_ALIGN(compiler->cpool_fill * (sizeof(sljit_uw) + 1));
|
||||
#endif /* SLJIT_CONFIG_ARM_V6 */
|
||||
|
||||
/* Compute the size of the data. */
|
||||
buf = compiler->buf;
|
||||
while (buf != NULL) {
|
||||
serialized_size += sizeof(sljit_uw) + SLJIT_SERIALIZE_ALIGN(buf->used_size);
|
||||
buf = buf->next;
|
||||
}
|
||||
|
||||
serialized_size += compiler->label_count * sizeof(struct sljit_serialized_label);
|
||||
|
||||
jump = compiler->jumps;
|
||||
while (jump != NULL) {
|
||||
serialized_size += sizeof(struct sljit_serialized_jump);
|
||||
jump = jump->next;
|
||||
}
|
||||
|
||||
const_ = compiler->consts;
|
||||
while (const_ != NULL) {
|
||||
serialized_size += sizeof(struct sljit_serialized_const);
|
||||
const_ = const_->next;
|
||||
}
|
||||
|
||||
result = (sljit_u8*)SLJIT_MALLOC(serialized_size, compiler->allocator_data);
|
||||
PTR_FAIL_IF_NULL(result);
|
||||
|
||||
if (size != NULL)
|
||||
*size = serialized_size;
|
||||
|
||||
ptr = result;
|
||||
serialized_compiler = (struct sljit_serialized_compiler*)ptr;
|
||||
ptr += sizeof(struct sljit_serialized_compiler);
|
||||
|
||||
serialized_compiler->signature = SLJIT_SERIALIZE_SIGNATURE;
|
||||
serialized_compiler->version = SLJIT_SERIALIZE_VERSION;
|
||||
serialized_compiler->cpu_type = 0;
|
||||
serialized_compiler->label_count = compiler->label_count;
|
||||
serialized_compiler->options = compiler->options;
|
||||
serialized_compiler->scratches = compiler->scratches;
|
||||
serialized_compiler->saveds = compiler->saveds;
|
||||
serialized_compiler->fscratches = compiler->fscratches;
|
||||
serialized_compiler->fsaveds = compiler->fsaveds;
|
||||
serialized_compiler->local_size = compiler->local_size;
|
||||
serialized_compiler->size = compiler->size;
|
||||
|
||||
#if (defined SLJIT_HAS_STATUS_FLAGS_STATE && SLJIT_HAS_STATUS_FLAGS_STATE)
|
||||
serialized_compiler->status_flags_state = compiler->status_flags_state;
|
||||
#endif /* SLJIT_HAS_STATUS_FLAGS_STATE */
|
||||
|
||||
#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) \
|
||||
|| ((defined SLJIT_CONFIG_ARM_32 && SLJIT_CONFIG_ARM_32) && (defined __SOFTFP__)) \
|
||||
|| (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
|
||||
serialized_compiler->args_size = compiler->args_size;
|
||||
#endif /* SLJIT_CONFIG_X86_32 || (SLJIT_CONFIG_ARM_32 && __SOFTFP__) || SLJIT_CONFIG_MIPS_32 */
|
||||
|
||||
#if (defined SLJIT_CONFIG_ARM_V6 && SLJIT_CONFIG_ARM_V6)
|
||||
serialized_compiler->cpool_diff = compiler->cpool_diff;
|
||||
serialized_compiler->cpool_fill = compiler->cpool_fill;
|
||||
serialized_compiler->patches = compiler->patches;
|
||||
|
||||
SLJIT_MEMCPY(ptr, compiler->cpool, compiler->cpool_fill * sizeof(sljit_uw));
|
||||
SLJIT_MEMCPY(ptr + compiler->cpool_fill * sizeof(sljit_uw), compiler->cpool_unique, compiler->cpool_fill);
|
||||
ptr += SLJIT_SERIALIZE_ALIGN(compiler->cpool_fill * (sizeof(sljit_uw) + 1));
|
||||
#endif /* SLJIT_CONFIG_ARM_V6 */
|
||||
|
||||
#if (defined SLJIT_CONFIG_MIPS && SLJIT_CONFIG_MIPS)
|
||||
serialized_compiler->delay_slot = compiler->delay_slot;
|
||||
#endif /* SLJIT_CONFIG_MIPS */
|
||||
|
||||
buf = compiler->buf;
|
||||
counter = 0;
|
||||
while (buf != NULL) {
|
||||
used_size = buf->used_size;
|
||||
*(sljit_uw*)ptr = used_size;
|
||||
ptr += sizeof(sljit_uw);
|
||||
SLJIT_MEMCPY(ptr, buf->memory, used_size);
|
||||
ptr += SLJIT_SERIALIZE_ALIGN(used_size);
|
||||
buf = buf->next;
|
||||
counter++;
|
||||
}
|
||||
serialized_compiler->buf_segment_count = counter;
|
||||
|
||||
label = compiler->labels;
|
||||
while (label != NULL) {
|
||||
serialized_label = (struct sljit_serialized_label*)ptr;
|
||||
serialized_label->size = label->size;
|
||||
ptr += sizeof(struct sljit_serialized_label);
|
||||
label = label->next;
|
||||
}
|
||||
|
||||
jump = compiler->jumps;
|
||||
counter = 0;
|
||||
while (jump != NULL) {
|
||||
serialized_jump = (struct sljit_serialized_jump*)ptr;
|
||||
serialized_jump->addr = jump->addr;
|
||||
serialized_jump->flags = jump->flags;
|
||||
|
||||
if (jump->flags & JUMP_ADDR)
|
||||
serialized_jump->value = jump->u.target;
|
||||
else if (jump->u.label != NULL)
|
||||
serialized_jump->value = jump->u.label->u.index;
|
||||
else
|
||||
serialized_jump->value = SLJIT_MAX_ADDRESS;
|
||||
|
||||
ptr += sizeof(struct sljit_serialized_jump);
|
||||
jump = jump->next;
|
||||
counter++;
|
||||
}
|
||||
serialized_compiler->jump_count = counter;
|
||||
|
||||
const_ = compiler->consts;
|
||||
counter = 0;
|
||||
while (const_ != NULL) {
|
||||
serialized_const = (struct sljit_serialized_const*)ptr;
|
||||
serialized_const->addr = const_->addr;
|
||||
ptr += sizeof(struct sljit_serialized_const);
|
||||
const_ = const_->next;
|
||||
counter++;
|
||||
}
|
||||
serialized_compiler->const_count = counter;
|
||||
|
||||
#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) \
|
||||
|| (defined SLJIT_DEBUG && SLJIT_DEBUG)
|
||||
if (!(options & SLJIT_SERIALIZE_IGNORE_DEBUG)) {
|
||||
serialized_debug_info = (struct sljit_serialized_debug_info*)ptr;
|
||||
serialized_debug_info->last_flags = compiler->last_flags;
|
||||
serialized_debug_info->last_return = compiler->last_return;
|
||||
serialized_debug_info->logical_local_size = compiler->logical_local_size;
|
||||
serialized_compiler->cpu_type |= SLJIT_SERIALIZE_DEBUG;
|
||||
#if (defined SLJIT_DEBUG && SLJIT_DEBUG)
|
||||
ptr += sizeof(struct sljit_serialized_debug_info);
|
||||
#endif /* SLJIT_DEBUG */
|
||||
}
|
||||
#endif /* SLJIT_ARGUMENT_CHECKS || SLJIT_DEBUG */
|
||||
|
||||
SLJIT_ASSERT((sljit_uw)(ptr - result) == serialized_size);
|
||||
return (sljit_uw*)result;
|
||||
}
|
||||
|
||||
SLJIT_API_FUNC_ATTRIBUTE struct sljit_compiler *sljit_deserialize_compiler(sljit_uw* buffer, sljit_uw size,
|
||||
sljit_s32 options, void *allocator_data)
|
||||
{
|
||||
struct sljit_compiler *compiler;
|
||||
struct sljit_serialized_compiler *serialized_compiler;
|
||||
struct sljit_serialized_label *serialized_label;
|
||||
struct sljit_serialized_jump *serialized_jump;
|
||||
struct sljit_serialized_const *serialized_const;
|
||||
#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) \
|
||||
|| (defined SLJIT_DEBUG && SLJIT_DEBUG)
|
||||
struct sljit_serialized_debug_info *serialized_debug_info;
|
||||
#endif /* SLJIT_ARGUMENT_CHECKS || SLJIT_DEBUG */
|
||||
struct sljit_memory_fragment *buf;
|
||||
struct sljit_memory_fragment *last_buf;
|
||||
struct sljit_label *label;
|
||||
struct sljit_label *last_label;
|
||||
struct sljit_label **label_list = NULL;
|
||||
struct sljit_jump *jump;
|
||||
struct sljit_jump *last_jump;
|
||||
struct sljit_const *const_;
|
||||
struct sljit_const *last_const;
|
||||
sljit_u8 *ptr = (sljit_u8*)buffer;
|
||||
sljit_u8 *end = ptr + size;
|
||||
sljit_uw i, used_size, aligned_size, label_count;
|
||||
SLJIT_UNUSED_ARG(options);
|
||||
|
||||
if (size < sizeof(struct sljit_serialized_compiler) || (size & (sizeof(sljit_uw) - 1)) != 0)
|
||||
return NULL;
|
||||
|
||||
serialized_compiler = (struct sljit_serialized_compiler*)ptr;
|
||||
|
||||
if (serialized_compiler->signature != SLJIT_SERIALIZE_SIGNATURE || serialized_compiler->version != SLJIT_SERIALIZE_VERSION)
|
||||
return NULL;
|
||||
|
||||
compiler = sljit_create_compiler(allocator_data);
|
||||
PTR_FAIL_IF(compiler == NULL);
|
||||
|
||||
compiler->label_count = serialized_compiler->label_count;
|
||||
compiler->options = serialized_compiler->options;
|
||||
compiler->scratches = serialized_compiler->scratches;
|
||||
compiler->saveds = serialized_compiler->saveds;
|
||||
compiler->fscratches = serialized_compiler->fscratches;
|
||||
compiler->fsaveds = serialized_compiler->fsaveds;
|
||||
compiler->local_size = serialized_compiler->local_size;
|
||||
compiler->size = serialized_compiler->size;
|
||||
|
||||
#if (defined SLJIT_HAS_STATUS_FLAGS_STATE && SLJIT_HAS_STATUS_FLAGS_STATE)
|
||||
compiler->status_flags_state = serialized_compiler->status_flags_state;
|
||||
#endif /* SLJIT_HAS_STATUS_FLAGS_STATE */
|
||||
|
||||
#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) \
|
||||
|| ((defined SLJIT_CONFIG_ARM_32 && SLJIT_CONFIG_ARM_32) && (defined __SOFTFP__)) \
|
||||
|| (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
|
||||
compiler->args_size = serialized_compiler->args_size;
|
||||
#endif /* SLJIT_CONFIG_X86_32 || (SLJIT_CONFIG_ARM_32 && __SOFTFP__) || SLJIT_CONFIG_MIPS_32 */
|
||||
|
||||
#if (defined SLJIT_CONFIG_ARM_V6 && SLJIT_CONFIG_ARM_V6)
|
||||
used_size = serialized_compiler->cpool_fill;
|
||||
aligned_size = SLJIT_SERIALIZE_ALIGN(used_size * (sizeof(sljit_uw) + 1));
|
||||
compiler->cpool_diff = serialized_compiler->cpool_diff;
|
||||
compiler->cpool_fill = used_size;
|
||||
compiler->patches = serialized_compiler->patches;
|
||||
|
||||
if ((sljit_uw)(end - ptr) < aligned_size)
|
||||
goto error;
|
||||
|
||||
SLJIT_MEMCPY(compiler->cpool, ptr, used_size * sizeof(sljit_uw));
|
||||
SLJIT_MEMCPY(compiler->cpool_unique, ptr + used_size * sizeof(sljit_uw), used_size);
|
||||
ptr += aligned_size;
|
||||
#endif /* SLJIT_CONFIG_ARM_V6 */
|
||||
|
||||
#if (defined SLJIT_CONFIG_MIPS && SLJIT_CONFIG_MIPS)
|
||||
compiler->delay_slot = serialized_compiler->delay_slot;
|
||||
#endif /* SLJIT_CONFIG_MIPS */
|
||||
|
||||
#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) \
|
||||
|| (defined SLJIT_DEBUG && SLJIT_DEBUG)
|
||||
if (!(serialized_compiler->cpu_type & SLJIT_SERIALIZE_DEBUG))
|
||||
goto error;
|
||||
#endif /* SLJIT_ARGUMENT_CHECKS || SLJIT_DEBUG */
|
||||
|
||||
ptr += sizeof(struct sljit_serialized_compiler);
|
||||
i = serialized_compiler->buf_segment_count;
|
||||
last_buf = NULL;
|
||||
while (i > 0) {
|
||||
if ((sljit_uw)(end - ptr) < sizeof(sljit_uw))
|
||||
goto error;
|
||||
|
||||
used_size = *(sljit_uw*)ptr;
|
||||
aligned_size = SLJIT_SERIALIZE_ALIGN(used_size);
|
||||
ptr += sizeof(sljit_uw);
|
||||
|
||||
if ((sljit_uw)(end - ptr) < aligned_size)
|
||||
goto error;
|
||||
|
||||
if (last_buf == NULL) {
|
||||
SLJIT_ASSERT(compiler->buf != NULL && compiler->buf->next == NULL);
|
||||
buf = compiler->buf;
|
||||
} else {
|
||||
buf = (struct sljit_memory_fragment*)SLJIT_MALLOC(BUF_SIZE, allocator_data);
|
||||
if (!buf)
|
||||
goto error;
|
||||
buf->next = NULL;
|
||||
}
|
||||
|
||||
buf->used_size = used_size;
|
||||
SLJIT_MEMCPY(buf->memory, ptr, used_size);
|
||||
|
||||
if (last_buf != NULL)
|
||||
last_buf->next = buf;
|
||||
last_buf = buf;
|
||||
|
||||
ptr += aligned_size;
|
||||
i--;
|
||||
}
|
||||
|
||||
last_label = NULL;
|
||||
label_count = serialized_compiler->label_count;
|
||||
if ((sljit_uw)(end - ptr) < label_count * sizeof(struct sljit_serialized_label))
|
||||
goto error;
|
||||
|
||||
label_list = (struct sljit_label **)SLJIT_MALLOC(label_count * sizeof(struct sljit_label*), allocator_data);
|
||||
if (label_list == NULL)
|
||||
goto error;
|
||||
|
||||
for (i = 0; i < label_count; i++) {
|
||||
label = (struct sljit_label*)ensure_abuf(compiler, sizeof(struct sljit_label));
|
||||
if (label == NULL)
|
||||
goto error;
|
||||
|
||||
serialized_label = (struct sljit_serialized_label*)ptr;
|
||||
label->next = NULL;
|
||||
label->u.index = i;
|
||||
label->size = serialized_label->size;
|
||||
|
||||
if (last_label != NULL)
|
||||
last_label->next = label;
|
||||
else
|
||||
compiler->labels = label;
|
||||
last_label = label;
|
||||
|
||||
label_list[i] = label;
|
||||
ptr += sizeof(struct sljit_serialized_label);
|
||||
}
|
||||
compiler->last_label = last_label;
|
||||
|
||||
last_jump = NULL;
|
||||
i = serialized_compiler->jump_count;
|
||||
if ((sljit_uw)(end - ptr) < i * sizeof(struct sljit_serialized_jump))
|
||||
goto error;
|
||||
|
||||
while (i > 0) {
|
||||
jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump));
|
||||
if (jump == NULL)
|
||||
goto error;
|
||||
|
||||
serialized_jump = (struct sljit_serialized_jump*)ptr;
|
||||
jump->next = NULL;
|
||||
jump->addr = serialized_jump->addr;
|
||||
jump->flags = serialized_jump->flags;
|
||||
|
||||
if (!(serialized_jump->flags & JUMP_ADDR)) {
|
||||
if (serialized_jump->value != SLJIT_MAX_ADDRESS) {
|
||||
if (serialized_jump->value >= label_count)
|
||||
goto error;
|
||||
jump->u.label = label_list[serialized_jump->value];
|
||||
} else
|
||||
jump->u.label = NULL;
|
||||
} else
|
||||
jump->u.target = serialized_jump->value;
|
||||
|
||||
if (last_jump != NULL)
|
||||
last_jump->next = jump;
|
||||
else
|
||||
compiler->jumps = jump;
|
||||
last_jump = jump;
|
||||
|
||||
ptr += sizeof(struct sljit_serialized_jump);
|
||||
i--;
|
||||
}
|
||||
compiler->last_jump = last_jump;
|
||||
|
||||
SLJIT_FREE(label_list, allocator_data);
|
||||
label_list = NULL;
|
||||
|
||||
last_const = NULL;
|
||||
i = serialized_compiler->const_count;
|
||||
if ((sljit_uw)(end - ptr) < i * sizeof(struct sljit_serialized_const))
|
||||
goto error;
|
||||
|
||||
while (i > 0) {
|
||||
const_ = (struct sljit_const*)ensure_abuf(compiler, sizeof(struct sljit_const));
|
||||
if (const_ == NULL)
|
||||
goto error;
|
||||
|
||||
serialized_const = (struct sljit_serialized_const*)ptr;
|
||||
const_->next = NULL;
|
||||
const_->addr = serialized_const->addr;
|
||||
|
||||
if (last_const != NULL)
|
||||
last_const->next = const_;
|
||||
else
|
||||
compiler->consts = const_;
|
||||
last_const = const_;
|
||||
|
||||
ptr += sizeof(struct sljit_serialized_const);
|
||||
i--;
|
||||
}
|
||||
compiler->last_const = last_const;
|
||||
|
||||
#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) \
|
||||
|| (defined SLJIT_DEBUG && SLJIT_DEBUG)
|
||||
if ((sljit_uw)(end - ptr) < sizeof(struct sljit_serialized_debug_info))
|
||||
goto error;
|
||||
|
||||
serialized_debug_info = (struct sljit_serialized_debug_info*)ptr;
|
||||
compiler->last_flags = (sljit_s32)serialized_debug_info->last_flags;
|
||||
compiler->last_return = serialized_debug_info->last_return;
|
||||
compiler->logical_local_size = serialized_debug_info->logical_local_size;
|
||||
#endif /* SLJIT_ARGUMENT_CHECKS || SLJIT_DEBUG */
|
||||
|
||||
return compiler;
|
||||
|
||||
error:
|
||||
sljit_free_compiler(compiler);
|
||||
if (label_list != NULL)
|
||||
SLJIT_FREE(label_list, allocator_data);
|
||||
return NULL;
|
||||
}
|
||||
@@ -1,344 +0,0 @@
|
||||
/*
|
||||
* Stack-less Just-In-Time compiler
|
||||
*
|
||||
* Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
/* Locks */
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
/* Executable Allocator */
|
||||
|
||||
#if (defined SLJIT_EXECUTABLE_ALLOCATOR && SLJIT_EXECUTABLE_ALLOCATOR) \
|
||||
&& !(defined SLJIT_WX_EXECUTABLE_ALLOCATOR && SLJIT_WX_EXECUTABLE_ALLOCATOR)
|
||||
#if (defined SLJIT_SINGLE_THREADED && SLJIT_SINGLE_THREADED)
|
||||
#define SLJIT_ALLOCATOR_LOCK()
|
||||
#define SLJIT_ALLOCATOR_UNLOCK()
|
||||
#elif !(defined _WIN32)
|
||||
#include <pthread.h>
|
||||
|
||||
static pthread_mutex_t allocator_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
#define SLJIT_ALLOCATOR_LOCK() pthread_mutex_lock(&allocator_lock)
|
||||
#define SLJIT_ALLOCATOR_UNLOCK() pthread_mutex_unlock(&allocator_lock)
|
||||
#else /* windows */
|
||||
static HANDLE allocator_lock;
|
||||
|
||||
static SLJIT_INLINE void allocator_grab_lock(void)
|
||||
{
|
||||
HANDLE lock;
|
||||
if (SLJIT_UNLIKELY(!InterlockedCompareExchangePointer(&allocator_lock, NULL, NULL))) {
|
||||
lock = CreateMutex(NULL, FALSE, NULL);
|
||||
if (InterlockedCompareExchangePointer(&allocator_lock, lock, NULL))
|
||||
CloseHandle(lock);
|
||||
}
|
||||
WaitForSingleObject(allocator_lock, INFINITE);
|
||||
}
|
||||
|
||||
#define SLJIT_ALLOCATOR_LOCK() allocator_grab_lock()
|
||||
#define SLJIT_ALLOCATOR_UNLOCK() ReleaseMutex(allocator_lock)
|
||||
#endif /* thread implementation */
|
||||
#endif /* SLJIT_EXECUTABLE_ALLOCATOR && !SLJIT_WX_EXECUTABLE_ALLOCATOR */
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
/* Stack */
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
#if ((defined SLJIT_UTIL_STACK && SLJIT_UTIL_STACK) \
|
||||
&& !(defined SLJIT_UTIL_SIMPLE_STACK_ALLOCATION && SLJIT_UTIL_SIMPLE_STACK_ALLOCATION)) \
|
||||
|| ((defined SLJIT_EXECUTABLE_ALLOCATOR && SLJIT_EXECUTABLE_ALLOCATOR) \
|
||||
&& !((defined SLJIT_PROT_EXECUTABLE_ALLOCATOR && SLJIT_PROT_EXECUTABLE_ALLOCATOR) \
|
||||
|| (defined SLJIT_WX_EXECUTABLE_ALLOCATOR && SLJIT_WX_EXECUTABLE_ALLOCATOR)))
|
||||
|
||||
#ifndef _WIN32
|
||||
/* Provides mmap function. */
|
||||
#include <sys/types.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#ifndef MAP_ANON
|
||||
#ifdef MAP_ANONYMOUS
|
||||
#define MAP_ANON MAP_ANONYMOUS
|
||||
#endif /* MAP_ANONYMOUS */
|
||||
#endif /* !MAP_ANON */
|
||||
|
||||
#ifndef MAP_ANON
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
#ifdef O_CLOEXEC
|
||||
#define SLJIT_CLOEXEC O_CLOEXEC
|
||||
#else /* !O_CLOEXEC */
|
||||
#define SLJIT_CLOEXEC 0
|
||||
#endif /* O_CLOEXEC */
|
||||
|
||||
/* Some old systems do not have MAP_ANON. */
|
||||
static int dev_zero = -1;
|
||||
|
||||
#if (defined SLJIT_SINGLE_THREADED && SLJIT_SINGLE_THREADED)
|
||||
|
||||
static SLJIT_INLINE int open_dev_zero(void)
|
||||
{
|
||||
dev_zero = open("/dev/zero", O_RDWR | SLJIT_CLOEXEC);
|
||||
|
||||
return dev_zero < 0;
|
||||
}
|
||||
|
||||
#else /* !SLJIT_SINGLE_THREADED */
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
static pthread_mutex_t dev_zero_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
static SLJIT_INLINE int open_dev_zero(void)
|
||||
{
|
||||
pthread_mutex_lock(&dev_zero_mutex);
|
||||
if (SLJIT_UNLIKELY(dev_zero < 0))
|
||||
dev_zero = open("/dev/zero", O_RDWR | SLJIT_CLOEXEC);
|
||||
|
||||
pthread_mutex_unlock(&dev_zero_mutex);
|
||||
return dev_zero < 0;
|
||||
}
|
||||
|
||||
#endif /* SLJIT_SINGLE_THREADED */
|
||||
#undef SLJIT_CLOEXEC
|
||||
#endif /* !MAP_ANON */
|
||||
#endif /* !_WIN32 */
|
||||
#endif /* open_dev_zero */
|
||||
|
||||
#if (defined SLJIT_UTIL_STACK && SLJIT_UTIL_STACK) \
|
||||
|| (defined SLJIT_EXECUTABLE_ALLOCATOR && SLJIT_EXECUTABLE_ALLOCATOR)
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
static SLJIT_INLINE sljit_uw get_page_alignment(void) {
|
||||
SYSTEM_INFO si;
|
||||
static sljit_uw sljit_page_align = 0;
|
||||
if (!sljit_page_align) {
|
||||
GetSystemInfo(&si);
|
||||
sljit_page_align = (sljit_uw)si.dwPageSize - 1;
|
||||
}
|
||||
return sljit_page_align;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
static SLJIT_INLINE sljit_uw get_page_alignment(void) {
|
||||
static sljit_uw sljit_page_align = 0;
|
||||
|
||||
sljit_sw align;
|
||||
|
||||
if (!sljit_page_align) {
|
||||
#ifdef _SC_PAGESIZE
|
||||
align = sysconf(_SC_PAGESIZE);
|
||||
#else
|
||||
align = getpagesize();
|
||||
#endif
|
||||
/* Should never happen. */
|
||||
if (align < 0)
|
||||
align = 4096;
|
||||
sljit_page_align = (sljit_uw)align - 1;
|
||||
}
|
||||
return sljit_page_align;
|
||||
}
|
||||
|
||||
#endif /* _WIN32 */
|
||||
|
||||
#endif /* get_page_alignment() */
|
||||
|
||||
#if (defined SLJIT_UTIL_STACK && SLJIT_UTIL_STACK)
|
||||
|
||||
#if (defined SLJIT_UTIL_SIMPLE_STACK_ALLOCATION && SLJIT_UTIL_SIMPLE_STACK_ALLOCATION)
|
||||
|
||||
SLJIT_API_FUNC_ATTRIBUTE struct sljit_stack* SLJIT_FUNC sljit_allocate_stack(sljit_uw start_size, sljit_uw max_size, void *allocator_data)
|
||||
{
|
||||
struct sljit_stack *stack;
|
||||
void *ptr;
|
||||
|
||||
SLJIT_UNUSED_ARG(allocator_data);
|
||||
|
||||
if (start_size > max_size || start_size < 1)
|
||||
return NULL;
|
||||
|
||||
stack = (struct sljit_stack*)SLJIT_MALLOC(sizeof(struct sljit_stack), allocator_data);
|
||||
if (stack == NULL)
|
||||
return NULL;
|
||||
|
||||
ptr = SLJIT_MALLOC(max_size, allocator_data);
|
||||
if (ptr == NULL) {
|
||||
SLJIT_FREE(stack, allocator_data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
stack->min_start = (sljit_u8 *)ptr;
|
||||
stack->end = stack->min_start + max_size;
|
||||
stack->start = stack->end - start_size;
|
||||
stack->top = stack->end;
|
||||
return stack;
|
||||
}
|
||||
|
||||
SLJIT_API_FUNC_ATTRIBUTE void SLJIT_FUNC sljit_free_stack(struct sljit_stack *stack, void *allocator_data)
|
||||
{
|
||||
SLJIT_UNUSED_ARG(allocator_data);
|
||||
SLJIT_FREE((void*)stack->min_start, allocator_data);
|
||||
SLJIT_FREE(stack, allocator_data);
|
||||
}
|
||||
|
||||
SLJIT_API_FUNC_ATTRIBUTE sljit_u8 *SLJIT_FUNC sljit_stack_resize(struct sljit_stack *stack, sljit_u8 *new_start)
|
||||
{
|
||||
if ((new_start < stack->min_start) || (new_start >= stack->end))
|
||||
return NULL;
|
||||
stack->start = new_start;
|
||||
return new_start;
|
||||
}
|
||||
|
||||
#else /* !SLJIT_UTIL_SIMPLE_STACK_ALLOCATION */
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
SLJIT_API_FUNC_ATTRIBUTE void SLJIT_FUNC sljit_free_stack(struct sljit_stack *stack, void *allocator_data)
|
||||
{
|
||||
SLJIT_UNUSED_ARG(allocator_data);
|
||||
VirtualFree((void*)stack->min_start, 0, MEM_RELEASE);
|
||||
SLJIT_FREE(stack, allocator_data);
|
||||
}
|
||||
|
||||
#else /* !_WIN32 */
|
||||
|
||||
SLJIT_API_FUNC_ATTRIBUTE void SLJIT_FUNC sljit_free_stack(struct sljit_stack *stack, void *allocator_data)
|
||||
{
|
||||
SLJIT_UNUSED_ARG(allocator_data);
|
||||
munmap((void*)stack->min_start, (size_t)(stack->end - stack->min_start));
|
||||
SLJIT_FREE(stack, allocator_data);
|
||||
}
|
||||
|
||||
#endif /* _WIN32 */
|
||||
|
||||
SLJIT_API_FUNC_ATTRIBUTE struct sljit_stack* SLJIT_FUNC sljit_allocate_stack(sljit_uw start_size, sljit_uw max_size, void *allocator_data)
|
||||
{
|
||||
struct sljit_stack *stack;
|
||||
void *ptr;
|
||||
sljit_uw page_align;
|
||||
|
||||
SLJIT_UNUSED_ARG(allocator_data);
|
||||
|
||||
if (start_size > max_size || start_size < 1)
|
||||
return NULL;
|
||||
|
||||
stack = (struct sljit_stack*)SLJIT_MALLOC(sizeof(struct sljit_stack), allocator_data);
|
||||
if (stack == NULL)
|
||||
return NULL;
|
||||
|
||||
/* Align max_size. */
|
||||
page_align = get_page_alignment();
|
||||
max_size = (max_size + page_align) & ~page_align;
|
||||
|
||||
#ifdef _WIN32
|
||||
ptr = VirtualAlloc(NULL, max_size, MEM_RESERVE, PAGE_READWRITE);
|
||||
if (!ptr) {
|
||||
SLJIT_FREE(stack, allocator_data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
stack->min_start = (sljit_u8 *)ptr;
|
||||
stack->end = stack->min_start + max_size;
|
||||
stack->start = stack->end;
|
||||
|
||||
if (sljit_stack_resize(stack, stack->end - start_size) == NULL) {
|
||||
sljit_free_stack(stack, allocator_data);
|
||||
return NULL;
|
||||
}
|
||||
#else /* !_WIN32 */
|
||||
#ifdef MAP_ANON
|
||||
ptr = mmap(NULL, max_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
|
||||
#else /* !MAP_ANON */
|
||||
if (SLJIT_UNLIKELY((dev_zero < 0) && open_dev_zero())) {
|
||||
SLJIT_FREE(stack, allocator_data);
|
||||
return NULL;
|
||||
}
|
||||
ptr = mmap(NULL, max_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, dev_zero, 0);
|
||||
#endif /* MAP_ANON */
|
||||
if (ptr == MAP_FAILED) {
|
||||
SLJIT_FREE(stack, allocator_data);
|
||||
return NULL;
|
||||
}
|
||||
stack->min_start = (sljit_u8 *)ptr;
|
||||
stack->end = stack->min_start + max_size;
|
||||
stack->start = stack->end - start_size;
|
||||
#endif /* _WIN32 */
|
||||
|
||||
stack->top = stack->end;
|
||||
return stack;
|
||||
}
|
||||
|
||||
SLJIT_API_FUNC_ATTRIBUTE sljit_u8 *SLJIT_FUNC sljit_stack_resize(struct sljit_stack *stack, sljit_u8 *new_start)
|
||||
{
|
||||
#if defined _WIN32 || defined(POSIX_MADV_DONTNEED)
|
||||
sljit_uw aligned_old_start;
|
||||
sljit_uw aligned_new_start;
|
||||
sljit_uw page_align;
|
||||
#endif
|
||||
|
||||
if ((new_start < stack->min_start) || (new_start >= stack->end))
|
||||
return NULL;
|
||||
|
||||
#ifdef _WIN32
|
||||
page_align = get_page_alignment();
|
||||
|
||||
aligned_new_start = (sljit_uw)new_start & ~page_align;
|
||||
aligned_old_start = ((sljit_uw)stack->start) & ~page_align;
|
||||
if (aligned_new_start != aligned_old_start) {
|
||||
if (aligned_new_start < aligned_old_start) {
|
||||
if (!VirtualAlloc((void*)aligned_new_start, aligned_old_start - aligned_new_start, MEM_COMMIT, PAGE_READWRITE))
|
||||
return NULL;
|
||||
}
|
||||
else {
|
||||
if (!VirtualFree((void*)aligned_old_start, aligned_new_start - aligned_old_start, MEM_DECOMMIT))
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
#elif defined(POSIX_MADV_DONTNEED)
|
||||
if (stack->start < new_start) {
|
||||
page_align = get_page_alignment();
|
||||
|
||||
aligned_new_start = (sljit_uw)new_start & ~page_align;
|
||||
aligned_old_start = ((sljit_uw)stack->start) & ~page_align;
|
||||
|
||||
if (aligned_new_start > aligned_old_start) {
|
||||
posix_madvise((void*)aligned_old_start, aligned_new_start - aligned_old_start, POSIX_MADV_DONTNEED);
|
||||
#ifdef MADV_FREE
|
||||
madvise((void*)aligned_old_start, aligned_new_start - aligned_old_start, MADV_FREE);
|
||||
#endif /* MADV_FREE */
|
||||
}
|
||||
}
|
||||
#endif /* _WIN32 */
|
||||
|
||||
stack->start = new_start;
|
||||
return new_start;
|
||||
}
|
||||
|
||||
#endif /* SLJIT_UTIL_SIMPLE_STACK_ALLOCATION */
|
||||
|
||||
#endif /* SLJIT_UTIL_STACK */
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "code_generator.h"
|
||||
#include "control_flow.h"
|
||||
// Shared widescreen backdrop-window detector (single source of truth across the
|
||||
// recompiler, the interpreter, and the sljit emitter). Self-contained C header;
|
||||
// recompiler and the interpreter). Self-contained C header;
|
||||
// included via relative path to avoid an include-dir collision (recompiler and
|
||||
// runtime both ship a gte.h).
|
||||
#include "../../runtime/include/ws_backdrop_detect.h"
|
||||
@@ -904,7 +904,7 @@ std::string CodeGenerator::translate_instruction(uint32_t addr, uint32_t instr)
|
||||
uint32_t rs = get_rs(instr), rt = get_rt(instr);
|
||||
if (opcode == 0x0B) {
|
||||
// Route through the shared runtime helper psx_ws_cull_sltiu so the
|
||||
// gcc emit, the sljit JIT, and the interpreter all widen identically.
|
||||
// gcc emit and the interpreter both widen identically.
|
||||
// It sign-extends SX and shifts by +margin (wide window
|
||||
// -margin <= SX < imm+margin, both edges); at margin=0 it reduces
|
||||
// bit-for-bit to the vanilla `SX <u imm` verdict (4:3 byte-identical).
|
||||
|
||||
@@ -177,8 +177,9 @@ struct RuntimeConfig {
|
||||
std::string overlay_autocompile_cmd_tcc;
|
||||
|
||||
// overlay_backend: overlay tier-selection (overlay_backend.h).
|
||||
// "auto" (default, empty == auto) | "gcc" | "tcc" | "auto-no-gcc" | "sljit"
|
||||
// (deprecated). auto resolves to gcc when a gcc TOOLCHAIN is present (a dev /
|
||||
// "auto" (default, empty == auto) | "gcc" | "tcc" | "auto-no-gcc". ("sljit"
|
||||
// was removed 2026-07-15; a stale value degrades to auto.) auto resolves to
|
||||
// gcc when a gcc TOOLCHAIN is present (a dev /
|
||||
// production box), else tcc (bundled, toolchain-free). "auto-no-gcc" forces
|
||||
// the tcc branch even with gcc present (simulate a toolchain-less user box;
|
||||
// gcc shards still load). The env var PSX_OVERLAY_BACKEND overrides at runtime.
|
||||
|
||||
@@ -1654,8 +1654,8 @@ void FullFunctionEmitter::emit_dispatch(
|
||||
|
||||
if (cps) {
|
||||
// RECURSION_BUG.md §25 — mark CPS mode at startup for runtime code that
|
||||
// must emit the CPS contract (the overlay sljit JIT, overlay_sljit.c).
|
||||
out += "\n/* CPS runtime-mode marker (overlay sljit JIT reads g_psx_cps_mode). */\n";
|
||||
// routes CPS continuations (overlay_loader.c).
|
||||
out += "\n/* CPS runtime-mode marker (the overlay loader reads g_psx_cps_mode). */\n";
|
||||
out += "static void psx_cps_mark_bios(void) {\n";
|
||||
out += " extern int g_psx_cps_mode; g_psx_cps_mode = 1;\n";
|
||||
out += "}\n";
|
||||
|
||||
@@ -1132,10 +1132,10 @@ int main(int argc, char** argv) {
|
||||
ds << "}\n";
|
||||
|
||||
if (codegen.cps_enabled()) {
|
||||
// RECURSION_BUG.md §25 — mark CPS mode at startup so the overlay sljit
|
||||
// JIT (overlay_sljit.c) emits the CPS contract. Static ctor: no clash
|
||||
// with the BIOS dispatch's marker.
|
||||
ds << "\n/* CPS runtime-mode marker (overlay sljit JIT reads g_psx_cps_mode). */\n";
|
||||
// RECURSION_BUG.md §25 — mark CPS mode at startup so runtime code that
|
||||
// routes CPS continuations (overlay_loader.c) sees the contract.
|
||||
// Static ctor: no clash with the BIOS dispatch's marker.
|
||||
ds << "\n/* CPS runtime-mode marker (the overlay loader reads g_psx_cps_mode). */\n";
|
||||
ds << "static void psx_cps_mark_game(void) {\n";
|
||||
ds << " extern int g_psx_cps_mode; g_psx_cps_mode = 1;\n";
|
||||
ds << "}\n";
|
||||
|
||||
@@ -30,7 +30,7 @@ void autocompile_set_cache_paths(const char *cache_dir, const char *captures);
|
||||
* REAL "developer machine" signal, distinct from autocompile_configured() which
|
||||
* only reports that a command STRING is set (the shipped game.toml always sets
|
||||
* one). overlay_backend_resolve uses this so `auto` picks gcc only when the
|
||||
* toolchain can really build a shard, else sljit (toolchain-less production).
|
||||
* toolchain can really build a shard, else tcc (toolchain-less production).
|
||||
* Memoized; safe to call repeatedly. */
|
||||
int autocompile_toolchain_available(void);
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ extern "C" {
|
||||
* Every mutable hardware subsystem gets a section here, and the capture is
|
||||
* proven complete by diffing a restored session's frames against a normal-boot
|
||||
* session's frames (see the "bootsnap" debug command). Host-side / recompiler-
|
||||
* derived state (dirty-RAM bitmap, overlay/sljit tables, debug rings) is NOT
|
||||
* derived state (dirty-RAM bitmap, overlay tables, debug rings) is NOT
|
||||
* serialized — it is re-derived from restored guest RAM on load.
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,28 +1,21 @@
|
||||
/* code_provider.h — the backend-agnostic code-production seam (SLJIT.md §3).
|
||||
/* code_provider.h — the backend-agnostic code-production seam.
|
||||
*
|
||||
* One interface the overlay capture/dispatch spine talks to instead of calling
|
||||
* a specific backend directly. The validated gcc spawn->DLL path becomes its
|
||||
* first implementation; the in-process sljit JIT (SLJIT.md §7 step 4) becomes
|
||||
* the second. Everything that makes overlays correct — content-keyed multi-
|
||||
* candidate dispatch, per-call live-byte validation, the self-mod blacklist,
|
||||
* the coverage manifest — is unchanged and backend-blind (SLJIT.md §2).
|
||||
* a specific backend directly. The validated gcc/tcc spawn->DLL->rescan path is
|
||||
* its implementation. Everything that makes overlays correct — content-keyed
|
||||
* multi-candidate dispatch, per-call live-byte validation, the self-mod
|
||||
* blacklist, the coverage manifest — is backend-blind.
|
||||
*
|
||||
* Two production lifecycles, BOTH expressed here because the two backends model
|
||||
* production differently and a provider implements whichever it supports:
|
||||
* Production is BATCH / ASYNC (request / busy / poll_main): kicked by the
|
||||
* autocapture tick, spawns a compiler off-thread, applied later via a cache
|
||||
* rescan on the emu thread. Seconds of latency; many fragments per run. A
|
||||
* provider sets the hooks it supports and leaves the rest NULL; callers
|
||||
* null-check before invoking.
|
||||
*
|
||||
* - BATCH / ASYNC (request / busy / poll_main): the gcc model. Kicked by the
|
||||
* autocapture tick, spawns a compiler off-thread, applied later via a cache
|
||||
* rescan on the emu thread. Seconds of latency; many fragments per run.
|
||||
* - SYNC / ON-MISS (compile_fragment): the sljit model. JIT a single fragment
|
||||
* in-process at a dispatch miss, sub-ms, return a native fn the caller
|
||||
* registers immediately. Wired into the dispatch path (overlay_loader.c
|
||||
* try_sljit_region); the sljit shard is then VALIDATED via the same-state
|
||||
* diff before it runs live (see SLJIT.md §11 / overlay_loader_dispatch).
|
||||
*
|
||||
* A provider sets the hooks it supports and leaves the rest NULL; callers
|
||||
* null-check before invoking. The gcc provider's batch hooks are thin pass-
|
||||
* throughs to autocompile_* — behavior is byte-for-byte what it was before the
|
||||
* abstraction (this is a pure refactor; selection defaults to gcc).
|
||||
* (The in-process sljit JIT provider — a synchronous on-miss producer with a
|
||||
* compile_fragment hook — was removed 2026-07-15; it had been disabled by
|
||||
* default since 2026-06-25 due to an emitter bug. Gaps gcc/tcc have not yet
|
||||
* covered fall to the interpreter, the always-safe precision-over-recall floor.)
|
||||
*/
|
||||
#ifndef PSXRECOMP_CODE_PROVIDER_H
|
||||
#define PSXRECOMP_CODE_PROVIDER_H
|
||||
@@ -34,29 +27,12 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Native shard ABI — identical to overlay_loader's OverlayFn / OverlaySljitFn. */
|
||||
/* Native shard ABI — identical to overlay_loader's OverlayFn. */
|
||||
typedef void (*CodeProviderFn)(CPUState *);
|
||||
|
||||
/* Result of a synchronous on-miss compile. fn == NULL means the provider
|
||||
* declined (caller runs the interpreter). On success [code_lo, code_lo+code_len)
|
||||
* is the PHYS byte-range the fragment was compiled from — the caller registers a
|
||||
* candidate over it for per-call live-byte validation (the backend-neutral
|
||||
* equivalent of the gcc .ranges manifest). */
|
||||
typedef struct {
|
||||
CodeProviderFn fn; /* NULL = declined */
|
||||
uint32_t code_lo; /* phys start of compiled code range */
|
||||
uint32_t code_len; /* byte length of compiled code range */
|
||||
/* sljit: position-independent serialized LIR for the persisted shard cache
|
||||
* (the caller writes it to disk then frees it via the sljit free helper);
|
||||
* NULL for providers/results that don't serialize. */
|
||||
void *serialized;
|
||||
unsigned long serialized_size;
|
||||
} CompiledFragment;
|
||||
|
||||
typedef struct CodeProvider {
|
||||
const char *name; /* "gcc" | "sljit" */
|
||||
int (*available)(void); /* gcc: a compile cmd is configured;
|
||||
sljit: always (no external toolchain) */
|
||||
const char *name; /* "gcc" (spawns gcc OR tcc) */
|
||||
int (*available)(void); /* a compile cmd is configured */
|
||||
|
||||
/* Batch/async production (autocapture-driven). request() returns 1 if a
|
||||
* compile was started. busy() is 1 while one is in flight. poll_main() runs
|
||||
@@ -65,36 +41,19 @@ typedef struct CodeProvider {
|
||||
int (*request)(void);
|
||||
int (*busy)(void);
|
||||
void (*poll_main)(void);
|
||||
|
||||
/* Synchronous on-miss production of one fragment. Fills *out (out->fn NULL =
|
||||
* declined → caller runs the interpreter, the always-safe precision-over-
|
||||
* recall floor). gcc leaves this hook NULL (a compiler spawn is far too slow
|
||||
* for the dispatch path); sljit JITs in-process. A NULL hook == always
|
||||
* declines. `bytes`/`size`/`image_base_vram` describe the image to decode
|
||||
* from (typically live RAM: bytes = RAM base, image_base_vram = 0). */
|
||||
void (*compile_fragment)(uint32_t entry, const uint8_t *bytes, uint32_t size,
|
||||
uint32_t image_base_vram, CompiledFragment *out);
|
||||
} CodeProvider;
|
||||
|
||||
/* Resolve the active backend (overlay_backend_resolve) and cache the matching
|
||||
* provider. cfg_backend = [runtime] overlay_backend ("auto"|"gcc"|"sljit", may
|
||||
* be NULL/empty for auto); gcc_configured = autocompile_configured(). Call once
|
||||
* at overlay-cache init, on the emu thread, before the run loop starts. */
|
||||
* provider. cfg_backend = [runtime] overlay_backend ("auto"|"gcc"|"tcc"|
|
||||
* "auto-no-gcc", may be NULL/empty for auto); gcc_configured =
|
||||
* autocompile_configured(). Call once at overlay-cache init, on the emu thread,
|
||||
* before the run loop starts. */
|
||||
void code_provider_init(const char *cfg_backend, int gcc_configured);
|
||||
|
||||
/* The active/primary provider (owns the batch path + default selection). Never
|
||||
* NULL — defaults to the gcc provider before code_provider_init() runs. */
|
||||
const CodeProvider *code_provider_active(void);
|
||||
|
||||
/* The sljit provider IF it is available, else NULL. gcc and sljit are
|
||||
* COMPLEMENTARY, not exclusive: gcc fills the cache via the async batch path +
|
||||
* prebuilt DLLs and never JITs synchronously (compile_fragment == NULL), while
|
||||
* sljit is the synchronous JIT-on-miss that fills the gaps gcc has not (yet)
|
||||
* covered. The dispatch path uses this — independent of which provider is
|
||||
* "active" — so on a gcc dev box sljit still JITs gcc-absent regions
|
||||
* (priority gcc > sljit > interp). NULL => sljit unavailable, gaps fall to interp. */
|
||||
const CodeProvider *code_provider_sljit(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -18,24 +18,7 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* sljit shard host-helper table — see SLJIT_PERSIST_CACHE.md Stage 1. The sljit
|
||||
* JIT routes its host-helper calls through this cpu-relative table (indexed by
|
||||
* the enum below) instead of baking absolute function pointers as SLJIT_IMM
|
||||
* operands, so a serialized shard's LIR contains zero absolute host addresses —
|
||||
* the prerequisite for persisting shards to disk (a reloaded process is at a
|
||||
* different ASLR base, so baked pointers would be stale). The ENUM ORDER is part
|
||||
* of the on-disk shard format: never reorder, only append, and bump the sljit
|
||||
* serialization tag when it changes. gcc codegen does NOT use this table. */
|
||||
enum {
|
||||
SLJIT_HLP_MEMX = 0, /* psx_sljit_memx — LWL/LWR/SWL/SWR */
|
||||
SLJIT_HLP_COP2, /* psx_sljit_cop2 — COP2/GTE, LWC2/SWC2 */
|
||||
SLJIT_HLP_WS_CULL, /* psx_ws_cull_sltiu — widescreen render-funnel cull */
|
||||
SLJIT_HLP_CALL, /* psx_sljit_call — jal/jalr call contract */
|
||||
SLJIT_HLP_WS_CULL_SLTI, /* psx_ws_cull_slti — signed funnel right edge (appended) */
|
||||
SLJIT_HLP_WS_CULL_BLTZ, /* psx_ws_cull_bltz — signed funnel left edge (appended) */
|
||||
SLJIT_HLP_WS_PLAYER_X_BOUND, /* typed native-wide signed gameplay bound */
|
||||
SLJIT_HLP_COUNT
|
||||
};
|
||||
/* (sljit shard host-helper table removed 2026-07-15 with the sljit tier.) */
|
||||
|
||||
typedef struct CPUState {
|
||||
uint32_t gpr[32]; /* $0..$31; gpr[0] is hardwired zero, never written */
|
||||
@@ -53,12 +36,6 @@ typedef struct CPUState {
|
||||
uint8_t (*read_byte)(uint32_t addr);
|
||||
void (*write_byte)(uint32_t addr, uint8_t value);
|
||||
|
||||
/* sljit host-helper pointer table (indexed by the SLJIT_HLP_* enum above).
|
||||
* Appended at the END of CPUState so existing field offsets are unchanged —
|
||||
* precompiled gcc overlay DLLs (which bake field offsets) are unaffected;
|
||||
* only the sljit JIT reads it. Populated once at startup. */
|
||||
void *sljit_helpers[SLJIT_HLP_COUNT];
|
||||
|
||||
/* Mult/div completion deadline (absolute guest cycle). Set by MULT/MULTU/
|
||||
* DIV/DIVU to psx_cycle_count()+latency; a later MFLO/MFHI stalls (advances
|
||||
* cycles) until this deadline — faithful R3000A behavior (Beetle
|
||||
|
||||
@@ -153,7 +153,7 @@ void gpu_ws_set_signed_x_bound_sites(const uint32_t *addresses,
|
||||
int psx_ws_is_signed_x_bound_site(uint32_t pc, uint32_t instr);
|
||||
|
||||
/* Shared render-funnel screen-X cull widening ([widescreen.cull] auto_screen_x):
|
||||
* the gcc emit, the sljit JIT, and the interpreter all route a flagged
|
||||
* the gcc emit and the interpreter both route a flagged
|
||||
* `sltiu rt, sx, 0x140/0x141` through this one helper so every overlay execution
|
||||
* path widens identically. Returns the sltiu verdict (1 = keep). 0 at 4:3. */
|
||||
int psx_ws_cull_sltiu(uint32_t sx, uint32_t imm);
|
||||
@@ -164,7 +164,7 @@ int psx_ws_cull_slti(uint32_t sx, uint32_t imm);
|
||||
int psx_ws_cull_bltz(uint32_t v);
|
||||
/* True if a run of instruction words carries the screen-extent reject signature
|
||||
* (a width compare AND a height compare from the configured immediate sets).
|
||||
* Used by sljit/interp to gate the widening to real render funnels. */
|
||||
* Used by the interp to gate the widening to real render funnels. */
|
||||
int psx_ws_func_has_screen_cull(const uint32_t *words, int n);
|
||||
/* Classify words[idx] as an X left-edge reject bltz (runtime imm sets). */
|
||||
int psx_ws_cull_bltz_at(const uint32_t *words, int n, int idx);
|
||||
@@ -172,7 +172,7 @@ int psx_ws_cull_bltz_at(const uint32_t *words, int n, int idx);
|
||||
* screen_h_imms); defaults 0x140/0x141 + 0xE0/0xF1. */
|
||||
void gpu_ws_set_cull_imms(const uint32_t *w, int nw, const uint32_t *h, int nh);
|
||||
int psx_ws_is_cull_w_imm(uint32_t imm);
|
||||
/* Per-game opt-in gates for the pattern-scanned interp/sljit widen hooks
|
||||
/* Per-game opt-in gates for the pattern-scanned interp widen hooks
|
||||
* (auto_screen_x cull + auto_backdrop preload). Default OFF: a title that
|
||||
* never opted in must never have its live code pattern-scanned and rewritten. */
|
||||
void gpu_ws_set_auto_hooks(int cull_on, int backdrop_on);
|
||||
@@ -213,7 +213,7 @@ int psx_ws_backdrop_x(int x);
|
||||
/* Backdrop PRELOAD ([widescreen.cull] auto_backdrop). psx_ws_backdrop_preload()
|
||||
* is nonzero only while native-wide widescreen is engaged (0 at 4:3 / boot /
|
||||
* FMV / full-2D), so the rewrite is byte-identical when off. psx_ws_backdrop_value()
|
||||
* is the one value substitution shared by the gcc emit, the sljit JIT, and the
|
||||
* is the one value substitution shared by the gcc emit and the
|
||||
* interpreter for a detected window bound: it returns `orig` unless preload is
|
||||
* engaged, in which case it forces the bound to preload the WHOLE finite tile
|
||||
* row (START -> 0, END -> large sentinel pinned by the generator's high clamp to
|
||||
|
||||
@@ -6,14 +6,15 @@
|
||||
* This is NOT specific to any one producer. It decides which native overlay
|
||||
* tier fills a gap, in priority order:
|
||||
*
|
||||
* static > gcc shard > tcc shard > sljit shard (deprecated) > interp
|
||||
* static > gcc shard > tcc shard > interp
|
||||
*
|
||||
* - gcc: spawn-gcc -> overlay DLL. The developer / production "holy grail"
|
||||
* (best-optimized; the shards shipped in releases).
|
||||
* - tcc: spawn-tcc -> overlay DLL. Bundled, toolchain-free user fallback that
|
||||
* fills regions the shipped gcc cache misses on a machine without gcc.
|
||||
* - sljit: in-process JIT. DEPRECATED (it mis-compiled; gated off) — kept only
|
||||
* until it is removed, and always the lowest native priority.
|
||||
*
|
||||
* (The in-process sljit JIT tier was removed 2026-07-15 — it mis-compiled and
|
||||
* had been gated off since 2026-06-25. Uncovered gaps fall to the interpreter.)
|
||||
*
|
||||
* gcc and tcc go through the IDENTICAL pipeline (recompiler -> C -> compiler ->
|
||||
* DLL -> loader); they differ only in the compiler binary the autocompile
|
||||
@@ -29,7 +30,7 @@ extern "C" {
|
||||
typedef enum {
|
||||
OVERLAY_BACKEND_AUTO = 0, /* gcc if a gcc toolchain is present, else tcc */
|
||||
OVERLAY_BACKEND_GCC = 1, /* force spawn-gcc->DLL (dev / production shards) */
|
||||
OVERLAY_BACKEND_SLJIT = 2, /* DEPRECATED in-process sljit producer (gated off) */
|
||||
/* value 2 was OVERLAY_BACKEND_SLJIT (removed 2026-07-15) */
|
||||
OVERLAY_BACKEND_TCC = 3, /* spawn-tcc->DLL (bundled, toolchain-free fallback) */
|
||||
OVERLAY_BACKEND_AUTO_NO_GCC = 4 /* dev/test: resolve like AUTO but pretend no gcc toolchain
|
||||
* (gcc shards still LOAD; gaps fill via tcc) — simulate a
|
||||
@@ -47,7 +48,7 @@ OverlayBackend overlay_backend_resolve(const char *cfg, int gcc_toolchain_availa
|
||||
/* The cached resolution (OVERLAY_BACKEND_AUTO until resolve() runs). */
|
||||
OverlayBackend overlay_backend_active(void);
|
||||
|
||||
/* "auto" | "gcc" | "sljit" | "tcc" | "auto-no-gcc". */
|
||||
/* "auto" | "gcc" | "tcc" | "auto-no-gcc". */
|
||||
const char *overlay_backend_name(OverlayBackend b);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
#ifndef PSX_OVERLAY_COMPILE_WORKER_H
|
||||
#define PSX_OVERLAY_COMPILE_WORKER_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Off-main-thread sljit compile worker.
|
||||
*
|
||||
* The dispatch (guest-fiber) thread, on an sljit gap-fill miss, snapshots the
|
||||
* region bytes and ENQUEUES here instead of JIT'ing inline (which used to spike
|
||||
* the frame). A single background worker pops the queue, JITs the fragment FROM
|
||||
* THE SNAPSHOT (never live guest RAM), and writes the serialized shard into the
|
||||
* on-disk cache via overlay_loader_async_publish(). The dispatch thread picks
|
||||
* the shard up on a later miss (cache-dirty -> idempotent rescan -> register),
|
||||
* so the candidate table stays single-threaded: the worker NEVER touches it.
|
||||
*
|
||||
* Safety invariants (see docs/ASYNC_OVERLAY_COMPILE.md §3/§5):
|
||||
* - candidate tables / sljit registry: dispatch thread only.
|
||||
* - guest RAM: worker reads only the enqueue-time snapshot copy.
|
||||
* - the sljit emitter's file-static scratch is safe because the worker is the
|
||||
* SOLE runtime caller of the compiler (dispatch enqueues; selftest is init-time).
|
||||
*/
|
||||
|
||||
/* Start/stop the worker. Idempotent. stop() signals + joins. */
|
||||
void overlay_compile_worker_start(void);
|
||||
void overlay_compile_worker_stop(void);
|
||||
|
||||
/* Enqueue a region for background JIT. `vaddr` is the VIRTUAL entry (carries
|
||||
* KSEG bits the guest uses for return_pc/jal targets). `src` points at the
|
||||
* live region bytes [phys .. phys+snap_len); enqueue COPIES them, so the caller
|
||||
* keeps ownership of `src`. `crc` is a content hash of the snapshot window,
|
||||
* used only for (phys,crc) dedup. Deduped: a QUEUED/COMPILING/DONE (phys,crc)
|
||||
* is ignored. Cheap no-op if the worker isn't running. */
|
||||
void overlay_compile_worker_enqueue(uint32_t vaddr, uint32_t phys,
|
||||
const uint8_t *src, uint32_t snap_len,
|
||||
uint32_t crc);
|
||||
|
||||
/* Telemetry for the debug server (any out-ptr may be NULL). */
|
||||
void overlay_compile_worker_stats(uint64_t *enqueued, uint64_t *compiled,
|
||||
uint64_t *failed, uint32_t *queued_now);
|
||||
|
||||
/* 1 if the worker thread is currently running. */
|
||||
int overlay_compile_worker_running(void);
|
||||
|
||||
/* Last / worst single-compile wall time (ms, SDL_GetTicks resolution). The whole
|
||||
* point of the worker is that this time is paid OFF the dispatch thread, so a
|
||||
* nonzero max here alongside a flat dispatch-thread ce_profile is the proof. */
|
||||
void overlay_compile_worker_timing(uint32_t *last_ms, uint32_t *max_ms);
|
||||
|
||||
/* Always-on ring of the most-recent compile results (one entry PER shard the
|
||||
* worker has produced or rejected). Query this for the window of interest —
|
||||
* never arm-then-capture. */
|
||||
typedef struct {
|
||||
uint32_t vaddr; /* virtual entry enqueued */
|
||||
uint32_t phys; /* physical entry (dedup key) */
|
||||
uint32_t crc; /* content crc of the enqueue-time snapshot */
|
||||
uint32_t code_lo; /* compiled range start (phys) */
|
||||
uint32_t code_len; /* compiled range length in bytes (0 if fail) */
|
||||
uint32_t compile_ms; /* wall time for this compile (off-thread) */
|
||||
uint8_t ok; /* 1 = published a shard, 0 = emitter declined */
|
||||
} OverlayCompileEvent;
|
||||
|
||||
/* Copy up to `cap` most-recent events into `out` in chronological order (oldest
|
||||
* first), returning the count copied. Thread-safe. */
|
||||
int overlay_compile_worker_recent(OverlayCompileEvent *out, int cap);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* PSX_OVERLAY_COMPILE_WORKER_H */
|
||||
@@ -28,10 +28,8 @@ void overlay_loader_init(const char *cache_dir, const char *game_id);
|
||||
* Addresses may be KUSEG/KSEG0/KSEG1; the loader keys by physical address. */
|
||||
int overlay_loader_native_block_add(uint32_t addr);
|
||||
|
||||
/* Apply the sljit live-execution policy once the Tier-2 backend is resolved.
|
||||
* Call AFTER code_provider_init(). Default live ON iff the resolved backend is
|
||||
* sljit (validated-live production); PSX_OVERLAY_SLJIT_LIVE env overrides. */
|
||||
void overlay_loader_apply_live_policy(void);
|
||||
/* (sljit removed 2026-07-15: overlay_loader_apply_live_policy was declared
|
||||
* here to resolve the sljit live-execution policy after code_provider_init.) */
|
||||
|
||||
/* Called from overlay_capture_on_dma after the capture-set insert.
|
||||
* Computes CRC32 of bytes, checks cache, loads DLL if present.
|
||||
|
||||
@@ -1,129 +0,0 @@
|
||||
#ifndef OVERLAY_SLJIT_H
|
||||
#define OVERLAY_SLJIT_H
|
||||
|
||||
/* overlay_sljit — Tier-2 self-contained in-process JIT backend (sljit).
|
||||
*
|
||||
* Architecture (locked, see SLJIT.md):
|
||||
* Tier 1 (highest) — statically recompiled native (main EXE + discovered
|
||||
* overlay DLLs). Fastest. Unchanged.
|
||||
* Tier 2 (this) — the "shard": a fragment recompiled ON THE FLY the first
|
||||
* time execution would otherwise fall to the interpreter.
|
||||
* Built in RAM (sljit, sub-ms, no toolchain), registered as
|
||||
* a native candidate so the NEXT encounter is Tier 2 not
|
||||
* Tier 3, then persisted so later sessions reload it instead
|
||||
* of re-discovering through the slow interpreter. This is
|
||||
* structurally identical to today's gcc capture->compile->
|
||||
* cache->load path; sljit is just an in-process producer.
|
||||
* Tier 3 (lowest) — dirty-RAM interpreter (dirty_ram_interp.c). Correctness
|
||||
* floor for not-yet-discovered code.
|
||||
*
|
||||
* SAFETY CONTRACT (precision over recall): the emitter compiles only what it can
|
||||
* prove it emits identically to the interpreter. The moment it meets any
|
||||
* instruction/shape it does not yet support, it ABORTS the whole fragment and
|
||||
* returns failure — the caller then runs Tier 3 (interpreter). A partial emitter
|
||||
* is therefore always safe: it can never ship a mis-compiled shard, only decline
|
||||
* to produce one. gcc remains the default backend; sljit slots in UNDER the
|
||||
* proven path and is gated behind backend selection (see overlay_backend.h-style
|
||||
* policy below) until it passes the same-state differential.
|
||||
*/
|
||||
|
||||
#include "cpu_state.h"
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* The native shard ABI — identical to overlay_loader's OverlayFn. */
|
||||
typedef void (*OverlaySljitFn)(CPUState *);
|
||||
|
||||
/* Backend-selection policy (OverlayBackend enum + resolve/name/active) now lives
|
||||
* in overlay_backend.h — it is compiler-neutral (gcc/tcc/sljit/auto) and not part
|
||||
* of the sljit emitter. */
|
||||
|
||||
/* sljit availability — always 1 once the lib is linked (no external dep). */
|
||||
int overlay_sljit_available(void);
|
||||
|
||||
/* Smoke test: JITs a trivial leaf in-process and runs it, returning 1 iff the
|
||||
* produced machine code computes the expected value. Proves the sljit codegen +
|
||||
* executable-allocator path works in THIS build/host before any real fragment is
|
||||
* trusted. Surfaced via the debug server. */
|
||||
int overlay_sljit_selftest(void);
|
||||
|
||||
/* Result of a fragment compile. fn == NULL means the emitter declined the whole
|
||||
* fragment to the interpreter (Tier 3) — the always-safe outcome. On success,
|
||||
* [code_lo, code_lo+code_len) is the PHYS byte-range the shard was compiled from
|
||||
* (the caller registers a candidate over it for per-call live-byte validation,
|
||||
* exactly like the gcc .ranges manifest). */
|
||||
typedef struct {
|
||||
OverlaySljitFn fn; /* NULL = declined */
|
||||
uint32_t code_lo; /* phys start of compiled code range */
|
||||
uint32_t code_len; /* byte length of compiled code range */
|
||||
uint32_t insns; /* MIPS instructions compiled (diagnostics) */
|
||||
/* Serialized (position-independent) LIR of this shard for the persisted
|
||||
* cache (Stage 2). Allocated by sljit_serialize_compiler; the caller writes
|
||||
* it to disk then frees it via overlay_sljit_free_serialized(). NULL if not
|
||||
* serialized. */
|
||||
void *serialized;
|
||||
unsigned long serialized_size;
|
||||
} OverlaySljitResult;
|
||||
|
||||
/* Attempt to JIT the leaf function at `entry` (guest vram) by decoding from
|
||||
* `bytes` (an image whose byte[0] maps to vram `image_base_vram`) of `size`
|
||||
* bytes. Fills *out. On ANY unsupported instruction/shape sets out->fn = NULL
|
||||
* (caller falls through to Tier 3) — see the SAFETY CONTRACT above. First slice:
|
||||
* compiles only single-block leaf functions terminating in `jr $ra`; everything
|
||||
* else (internal branches, jal/jalr, jr to non-$ra, opcodes outside the slice)
|
||||
* declines the whole fragment. */
|
||||
void overlay_sljit_try_compile(uint32_t entry,
|
||||
const uint8_t *bytes, uint32_t size,
|
||||
uint32_t image_base_vram,
|
||||
OverlaySljitResult *out);
|
||||
|
||||
/* Diagnostics counters for the debug server. */
|
||||
void overlay_sljit_get_status(int *available, int *selftest_ok,
|
||||
uint64_t *compiles, uint64_t *declines,
|
||||
uint64_t *bytes_emitted);
|
||||
|
||||
/* Call helper invoked by JIT'd shards at a jal/jalr site. Mirrors the dirty-RAM
|
||||
* interpreter's call path EXACTLY: dispatch the callee as a unit (compiled /
|
||||
* native-overlay / interp), then apply the (ra,sp) call contract. Returns 1 if
|
||||
* the shard must return immediately (a transfer/bail unwind is in progress —
|
||||
* cpu->pc / g_psx_call_bail carry it up to the dispatch loop), 0 if the callee
|
||||
* returned normally and the shard should continue at the next instruction.
|
||||
* check_contract: 1 for jal and jalr whose link reg is $ra (the contract checks
|
||||
* $ra==return_pc); 0 for jalr to a non-$ra link (the callee won't set $ra to
|
||||
* return_pc, so the contract doesn't apply) — matches the interpreter. Defined
|
||||
* in overlay_loader.c (has the dispatch machinery). */
|
||||
int psx_sljit_call(CPUState *cpu, uint32_t target, uint32_t return_pc,
|
||||
int check_contract);
|
||||
|
||||
/* Per-instruction helpers a shard calls for op-classes that are emitted as a
|
||||
* call rather than inlined — each reproduces the dirty-RAM interpreter's exact
|
||||
* behaviour for that instruction (parity by construction). Defined in
|
||||
* overlay_loader.c.
|
||||
* psx_sljit_cop2 — COP2/GTE: MFC2/CFC2/MTC2/CTC2/cmd (op 0x12), LWC2 (0x32),
|
||||
* SWC2 (0x3A).
|
||||
* psx_sljit_memx — unaligned mem: LWL/LWR/SWL/SWR (op 0x22/0x26/0x2A/0x2E). */
|
||||
void psx_sljit_cop2(CPUState *cpu, uint32_t insn);
|
||||
void psx_sljit_memx(CPUState *cpu, uint32_t insn);
|
||||
|
||||
/* Populate cpu->sljit_helpers[] with the host-helper addresses the JIT calls
|
||||
* through (cpu-relative, so shard LIR is position-independent — see Stage 1 in
|
||||
* SLJIT_PERSIST_CACHE.md). Call once at startup after CPUState is wired. */
|
||||
void overlay_sljit_init_helpers(CPUState *cpu);
|
||||
|
||||
/* Reload a serialized shard blob (persisted cache, Stage 2) into a runnable
|
||||
* native fn for THIS process. Returns NULL on failure. The blob's LIR is
|
||||
* position-independent (Stage 1), so the regenerated code reaches host helpers
|
||||
* via the cpu-relative table — correct at this process's ASLR base. */
|
||||
OverlaySljitFn overlay_sljit_deserialize(const void *blob, unsigned long blob_size);
|
||||
|
||||
/* Free a buffer returned in OverlaySljitResult.serialized (sljit allocator). */
|
||||
void overlay_sljit_free_serialized(void *p);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* OVERLAY_SLJIT_H */
|
||||
@@ -4,7 +4,7 @@
|
||||
* Generic widescreen BACKDROP-PRELOAD detector ([widescreen.cull] auto_backdrop)
|
||||
*
|
||||
* Self-contained (depends only on <stdint.h>) so ONE implementation is shared
|
||||
* verbatim by the recompiler (C++) and the runtime interpreter + sljit emitter
|
||||
* verbatim by the recompiler (C++) and the runtime interpreter
|
||||
* (C). Having a single source of truth matters here: a false positive corrupts
|
||||
* unrelated overlay codegen (see BACKDROP_PRELOAD.md §risk), so the gate must be
|
||||
* identical on every path.
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* auto_screen_x)
|
||||
*
|
||||
* Self-contained (depends only on <stdint.h>) so ONE implementation is shared
|
||||
* verbatim by the recompiler (C++) and the runtime interpreter + sljit emitter
|
||||
* verbatim by the recompiler (C++) and the runtime interpreter
|
||||
* (C) — the widen must be identical on every execution path or the backends
|
||||
* diverge (same contract as ws_backdrop_detect.h).
|
||||
*
|
||||
|
||||
@@ -198,8 +198,6 @@ set(PSXRECOMP_RUNTIME_SOURCES
|
||||
${PSXRECOMP_ROOT}/runtime/src/overlay_capture.c
|
||||
${PSXRECOMP_ROOT}/runtime/src/overlay_loader.c
|
||||
${PSXRECOMP_ROOT}/runtime/src/overlay_posix.c
|
||||
${PSXRECOMP_ROOT}/runtime/src/overlay_compile_worker.c
|
||||
${PSXRECOMP_ROOT}/runtime/src/overlay_sljit.c
|
||||
${PSXRECOMP_ROOT}/runtime/src/overlay_backend.c
|
||||
${PSXRECOMP_ROOT}/runtime/src/autocompile.c
|
||||
${PSXRECOMP_ROOT}/runtime/src/code_provider.c
|
||||
@@ -208,9 +206,8 @@ set(PSXRECOMP_RUNTIME_SOURCES
|
||||
${PSXRECOMP_ROOT}/runtime/src/psx_keybinds.c
|
||||
${PSXRECOMP_ROOT}/recompiler/src/config_loader.cpp
|
||||
${PSXRECOMP_ROOT}/recompiler/src/ps1_exe_parser.cpp
|
||||
# Tier-2 in-process JIT backend (sljit, BSD-2-Clause). Single TU; sljit
|
||||
# auto-detects the host arch (SLJIT_CONFIG_AUTO). See lib/sljit/LICENSE.
|
||||
${PSXRECOMP_ROOT}/lib/sljit/sljit_src/sljitLir.c
|
||||
# (sljit Tier-2 in-process JIT backend removed 2026-07-15 — was disabled by
|
||||
# default since 2026-06-25; gaps fall to the interpreter, gcc/tcc unaffected.)
|
||||
)
|
||||
|
||||
set(PSXRECOMP_RUNTIME_INCLUDE_DIRS
|
||||
@@ -219,7 +216,6 @@ set(PSXRECOMP_RUNTIME_INCLUDE_DIRS
|
||||
${PSXRECOMP_ROOT}/recompiler/include
|
||||
${PSXRECOMP_ROOT}/recompiler/lib/fmt/include
|
||||
${PSXRECOMP_ROOT}/recompiler/lib/toml11
|
||||
${PSXRECOMP_ROOT}/lib/sljit/sljit_src
|
||||
)
|
||||
|
||||
set(PSXRECOMP_BIOS_GENERATED
|
||||
|
||||
+13
-54
@@ -1,20 +1,20 @@
|
||||
/* code_provider.c — the CodeProvider implementations + active selection.
|
||||
/* code_provider.c — the CodeProvider implementation + active selection.
|
||||
* See code_provider.h for the abstraction.
|
||||
*
|
||||
* The "spawn compiler" provider is a thin pass-through over the validated
|
||||
* autocompile_* path (compile emitted overlay C to a DLL and load it); it backs
|
||||
* BOTH the gcc and tcc tiers, which differ only in which compiler the pipeline
|
||||
* invokes. The in-process JIT provider (code_provider_sljit) is a separate,
|
||||
* legacy path. Which provider is active is decided once at init by
|
||||
* invokes. Which behavior is active is decided once at init by
|
||||
* overlay_backend_resolve() in overlay_backend.c (env PSX_OVERLAY_BACKEND >
|
||||
* [runtime] overlay_backend > auto). The auto tier order is:
|
||||
* static -> gcc (if gcc on PATH) -> tcc (bundled toolchain-free fallback). */
|
||||
* static -> gcc (if gcc on PATH) -> tcc (bundled toolchain-free fallback).
|
||||
* (The in-process sljit JIT provider was removed 2026-07-15; overlay gaps fall
|
||||
* to the interpreter.) */
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "code_provider.h"
|
||||
#include "autocompile.h"
|
||||
#include "overlay_sljit.h"
|
||||
#include "overlay_backend.h"
|
||||
|
||||
/* ---- gcc provider: pass-through to the validated spawn->DLL->rescan path --- */
|
||||
@@ -29,39 +29,6 @@ static const CodeProvider s_gcc = {
|
||||
/* request */ autocompile_request,
|
||||
/* busy */ autocompile_busy,
|
||||
/* poll_main */ autocompile_poll_main,
|
||||
/* compile_fragment */ NULL,
|
||||
};
|
||||
|
||||
/* ---- sljit provider: in-process JIT (sync on-miss only, v1) ---------------- */
|
||||
/* No batch/async path: sljit produces synchronously at a dispatch miss, so
|
||||
* request/busy/poll_main are inert. compile_fragment delegates to the MIPS->sljit
|
||||
* emitter (overlay_sljit_try_compile), which compiles the supported instruction
|
||||
* classes and declines the rest (fn=NULL) — safe by construction (precision over
|
||||
* recall). The returned shard is validated via the same-state diff before live. */
|
||||
static int sljit_request(void) { return 0; }
|
||||
static int sljit_busy(void) { return 0; }
|
||||
static void sljit_poll_main(void) { }
|
||||
|
||||
static void sljit_compile_fragment(uint32_t entry, const uint8_t *bytes,
|
||||
uint32_t size, uint32_t image_base_vram,
|
||||
CompiledFragment *out) {
|
||||
OverlaySljitResult r = {0};
|
||||
overlay_sljit_try_compile(entry, bytes, size, image_base_vram, &r);
|
||||
/* OverlaySljitFn and CodeProviderFn are the same void fn(CPUState*) type. */
|
||||
out->fn = (CodeProviderFn)r.fn;
|
||||
out->code_lo = r.code_lo;
|
||||
out->code_len = r.code_len;
|
||||
out->serialized = r.serialized; /* for the persisted shard cache */
|
||||
out->serialized_size = r.serialized_size;
|
||||
}
|
||||
|
||||
static const CodeProvider s_sljit = {
|
||||
/* name */ "sljit",
|
||||
/* available */ overlay_sljit_available,
|
||||
/* request */ sljit_request,
|
||||
/* busy */ sljit_busy,
|
||||
/* poll_main */ sljit_poll_main,
|
||||
/* compile_fragment */ sljit_compile_fragment,
|
||||
};
|
||||
|
||||
/* ---- active selection ------------------------------------------------------ */
|
||||
@@ -70,22 +37,14 @@ static const CodeProvider s_sljit = {
|
||||
static const CodeProvider *s_active = &s_gcc;
|
||||
|
||||
void code_provider_init(const char *cfg_backend, int gcc_configured) {
|
||||
/* overlay_backend_resolve resolves AUTO to GCC or SLJIT and never returns
|
||||
* AUTO, so this maps cleanly to one of the two providers. It also logs the
|
||||
* decision (surfaced via the sljit_status debug command). */
|
||||
OverlayBackend b = overlay_backend_resolve(cfg_backend, gcc_configured);
|
||||
/* The "gcc" provider is really the spawn-a-compiler->DLL->rescan path; it is
|
||||
* compiler-agnostic (the actual compiler is whatever overlay_autocompile_cmd
|
||||
* runs — gcc OR tcc, selected in main.cpp by the resolved backend). So BOTH
|
||||
* gcc and tcc map to it. Only the deprecated in-process sljit producer is
|
||||
* separate. */
|
||||
s_active = (b == OVERLAY_BACKEND_SLJIT) ? &s_sljit : &s_gcc;
|
||||
/* overlay_backend_resolve resolves AUTO to GCC or TCC (never AUTO). Both the
|
||||
* gcc and tcc tiers are the same spawn-a-compiler->DLL->rescan provider
|
||||
* (the actual compiler is whatever overlay_autocompile_cmd runs, selected in
|
||||
* main.cpp by the resolved backend), so there is a single provider now. The
|
||||
* call still runs for its side effect of logging the resolved backend
|
||||
* (surfaced via the overlay_status debug command). */
|
||||
(void)overlay_backend_resolve(cfg_backend, gcc_configured);
|
||||
s_active = &s_gcc;
|
||||
}
|
||||
|
||||
const CodeProvider *code_provider_active(void) { return s_active; }
|
||||
|
||||
/* sljit provider for the synchronous JIT-on-miss gap-fill, regardless of which
|
||||
* provider is "active". NULL when sljit isn't available so callers fall to interp. */
|
||||
const CodeProvider *code_provider_sljit(void) {
|
||||
return (s_sljit.available && s_sljit.available()) ? &s_sljit : NULL;
|
||||
}
|
||||
|
||||
@@ -143,7 +143,7 @@ static uint64_t hash_cpu(const CPUState *c) {
|
||||
h = fnv(h, &c->read_fudge, 1);
|
||||
h = fnv(h, &c->ld_which_t, 1);
|
||||
h = fnv_u32(h, c->ld_absorb);
|
||||
/* EXCLUDED (host-only): read_word/.../write_byte fn ptrs, sljit_helpers. */
|
||||
/* EXCLUDED (host-only): read_word/.../write_byte fn ptrs. */
|
||||
return h;
|
||||
}
|
||||
|
||||
|
||||
+4
-117
@@ -20,9 +20,7 @@
|
||||
#include "overlay_loader.h"
|
||||
#include "overlay_capture.h"
|
||||
#include "code_provider.h"
|
||||
#include "overlay_sljit.h"
|
||||
#include "overlay_backend.h"
|
||||
#include "overlay_compile_worker.h"
|
||||
#include "cpu_state.h"
|
||||
#include "dma.h"
|
||||
#include "gpu.h"
|
||||
@@ -10641,120 +10639,12 @@ static void handle_autocompile_status(int id, const char *json)
|
||||
(unsigned long long)overlay_autocapture_last_insns_delta(), comp);
|
||||
}
|
||||
|
||||
/* sljit_status: Tier-2 in-process JIT backend state. Runs the codegen smoke
|
||||
* test on first query (JITs a trivial leaf and executes it), reports the
|
||||
* resolved overlay backend (gcc/sljit/auto) and the shard compile/decline
|
||||
* counters. */
|
||||
static void handle_sljit_status(int id, const char *json)
|
||||
{
|
||||
/* Declarations come from overlay_sljit.h (included above). */
|
||||
extern unsigned int overlay_loader_sljit_registered(void);
|
||||
extern unsigned int overlay_loader_sljit_obsoleted(void);
|
||||
extern unsigned int overlay_loader_sljit_reloaded(void);
|
||||
extern unsigned int overlay_loader_sljit_reload_seen(void);
|
||||
extern unsigned int overlay_loader_sljit_reload_hdrbad(void);
|
||||
extern unsigned int overlay_loader_sljit_reload_deserfail(void);
|
||||
extern unsigned int overlay_loader_sljit_persist_calls(void);
|
||||
extern unsigned int overlay_loader_sljit_persist_writes(void);
|
||||
extern int overlay_loader_sljit_persist_on(void);
|
||||
extern int overlay_loader_sljit_persist_dbg(void);
|
||||
extern int overlay_loader_get_sljit_live(void);
|
||||
(void)json;
|
||||
int selftest = overlay_sljit_selftest();
|
||||
int available = 0, st_ok = 0;
|
||||
unsigned long long compiles = 0, declines = 0, bytes = 0;
|
||||
overlay_sljit_get_status(&available, &st_ok, &compiles, &declines, &bytes);
|
||||
send_fmt("{\"id\":%d,\"ok\":true,\"backend\":\"%s\",\"available\":%d,"
|
||||
"\"selftest_ok\":%d,\"live\":%d,\"compiles\":%llu,\"declines\":%llu,"
|
||||
"\"bytes_emitted\":%llu,\"shards_registered\":%u,\"obsoleted\":%u,"
|
||||
"\"reloaded\":%u,\"reload_seen\":%u,\"reload_hdrbad\":%u,\"reload_deserfail\":%u,"
|
||||
"\"persist_on\":%d,\"persist_calls\":%u,\"persist_writes\":%u,"
|
||||
"\"persist_dbg\":%d}\n",
|
||||
id, overlay_backend_name(overlay_backend_active()), available,
|
||||
selftest, overlay_loader_get_sljit_live(), compiles, declines, bytes,
|
||||
overlay_loader_sljit_registered(),
|
||||
overlay_loader_sljit_obsoleted(),
|
||||
overlay_loader_sljit_reloaded(),
|
||||
overlay_loader_sljit_reload_seen(),
|
||||
overlay_loader_sljit_reload_hdrbad(),
|
||||
overlay_loader_sljit_reload_deserfail(),
|
||||
overlay_loader_sljit_persist_on(),
|
||||
overlay_loader_sljit_persist_calls(),
|
||||
overlay_loader_sljit_persist_writes(),
|
||||
overlay_loader_sljit_persist_dbg());
|
||||
}
|
||||
|
||||
/* sljit_async: off-thread compile-worker telemetry. Reports the always-on
|
||||
* counters (enqueued/compiled/failed/queued_now), last/worst off-thread compile
|
||||
* time, the resolved on-disk shard dir, and the recent-compile ring. This is the
|
||||
* ring-buffer proof that the worker actually does work: a rising `compiled` with
|
||||
* a populated `recent[]` (each with a phys/crc/code_len/ms) while the dispatch
|
||||
* thread's ce_profile stays flat == JIT moved off the hot path. */
|
||||
static void handle_sljit_async(int id, const char *json)
|
||||
{
|
||||
extern void overlay_loader_sljit_cache_dir(char *buf, int n);
|
||||
extern int overlay_loader_sljit_async_on(void);
|
||||
extern int overlay_loader_sljit_tier_enabled(void);
|
||||
(void)json;
|
||||
uint64_t enq = 0, done = 0, failed = 0;
|
||||
uint32_t queued = 0, last_ms = 0, max_ms = 0;
|
||||
overlay_compile_worker_stats(&enq, &done, &failed, &queued);
|
||||
overlay_compile_worker_timing(&last_ms, &max_ms);
|
||||
char dir[768] = {0};
|
||||
overlay_loader_sljit_cache_dir(dir, (int)sizeof(dir));
|
||||
/* escape backslashes in the Windows path for valid JSON */
|
||||
char dir_esc[1536]; int di = 0;
|
||||
for (int i = 0; dir[i] && di < (int)sizeof(dir_esc) - 2; i++) {
|
||||
if (dir[i] == '\\') dir_esc[di++] = '\\';
|
||||
dir_esc[di++] = dir[i];
|
||||
}
|
||||
dir_esc[di] = '\0';
|
||||
|
||||
OverlayCompileEvent evs[64];
|
||||
int ne = overlay_compile_worker_recent(evs, 64);
|
||||
|
||||
char out[16384];
|
||||
int n = snprintf(out, sizeof(out),
|
||||
"{\"id\":%d,\"ok\":true,\"tier_enabled\":%d,\"worker_running\":%d,\"async_on\":%d,"
|
||||
"\"enqueued\":%llu,\"compiled\":%llu,\"failed\":%llu,\"queued_now\":%u,"
|
||||
"\"last_compile_ms\":%u,\"max_compile_ms\":%u,\"cache_dir\":\"%s\","
|
||||
"\"recent\":[",
|
||||
id, overlay_loader_sljit_tier_enabled(),
|
||||
overlay_compile_worker_running(), overlay_loader_sljit_async_on(),
|
||||
(unsigned long long)enq, (unsigned long long)done,
|
||||
(unsigned long long)failed, queued, last_ms, max_ms, dir_esc);
|
||||
for (int i = 0; i < ne && n < (int)sizeof(out) - 160; i++) {
|
||||
n += snprintf(out + n, sizeof(out) - n,
|
||||
"%s{\"vaddr\":\"0x%08X\",\"phys\":\"0x%08X\",\"crc\":\"0x%08X\","
|
||||
"\"code_lo\":\"0x%08X\",\"code_len\":%u,\"ms\":%u,\"ok\":%d}",
|
||||
i ? "," : "", evs[i].vaddr, evs[i].phys, evs[i].crc,
|
||||
evs[i].code_lo, evs[i].code_len, evs[i].compile_ms, evs[i].ok);
|
||||
}
|
||||
n += snprintf(out + n, sizeof(out) - n, "]}\n");
|
||||
send_fmt("%s", out);
|
||||
}
|
||||
|
||||
/* sljit_try <hex_addr>: force a one-shot JIT of the leaf function at a phys
|
||||
* address from live RAM (bypasses the diff gate — a probe) and report whether
|
||||
* the emitter accepted it. Lets the leaf emitter be exercised against a known
|
||||
* function without waiting for a dispatch miss. */
|
||||
static void handle_sljit_try(int id, const char *json)
|
||||
{
|
||||
extern void overlay_loader_sljit_probe(uint32_t addr, OverlaySljitResult *out);
|
||||
uint32_t addr = 0;
|
||||
const char *p = strstr(json, "\"addr\"");
|
||||
if (p) { p = strchr(p, ':'); if (p) addr = (uint32_t)strtoul(p + 1, NULL, 0); }
|
||||
if (!addr) { send_fmt("{\"id\":%d,\"ok\":false,\"err\":\"need addr\"}\n", id); return; }
|
||||
OverlaySljitResult r = {0};
|
||||
overlay_loader_sljit_probe(addr, &r);
|
||||
send_fmt("{\"id\":%d,\"ok\":true,\"addr\":\"0x%08X\",\"compiled\":%d,"
|
||||
"\"insns\":%u,\"code_lo\":\"0x%08X\",\"code_len\":%u}\n",
|
||||
id, addr, r.fn ? 1 : 0, r.insns, r.code_lo, r.code_len);
|
||||
}
|
||||
/* (sljit removed 2026-07-15: the sljit_status, sljit_async, and sljit_try TCP
|
||||
* command handlers lived here. They reported the deprecated in-process JIT
|
||||
* tier's backend/worker/probe state, which no longer exists.) */
|
||||
|
||||
/* autocompile_run: manually kick the active code provider's batch production
|
||||
* (gcc: spawn the configured background compile; sljit: inert, it produces
|
||||
* synchronously on a dispatch miss). */
|
||||
* (gcc: spawn the configured background compile). */
|
||||
static void handle_autocompile_run(int id, const char *json)
|
||||
{
|
||||
(void)json;
|
||||
@@ -12408,9 +12298,6 @@ static const CmdEntry s_commands[] = {
|
||||
{ "turbo_audio_sink", handle_turbo_audio_sink },
|
||||
{ "load_transitions", handle_load_transitions },
|
||||
{ "autocompile_status", handle_autocompile_status },
|
||||
{ "sljit_status", handle_sljit_status },
|
||||
{ "sljit_async", handle_sljit_async },
|
||||
{ "sljit_try", handle_sljit_try },
|
||||
{ "autocompile_run", handle_autocompile_run },
|
||||
{ "overlay_rescan", handle_overlay_rescan },
|
||||
{ NULL, NULL }
|
||||
|
||||
@@ -219,7 +219,7 @@ int dirty_ram_interp_is_active(void) { return g_dirty_interp_active; }
|
||||
* the backend actually executing at that instant:
|
||||
* 0 = host/other (SDL, GPU, top-level dispatch glue, idle)
|
||||
* 1 = dirty-RAM interpreter (exec_one body / precise slice)
|
||||
* 2 = native overlay shard (gcc DLL or sljit)
|
||||
* 2 = native overlay shard (gcc/tcc DLL)
|
||||
* 3 = compiled static text (game EXE / recompiled BIOS, incl. IRQ handler)
|
||||
* 4 = GPU GP0 command processing (gpu.c — raster/batch/VRAM-transfer work)
|
||||
* Longjmp contract mirrors g_dirty_interp_active: exception delivery saves it
|
||||
@@ -916,7 +916,7 @@ static int g_xprobe_watch(uint32_t t) {
|
||||
static void xprobe_event(uint32_t src_pc, uint8_t op, uint8_t site, uint32_t target,
|
||||
uint32_t ds_insn, uint32_t sp, uint32_t ra, int want_detail);
|
||||
/* Watched-target call note for NON-interp call sites (overlay shard call-outs
|
||||
* via dispatch_call / psx_sljit_call). phase 0 = before the call (ds = a0),
|
||||
* via dispatch_call). phase 0 = before the call (ds = a0),
|
||||
* phase 1 = after it returned (ds = v0). Recorded as XOP_RES with
|
||||
* site = 20+phase so the `watched` dump shows the shard-side call + result. */
|
||||
void dirty_ram_xprobe_call_note(CPUState *cpu, uint32_t target, uint32_t ra, uint8_t phase) {
|
||||
|
||||
+7
-7
@@ -883,7 +883,7 @@ int gpu_ws_mmx6_validate(int *bad_out) {
|
||||
}
|
||||
|
||||
/* Shared render-funnel screen-X cull widening ([widescreen.cull] auto_screen_x),
|
||||
* called identically by the gcc emit, the sljit JIT, and the interpreter so every
|
||||
* called identically by the gcc emit and the interpreter so every
|
||||
* overlay execution path widens the same way. sx = the GTE screen-X as loaded by
|
||||
* the guest's lhu (low 16 bits significant); imm = the original bound (0x140 /
|
||||
* 0x141). Returns the sltiu verdict (1 = on-screen/keep). Sign-extends sx and
|
||||
@@ -914,7 +914,7 @@ int psx_ws_cull_bltz(uint32_t v) {
|
||||
* screen_h_imms). The width/height immediates are per-game (Tomba: 0x140/0x141
|
||||
* + 0xE0/0xF1 on a 320 display; Ape Escape: 0x181 + 0xF1 on 368). Defaults
|
||||
* keep the original Tomba signature so existing configs are unchanged. The
|
||||
* sets are consulted by the shared detector on every backend (interp + sljit;
|
||||
* sets are consulted by the shared detector on every backend (interp;
|
||||
* the recompiler reads the same config at gen time). */
|
||||
static uint32_t ws_cull_w_imms[8] = { 0x140, 0x141 };
|
||||
static int ws_cull_w_n = 2;
|
||||
@@ -936,8 +936,8 @@ int psx_ws_is_cull_w_imm(uint32_t imm) {
|
||||
return psx_ws_cull_imm_in(imm, ws_cull_w_imms, ws_cull_w_n);
|
||||
}
|
||||
|
||||
/* ---- Runtime gates for the pattern-scanned widescreen hooks. The interp and
|
||||
* sljit derive widen sites by scanning live code; that derivation must honor
|
||||
/* ---- Runtime gates for the pattern-scanned widescreen hooks. The interp
|
||||
* derives widen sites by scanning live code; that derivation must honor
|
||||
* the SAME per-game [widescreen.cull] opt-ins the recompiler emit does. These
|
||||
* default OFF: a title that never opted in must never have its code
|
||||
* pattern-scanned and rewritten (an ungated backdrop false positive rewrites a
|
||||
@@ -953,7 +953,7 @@ int psx_ws_auto_cull_on(void) { return ws_auto_cull_on_cfg; }
|
||||
|
||||
/* Detect the GTE screen-extent trivial-reject signature in a run of
|
||||
* instruction words: at least one width compare AND one height compare
|
||||
* (slti or sltiu, immediates from the configured sets). Lets the sljit JIT +
|
||||
* (slti or sltiu, immediates from the configured sets). Lets the
|
||||
* interpreter gate the cull-widening to real render funnels (a lone width
|
||||
* compare elsewhere must stay vanilla). Same shared scan the recompiler's
|
||||
* func_has_screen_extent_cull uses (ws_cull_detect.h). */
|
||||
@@ -987,7 +987,7 @@ int psx_ws_backdrop_x(int x) {
|
||||
}
|
||||
|
||||
/* Backdrop PRELOAD predicate + value substitution ([widescreen.cull]
|
||||
* auto_backdrop). The recompiler/sljit/interp detect each scrolling-backdrop
|
||||
* auto_backdrop). The recompiler/interp detect each scrolling-backdrop
|
||||
* column-window generator (see ws_backdrop_detect.h) and route its window START
|
||||
* and END bounds through psx_ws_backdrop_value(). When native-wide is engaged we
|
||||
* WIDEN the camera-tracked window by the 16:9 reveal: the START (left) bound
|
||||
@@ -1035,7 +1035,7 @@ uint32_t psx_ws_backdrop_value(uint32_t orig, int is_end, int window_cols) {
|
||||
finalv = is_end ? (orig + (uint32_t)m) : (orig - (uint32_t)m);
|
||||
}
|
||||
/* Single chokepoint for every path (interp hook, native cache-DLL via the
|
||||
* overlay callback, sljit). The interp records its own richer entry, so skip
|
||||
* overlay callback). The interp records its own richer entry, so skip
|
||||
* here when it set the flag; native-DLL calls (flag 0) are recorded here with
|
||||
* pc/extent/camera = 0 -- which is how the ring sees native execution. */
|
||||
if (!from_interp)
|
||||
|
||||
+10
-16
@@ -21,7 +21,6 @@
|
||||
#include "overlay_loader.h"
|
||||
#include "autocompile.h"
|
||||
#include "code_provider.h"
|
||||
#include "overlay_sljit.h"
|
||||
#include "overlay_backend.h"
|
||||
#include "gpu.h"
|
||||
#include "present_ring.h"
|
||||
@@ -956,9 +955,9 @@ static std::filesystem::path default_memcard_dir(const char* argv0) {
|
||||
|
||||
static void close_controller(void);
|
||||
|
||||
extern "C" void overlay_compile_worker_stop(void);
|
||||
static void shutdown_runtime(void) {
|
||||
overlay_compile_worker_stop(); /* signal + join the off-thread sljit worker */
|
||||
/* (sljit removed 2026-07-15: overlay_compile_worker_stop joined the
|
||||
* off-thread JIT worker here; the worker no longer exists.) */
|
||||
memcard_flush_all();
|
||||
overlay_capture_write_json();
|
||||
if (sdl_audio_device) {
|
||||
@@ -2290,7 +2289,7 @@ static void sdl_vblank_present(void) {
|
||||
* frontend is skipping presents. Both are cheap and emu-thread-only. */
|
||||
overlay_autocapture_tick();
|
||||
{ /* Apply a finished batch compile via the active code provider (gcc:
|
||||
* cache rescan on done; sljit: no-op — it produces synchronously). */
|
||||
* cache rescan on done). */
|
||||
const CodeProvider *cp = code_provider_active();
|
||||
if (cp->poll_main) cp->poll_main();
|
||||
}
|
||||
@@ -3095,8 +3094,8 @@ int main(int argc, char** argv) {
|
||||
gte_ws_configure_dome_sites(
|
||||
gc.ws_dome_call_sites.data(), (int)gc.ws_dome_call_sites.size());
|
||||
/* [widescreen.cull] per-game gates + signature immediates for the
|
||||
* pattern-scanned interp/sljit widen hooks. A title that never
|
||||
* opted in must never have its live code scanned and rewritten. */
|
||||
* pattern-scanned interp widen hooks. A title that never opted in
|
||||
* must never have its live code scanned and rewritten. */
|
||||
gpu_ws_set_auto_hooks(gc.ws_auto_screen_x_cull ? 1 : 0,
|
||||
gc.ws_auto_backdrop_preload ? 1 : 0);
|
||||
if (!gc.ws_cull_w_imms.empty() || !gc.ws_cull_h_imms.empty())
|
||||
@@ -3196,7 +3195,7 @@ int main(int argc, char** argv) {
|
||||
* dev/production box). auto => gcc if so, else tcc; auto-no-gcc =>
|
||||
* tcc even with gcc present (simulate a toolchain-less user box).
|
||||
* env PSX_OVERLAY_BACKEND overrides. Tiers: static > gcc > tcc >
|
||||
* (sljit, deprecated/off) > interp. */
|
||||
* interp. */
|
||||
const char *cfg_backend = gc.runtime.overlay_backend.empty()
|
||||
? nullptr : gc.runtime.overlay_backend.c_str();
|
||||
int gcc_avail = gc.runtime.has_overlay_autocompile_cmd
|
||||
@@ -3265,11 +3264,8 @@ int main(int argc, char** argv) {
|
||||
overlay_backend_name(eff), cache_dir.c_str());
|
||||
}
|
||||
code_provider_init(cfg_backend, gcc_avail);
|
||||
/* Now that the backend is resolved, apply the sljit live policy:
|
||||
* a toolchain-less (sljit) machine runs validated shards live so
|
||||
* it self-improves on the normal play path; PSX_OVERLAY_SLJIT_LIVE
|
||||
* overrides. (Validated-live, not blind — see the dispatch gate.) */
|
||||
overlay_loader_apply_live_policy();
|
||||
/* (sljit removed 2026-07-15: overlay_loader_apply_live_policy was
|
||||
* called here once the backend resolved.) */
|
||||
}
|
||||
std::fprintf(stdout, "psxrecomp: loaded game config %s (%s, %s)\n",
|
||||
game_config_path, game_name.c_str(), game_id.c_str());
|
||||
@@ -4011,10 +4007,8 @@ int main(int argc, char** argv) {
|
||||
cpu.write_half = psx_write_half;
|
||||
cpu.read_byte = psx_read_byte;
|
||||
cpu.write_byte = psx_write_byte;
|
||||
/* Wire the sljit JIT host-helper table (cpu-relative => position-independent
|
||||
* shards; prerequisite for the persisted sljit shard cache). Harmless when
|
||||
* sljit isn't the active backend. */
|
||||
overlay_sljit_init_helpers(&cpu);
|
||||
/* (sljit removed 2026-07-15: overlay_sljit_init_helpers wired the JIT-shard
|
||||
* host-helper table into cpu here; no shards consume it now.) */
|
||||
|
||||
/* BIOS backend select (CLAUDE.md §0 amendment 2026-07-02): LLE (the
|
||||
* recompiled BIOS — default, reference implementation, oracle) vs the
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* overlay_backend.c — compiler-neutral overlay tier-selection policy.
|
||||
* See overlay_backend.h. Moved out of overlay_sljit.c (which is now just the
|
||||
* deprecated sljit emitter): deciding gcc vs tcc vs sljit is not sljit's job. */
|
||||
* See overlay_backend.h. (Originally split out of overlay_sljit.c, whose
|
||||
* sljit tier was removed 2026-07-15: tier selection was never sljit's job.) */
|
||||
|
||||
#include "overlay_backend.h"
|
||||
|
||||
@@ -22,7 +22,6 @@ static void backend_log(const char *fmt, ...) {
|
||||
const char *overlay_backend_name(OverlayBackend b) {
|
||||
switch (b) {
|
||||
case OVERLAY_BACKEND_GCC: return "gcc";
|
||||
case OVERLAY_BACKEND_SLJIT: return "sljit";
|
||||
case OVERLAY_BACKEND_TCC: return "tcc";
|
||||
case OVERLAY_BACKEND_AUTO_NO_GCC: return "auto-no-gcc";
|
||||
default: return "auto";
|
||||
@@ -32,7 +31,8 @@ const char *overlay_backend_name(OverlayBackend b) {
|
||||
static OverlayBackend parse_backend(const char *s, OverlayBackend dflt) {
|
||||
if (!s || !*s) return dflt;
|
||||
if (!strcmp(s, "gcc")) return OVERLAY_BACKEND_GCC;
|
||||
if (!strcmp(s, "sljit")) return OVERLAY_BACKEND_SLJIT;
|
||||
/* "sljit" (removed 2026-07-15) is no longer recognized — a stale config
|
||||
* value falls through to `dflt` (AUTO), so old game.toml still boots. */
|
||||
if (!strcmp(s, "tcc")) return OVERLAY_BACKEND_TCC;
|
||||
if (!strcmp(s, "auto-no-gcc")) return OVERLAY_BACKEND_AUTO_NO_GCC;
|
||||
if (!strcmp(s, "auto")) return OVERLAY_BACKEND_AUTO;
|
||||
@@ -41,8 +41,8 @@ static OverlayBackend parse_backend(const char *s, OverlayBackend dflt) {
|
||||
|
||||
OverlayBackend overlay_backend_resolve(const char *cfg, int gcc_toolchain_available) {
|
||||
/* Precedence: env PSX_OVERLAY_BACKEND > game.toml [runtime] overlay_backend
|
||||
* (cfg) > AUTO. Tier order is static > gcc > tcc > (sljit, deprecated/off) >
|
||||
* interp. AUTO prefers gcc when a gcc TOOLCHAIN is actually present (a dev /
|
||||
* (cfg) > AUTO. Tier order is static > gcc > tcc > interp. AUTO prefers gcc
|
||||
* when a gcc TOOLCHAIN is actually present (a dev /
|
||||
* production box), else tcc (the bundled toolchain-free user fallback).
|
||||
* AUTO_NO_GCC forces the tcc branch even when gcc IS present — it simulates a
|
||||
* toolchain-less user box on a dev machine (gcc shards still LOAD via the
|
||||
|
||||
@@ -1,204 +0,0 @@
|
||||
#include "overlay_compile_worker.h"
|
||||
#include "code_provider.h"
|
||||
#include "overlay_sljit.h"
|
||||
#include "crc32.h"
|
||||
#include <SDL.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/* Dispatch-side publish (overlay_loader.c): atomically write the serialized
|
||||
* shard into the on-disk cache + flag the loader to rescan-on-miss. It touches
|
||||
* only the filesystem and read-only-after-init cache-path globals, so it is safe
|
||||
* to call from this worker thread (it never touches the candidate table). */
|
||||
extern void overlay_loader_async_publish(uint32_t entry_phys, uint32_t lo,
|
||||
uint32_t len, uint32_t crc,
|
||||
const void *blob, unsigned long blob_size);
|
||||
|
||||
#define WQ_CAP 256 /* bounded work queue */
|
||||
#define STATE_CAP 4096 /* per-(phys,crc) dedup map */
|
||||
#define SNAP_MAX (8u * 1024u) /* max window we JIT (SLJIT_MAX_FRAG_INSNS*4) */
|
||||
|
||||
typedef struct {
|
||||
uint32_t vaddr, phys, crc, snap_len;
|
||||
uint8_t *snap; /* worker-owned copy of the region bytes */
|
||||
} WorkItem;
|
||||
|
||||
enum { ST_QUEUED = 1, ST_COMPILING = 2, ST_DONE = 3, ST_FAILED = 4 };
|
||||
typedef struct { uint32_t phys, crc; uint8_t state; } StateEnt;
|
||||
|
||||
static SDL_Thread *s_thread;
|
||||
static SDL_mutex *s_mtx;
|
||||
static SDL_cond *s_cv;
|
||||
static int s_running;
|
||||
|
||||
static WorkItem s_q[WQ_CAP];
|
||||
static int s_q_head, s_q_tail, s_q_n;
|
||||
|
||||
static StateEnt s_state[STATE_CAP];
|
||||
static int s_state_n;
|
||||
|
||||
static uint64_t s_enq, s_done, s_failed;
|
||||
|
||||
/* Always-on recent-compile ring (CLAUDE.md ring-buffer doctrine): every compile,
|
||||
* success or decline, lands here so a probe can QUERY what the worker did in the
|
||||
* window of interest rather than time-and-capture. Guarded by s_mtx. */
|
||||
#define EVT_CAP 64
|
||||
static OverlayCompileEvent s_evt[EVT_CAP];
|
||||
static uint64_t s_evt_seq; /* total events recorded ever */
|
||||
static uint32_t s_last_ms, s_max_ms;
|
||||
|
||||
/* dedup map — all accesses hold s_mtx. */
|
||||
static StateEnt *state_find(uint32_t phys, uint32_t crc) {
|
||||
for (int i = 0; i < s_state_n; i++)
|
||||
if (s_state[i].phys == phys && s_state[i].crc == crc) return &s_state[i];
|
||||
return NULL;
|
||||
}
|
||||
static StateEnt *state_add(uint32_t phys, uint32_t crc, uint8_t st) {
|
||||
if (s_state_n >= STATE_CAP) {
|
||||
/* Full: evict the first finished entry (the on-disk cache is the source
|
||||
* of truth, so a later re-miss can re-enqueue if ever needed). COMPILING/
|
||||
* QUEUED entries are never evicted (in flight). */
|
||||
for (int i = 0; i < s_state_n; i++)
|
||||
if (s_state[i].state == ST_DONE || s_state[i].state == ST_FAILED) {
|
||||
s_state[i] = s_state[--s_state_n];
|
||||
break;
|
||||
}
|
||||
if (s_state_n >= STATE_CAP) return NULL;
|
||||
}
|
||||
StateEnt *e = &s_state[s_state_n++];
|
||||
e->phys = phys; e->crc = crc; e->state = st;
|
||||
return e;
|
||||
}
|
||||
|
||||
static int worker_main(void *unused) {
|
||||
(void)unused;
|
||||
const CodeProvider *cp = code_provider_sljit();
|
||||
SDL_LockMutex(s_mtx);
|
||||
for (;;) {
|
||||
while (s_running && s_q_n == 0) SDL_CondWait(s_cv, s_mtx);
|
||||
if (!s_running && s_q_n == 0) break;
|
||||
WorkItem it = s_q[s_q_head];
|
||||
s_q_head = (s_q_head + 1) % WQ_CAP;
|
||||
s_q_n--;
|
||||
{ StateEnt *e = state_find(it.phys, it.crc); if (e) e->state = ST_COMPILING; }
|
||||
SDL_UnlockMutex(s_mtx);
|
||||
|
||||
/* Compile off-thread FROM THE SNAPSHOT (base = phys, so the fragment's
|
||||
* byte offset into the buffer is entry_phys - phys). Never live RAM. */
|
||||
int ok = 0;
|
||||
uint32_t ev_lo = 0, ev_len = 0;
|
||||
Uint32 t0 = SDL_GetTicks();
|
||||
if (cp && cp->compile_fragment && it.snap) {
|
||||
CompiledFragment frag;
|
||||
memset(&frag, 0, sizeof frag);
|
||||
cp->compile_fragment(it.vaddr, it.snap, it.snap_len, it.phys, &frag);
|
||||
if (frag.fn && frag.serialized && frag.serialized_size && frag.code_len &&
|
||||
frag.code_lo >= it.phys &&
|
||||
(frag.code_lo - it.phys) + frag.code_len <= it.snap_len) {
|
||||
/* crc over the EXACT compiled range within the snapshot — this is
|
||||
* what dispatch re-hashes live RAM against when it loads the shard
|
||||
* (matches persist_sljit_shard's crc-over-code-range). */
|
||||
uint32_t off = frag.code_lo - it.phys;
|
||||
uint32_t crc = crc32_update(0xFFFFFFFFu, it.snap + off, frag.code_len)
|
||||
^ 0xFFFFFFFFu;
|
||||
overlay_loader_async_publish(it.phys, frag.code_lo, frag.code_len,
|
||||
crc, frag.serialized, frag.serialized_size);
|
||||
ev_lo = frag.code_lo; ev_len = frag.code_len;
|
||||
ok = 1;
|
||||
}
|
||||
if (frag.serialized) overlay_sljit_free_serialized(frag.serialized);
|
||||
}
|
||||
uint32_t dur = (uint32_t)(SDL_GetTicks() - t0);
|
||||
free(it.snap);
|
||||
|
||||
SDL_LockMutex(s_mtx);
|
||||
{ StateEnt *e = state_find(it.phys, it.crc); if (e) e->state = ok ? ST_DONE : ST_FAILED; }
|
||||
if (ok) s_done++; else s_failed++;
|
||||
/* record into the recent-compile ring */
|
||||
OverlayCompileEvent *ev = &s_evt[s_evt_seq % EVT_CAP];
|
||||
ev->vaddr = it.vaddr; ev->phys = it.phys; ev->crc = it.crc;
|
||||
ev->code_lo = ev_lo; ev->code_len = ev_len;
|
||||
ev->compile_ms = dur; ev->ok = (uint8_t)ok;
|
||||
s_evt_seq++;
|
||||
s_last_ms = dur; if (dur > s_max_ms) s_max_ms = dur;
|
||||
}
|
||||
SDL_UnlockMutex(s_mtx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void overlay_compile_worker_start(void) {
|
||||
if (s_thread) return;
|
||||
if (!s_mtx) s_mtx = SDL_CreateMutex();
|
||||
if (!s_cv) s_cv = SDL_CreateCond();
|
||||
if (!s_mtx || !s_cv) return;
|
||||
s_running = 1;
|
||||
s_thread = SDL_CreateThread(worker_main, "psx-sljit-compile", NULL);
|
||||
if (!s_thread) s_running = 0;
|
||||
}
|
||||
|
||||
void overlay_compile_worker_stop(void) {
|
||||
if (!s_thread) return;
|
||||
SDL_LockMutex(s_mtx);
|
||||
s_running = 0;
|
||||
SDL_CondSignal(s_cv);
|
||||
SDL_UnlockMutex(s_mtx);
|
||||
SDL_WaitThread(s_thread, NULL);
|
||||
s_thread = NULL;
|
||||
SDL_LockMutex(s_mtx);
|
||||
while (s_q_n) { free(s_q[s_q_head].snap); s_q_head = (s_q_head + 1) % WQ_CAP; s_q_n--; }
|
||||
SDL_UnlockMutex(s_mtx);
|
||||
}
|
||||
|
||||
void overlay_compile_worker_enqueue(uint32_t vaddr, uint32_t phys,
|
||||
const uint8_t *src, uint32_t snap_len,
|
||||
uint32_t crc) {
|
||||
if (!s_thread || !src || snap_len == 0) return;
|
||||
if (snap_len > SNAP_MAX) snap_len = SNAP_MAX;
|
||||
SDL_LockMutex(s_mtx);
|
||||
if (state_find(phys, crc) || s_q_n >= WQ_CAP) { SDL_UnlockMutex(s_mtx); return; }
|
||||
uint8_t *copy = (uint8_t *)malloc(snap_len);
|
||||
if (!copy) { SDL_UnlockMutex(s_mtx); return; }
|
||||
memcpy(copy, src, snap_len);
|
||||
if (!state_add(phys, crc, ST_QUEUED)) { free(copy); SDL_UnlockMutex(s_mtx); return; }
|
||||
WorkItem *it = &s_q[s_q_tail];
|
||||
it->vaddr = vaddr; it->phys = phys; it->crc = crc; it->snap_len = snap_len; it->snap = copy;
|
||||
s_q_tail = (s_q_tail + 1) % WQ_CAP;
|
||||
s_q_n++;
|
||||
s_enq++;
|
||||
SDL_CondSignal(s_cv);
|
||||
SDL_UnlockMutex(s_mtx);
|
||||
}
|
||||
|
||||
void overlay_compile_worker_stats(uint64_t *enqueued, uint64_t *compiled,
|
||||
uint64_t *failed, uint32_t *queued_now) {
|
||||
if (s_mtx) SDL_LockMutex(s_mtx);
|
||||
if (enqueued) *enqueued = s_enq;
|
||||
if (compiled) *compiled = s_done;
|
||||
if (failed) *failed = s_failed;
|
||||
if (queued_now) *queued_now = (uint32_t)s_q_n;
|
||||
if (s_mtx) SDL_UnlockMutex(s_mtx);
|
||||
}
|
||||
|
||||
int overlay_compile_worker_running(void) { return s_thread != NULL; }
|
||||
|
||||
void overlay_compile_worker_timing(uint32_t *last_ms, uint32_t *max_ms) {
|
||||
if (s_mtx) SDL_LockMutex(s_mtx);
|
||||
if (last_ms) *last_ms = s_last_ms;
|
||||
if (max_ms) *max_ms = s_max_ms;
|
||||
if (s_mtx) SDL_UnlockMutex(s_mtx);
|
||||
}
|
||||
|
||||
int overlay_compile_worker_recent(OverlayCompileEvent *out, int cap) {
|
||||
if (!out || cap <= 0) return 0;
|
||||
if (s_mtx) SDL_LockMutex(s_mtx);
|
||||
uint64_t total = s_evt_seq;
|
||||
int avail = (total < (uint64_t)EVT_CAP) ? (int)total : EVT_CAP;
|
||||
if (avail > cap) avail = cap;
|
||||
/* chronological (oldest first): the oldest of the kept window is at
|
||||
* (seq - avail). */
|
||||
uint64_t start = total - (uint64_t)avail;
|
||||
for (int i = 0; i < avail; i++)
|
||||
out[i] = s_evt[(start + (uint64_t)i) % EVT_CAP];
|
||||
if (s_mtx) SDL_UnlockMutex(s_mtx);
|
||||
return avail;
|
||||
}
|
||||
+41
-601
@@ -1,7 +1,6 @@
|
||||
#include "overlay_loader.h"
|
||||
#include "overlay_api.h"
|
||||
#include "code_provider.h"
|
||||
#include "overlay_sljit.h"
|
||||
#include "overlay_backend.h"
|
||||
#include "crc32.h"
|
||||
#include "dirty_ram_interp.h"
|
||||
@@ -144,8 +143,8 @@ static void range_index_add_candidate(int ci) {
|
||||
}
|
||||
|
||||
/* RECURSION_BUG.md §25 — 1 when the build is continuation-passing (set by a
|
||||
* constructor in the generated CPS dispatch). The overlay dispatch + sljit JIT
|
||||
* read it to emit/route the CPS tail-transfer contract. Defined here (before
|
||||
* constructor in the generated CPS dispatch). The overlay dispatch reads it to
|
||||
* emit/route the CPS tail-transfer contract. Defined here (before
|
||||
* overlay_loader_dispatch) so both can see it. 0 = legacy unit-model. */
|
||||
int g_psx_cps_mode = 0;
|
||||
|
||||
@@ -333,15 +332,8 @@ static int s_shadow_saved_supp = 0;
|
||||
static uint64_t s_shadow_epoch = 0;
|
||||
static uint32_t s_shadow_escapes = 0;
|
||||
static uint32_t s_shadow_escapes_native = 0;
|
||||
/* Live sljit mode (PSX_OVERLAY_SLJIT_LIVE / sljit_live debug cmd): JIT overlay
|
||||
* functions on-miss and run them LIVE without the per-shard differential — the
|
||||
* production model (sljit is the sync on-miss producer; safety is the emitter's
|
||||
* decline-on-unsupported contract, validated broadly). Off by default; the diff
|
||||
* gate above stays the dev path. Lets a developer feel the real toolchain-less
|
||||
* player experience (pure sljit + interp floor, no gcc, no diff overhead). */
|
||||
static int s_sljit_live = 0;
|
||||
void overlay_loader_set_sljit_live(int on) { s_sljit_live = on ? 1 : 0; }
|
||||
int overlay_loader_get_sljit_live(void) { return s_sljit_live; }
|
||||
/* (sljit removed 2026-07-15: the deprecated in-process JIT tier and its
|
||||
* live-execution mode are gone; overlay gaps fall to the interpreter.) */
|
||||
static void run_shadow_diff(CPUState *cpu, Candidate *c, uint32_t addr);
|
||||
|
||||
/* ---- Counters (surfaced via overlay_loader_status) --------------------- */
|
||||
@@ -366,8 +358,6 @@ static uint64_t s_diffgate_interp = 0; /* CPS interior re-entries sent to the
|
||||
static uint32_t s_last_crc = 0;
|
||||
static uint32_t s_no_manifest = 0; /* exports skipped (no manifest range)*/
|
||||
static uint32_t s_selfmod = 0; /* entries blacklisted (self-mod) */
|
||||
static uint32_t s_sljit_registered = 0; /* sljit Tier-2 shards registered */
|
||||
static uint32_t s_sljit_obsoleted = 0; /* sljit shards superseded by a gcc DLL*/
|
||||
static uint32_t s_last_write_pc = 0;
|
||||
static uint32_t s_last_write_addr = 0;
|
||||
static uint32_t s_last_write_size = 0;
|
||||
@@ -607,155 +597,14 @@ static void cand_register(uint32_t phys, OverlayFn fn, const ManFn *m, int dll)
|
||||
idx_set_head(phys, idx);
|
||||
range_index_add_candidate(idx);
|
||||
if (c->state == ENTRY_VALID) s_valid_count++;
|
||||
|
||||
/* Obsolescence (load priority: static > gcc shard > sljit shard > interp). A
|
||||
* gcc DLL is the optimized, dev-validated artifact and OUTRANKS an sljit shard
|
||||
* a user machine JIT'd for the same function. Once this gcc candidate is in the
|
||||
* chain it already wins dispatch (it's at the head, ahead of the older sljit
|
||||
* shard, and content-keyed dispatch picks the first match), but we also
|
||||
* explicitly retire the superseded sljit shard so it can never run — even if
|
||||
* this gcc candidate is later self-mod-blacklisted.
|
||||
*
|
||||
* "Same function/content" is keyed on CURRENT LIVE-RAM VALIDITY, not crc_code
|
||||
* equality: the two backends hash DIFFERENT byte ranges for the same function
|
||||
* (sljit hashes its contiguous JIT fragment [entry, terminator]; the gcc
|
||||
* .ranges manifest hashes the recompiler's per-function instruction walk, with
|
||||
* interleaved jump-tables/data excised), so their crc_code values legitimately
|
||||
* differ even for identical source bytes. The backend-independent test is: this
|
||||
* gcc candidate matches live RAM (c->state == VALID, computed above) AND the
|
||||
* sljit shard at the same entry also matches live RAM right now — i.e. both
|
||||
* describe the CURRENTLY-LIVE variant. An sljit shard for a DIFFERENT overlay
|
||||
* variant at this address does NOT match live and is kept (distinct coverage). */
|
||||
if (c->state == ENTRY_VALID) {
|
||||
for (int j = c->next; j >= 0; j = s_cand[j].next) {
|
||||
Candidate *o = &s_cand[j];
|
||||
if (o->dll == -1 && o->state != ENTRY_BLACKLIST &&
|
||||
cand_crc(o) == o->crc_code) { /* o matches live => same variant */
|
||||
if (o->state == ENTRY_VALID && s_valid_count > 0) s_valid_count--;
|
||||
o->state = ENTRY_BLACKLIST;
|
||||
s_sljit_obsoleted++;
|
||||
loader_log("sljit shard 0x%08X obsoleted by gcc DLL", o->addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* (sljit removed 2026-07-15: the gcc-DLL-obsoletes-older-sljit-shard block
|
||||
* lived here. With no sljit shards ever registered (dll == -1), there is
|
||||
* nothing to obsolete; the normal content-keyed chain dispatch stands.) */
|
||||
}
|
||||
|
||||
/* ---- sljit Tier-2 shard registration (SLJIT.md §7 step 5) -------------- */
|
||||
/* Register an in-process JIT shard as a native candidate, parallel to
|
||||
* cand_register but without a .ranges manifest: the shard was JIT'd from the
|
||||
* live RAM bytes over [lo, lo+len), so crc_code is hashed from those same bytes
|
||||
* — a later dispatch runs the shard iff the code is still byte-identical
|
||||
* (reload-on-return / self-mod safety, the same live-byte contract gcc
|
||||
* candidates obey). */
|
||||
/* crc_override: 0 => hash the crc from live RAM (an in-session JIT, where the
|
||||
* overlay bytes ARE loaded). Non-zero => use it verbatim — a shard RELOADED from
|
||||
* the persisted cache at init, where the overlay code may not be in RAM yet; the
|
||||
* blob header carries the crc it was compiled from, and dispatch re-hashes live
|
||||
* RAM against it so the shard only runs once the identical bytes are present. */
|
||||
static void register_sljit_candidate(uint32_t phys, OverlayFn fn,
|
||||
uint32_t lo, uint32_t len, uint32_t crc_override) {
|
||||
if (s_cand_n >= CAND_CAP || len == 0) return;
|
||||
int idx = s_cand_n++;
|
||||
Candidate *c = &s_cand[idx];
|
||||
c->addr = phys & 0x1FFFFFFFu;
|
||||
c->fn = fn;
|
||||
c->dll = -1; /* sentinel: sljit shard, not a DLL */
|
||||
c->nranges = 1;
|
||||
c->range_lo[0] = lo & 0x1FFFFFFFu;
|
||||
c->range_len[0] = len;
|
||||
extern void overlay_watch_set_range(uint32_t phys, uint32_t len);
|
||||
overlay_watch_set_range(c->range_lo[0], c->range_len[0]);
|
||||
c->crc_code = crc_override ? crc_override : cand_crc(c); /* live bytes == JIT'd-from bytes */
|
||||
c->val_gen = cand_gensum(c);
|
||||
c->state = ENTRY_VALID;
|
||||
c->diff_passes = 0;
|
||||
c->device_touch = 0;
|
||||
c->next = idx_head(c->addr);
|
||||
idx_set_head(c->addr, idx);
|
||||
range_index_add_candidate(idx);
|
||||
s_valid_count++;
|
||||
s_sljit_registered++;
|
||||
loader_log("sljit shard registered 0x%08X [%u bytes]", c->addr, len);
|
||||
}
|
||||
|
||||
/* JIT-on-miss memo: phys entries already attempted (compiled or declined), so a
|
||||
* declined fragment isn't re-attempted every dispatch. */
|
||||
#define MAX_SLJIT_TRIED 512
|
||||
static uint32_t s_sljit_tried[MAX_SLJIT_TRIED];
|
||||
static int s_sljit_tried_n = 0;
|
||||
static int sljit_already_tried(uint32_t phys) {
|
||||
for (int i = 0; i < s_sljit_tried_n; i++)
|
||||
if (s_sljit_tried[i] == phys) return 1;
|
||||
return 0;
|
||||
}
|
||||
static void sljit_mark_tried(uint32_t phys) {
|
||||
if (s_sljit_tried_n < MAX_SLJIT_TRIED) s_sljit_tried[s_sljit_tried_n++] = phys;
|
||||
}
|
||||
|
||||
/* Persist a freshly-JIT'd shard's serialized LIR to the on-disk cache (defined
|
||||
* below, after the cache-path globals). */
|
||||
static void persist_sljit_shard(uint32_t entry_phys, uint32_t lo, uint32_t len,
|
||||
const void *blob, unsigned long blob_size);
|
||||
|
||||
#include "overlay_compile_worker.h"
|
||||
/* Off-main-thread sljit compile (docs/ASYNC_OVERLAY_COMPILE.md). The background
|
||||
* worker JITs from a snapshot and PUBLISHES the shard to the on-disk cache +
|
||||
* sets s_async_cache_dirty; the DISPATCH thread rescans-and-registers on its
|
||||
* next miss (candidate table stays single-threaded). PSX_SLJIT_SYNC=1 forces
|
||||
* the legacy synchronous JIT-on-miss path. */
|
||||
#ifdef _WIN32
|
||||
static volatile LONG s_async_cache_dirty = 0;
|
||||
# define async_cache_dirty_exchange(v) InterlockedExchange(&s_async_cache_dirty, (v))
|
||||
#else
|
||||
static volatile int s_async_cache_dirty = 0;
|
||||
# define async_cache_dirty_exchange(v) __atomic_exchange_n(&s_async_cache_dirty, (v), __ATOMIC_SEQ_CST)
|
||||
#endif
|
||||
static int g_sljit_async = 1;
|
||||
|
||||
/* MASTER SAFETY GATE (2026-06-25): the sljit EMITTER mistranslates some overlay
|
||||
* instruction(s). Proven on MMX6: sljit-only save-load softlocks/loopbacks, PURE
|
||||
* INTERP works (discriminator), and it reproduces in the SYNCHRONOUS path — so it
|
||||
* is the emitter, not the async worker. Until the emitter bug is found + fixed the
|
||||
* whole sljit tier is DISABLED by default: no shard READ (scan_sljit_cache_dir),
|
||||
* no shard GENERATE (worker / sync try / live gap-fill), every overlay miss falls
|
||||
* to the interpreter. gcc (DLL cache + autocompile) and overlay capture are
|
||||
* untouched, so priority is gcc > interp and dispatch misses are still recorded
|
||||
* for future gcc coverage. PSX_SLJIT_ENABLE=1 re-enables it for emitter-fix
|
||||
* testing. See memory mmx6_sljit_shard_malformed. */
|
||||
static int g_sljit_tier_enabled = 0;
|
||||
|
||||
/* Attempt an in-process JIT of the leaf function at `phys` from live RAM, and
|
||||
* register the shard as a candidate on success. Gated by the caller to the
|
||||
* validation configuration only (backend==sljit + diff_mode); see the dispatch
|
||||
* hook. Decodes from live RAM exactly as the interpreter does. */
|
||||
static void try_sljit_region(uint32_t addr) {
|
||||
uint32_t phys = addr & 0x1FFFFFFFu;
|
||||
/* Use the sljit provider specifically (not the active one): gcc is the
|
||||
* primary/batch provider with compile_fragment == NULL, so the synchronous
|
||||
* JIT-on-miss gap-fill must go through sljit even when gcc is "active". */
|
||||
const CodeProvider *cp = code_provider_sljit();
|
||||
if (!cp || !cp->compile_fragment) return;
|
||||
if (sljit_already_tried(phys)) return;
|
||||
extern int dirty_ram_is_dirty(uint32_t phys);
|
||||
if (!dirty_ram_is_dirty(phys)) return; /* only JIT real runtime code */
|
||||
sljit_mark_tried(phys);
|
||||
uint8_t *ram = memory_get_ram_ptr();
|
||||
if (!ram) return;
|
||||
CompiledFragment frag = {0};
|
||||
/* Decode from live RAM: bytes = RAM base, image_base_vram = 0 ⇒ byte offset =
|
||||
* (entry & 0x1FFFFFFF) = phys. Pass the VIRTUAL entry so return_pc and
|
||||
* jal/J targets carry the KSEG bits the guest uses — the interpreter computes
|
||||
* pc from the virtual address, and saved $ra values must match byte-exact. */
|
||||
cp->compile_fragment(addr, ram, 2u * 1024u * 1024u, 0u, &frag);
|
||||
if (frag.fn) {
|
||||
register_sljit_candidate(phys, (OverlayFn)frag.fn, frag.code_lo, frag.code_len, 0);
|
||||
/* Persist the position-independent shard so a later (possibly toolchain-
|
||||
* less) session reloads it instead of re-JITing through the interpreter. */
|
||||
persist_sljit_shard(phys, frag.code_lo, frag.code_len,
|
||||
frag.serialized, frag.serialized_size);
|
||||
}
|
||||
if (frag.serialized) overlay_sljit_free_serialized(frag.serialized);
|
||||
}
|
||||
/* (sljit removed 2026-07-15: register_sljit_candidate, the JIT-on-miss memo,
|
||||
* the async compile-worker glue, the master tier gate, and try_sljit_region
|
||||
* lived here. Overlay misses now fall to the interpreter.) */
|
||||
|
||||
/* ---- Global state ------------------------------------------------------ */
|
||||
|
||||
@@ -1046,9 +895,9 @@ static int cache_idx_has_basename(const char *fname) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Canonical cache arch-abi tag (see SLJIT.md §4 — caches are namespaced per
|
||||
* backend AND per target so a Windows-x64 gcc DLL and, later, a same-OS arm64
|
||||
* or an sljit blob for the same fragment never comingle). compile_overlays.py
|
||||
/* Canonical cache arch-abi tag (caches are namespaced per backend AND per
|
||||
* target so a Windows-x64 gcc DLL and, later, a same-OS arm64 build for the
|
||||
* same fragment never comingle). compile_overlays.py
|
||||
* computes the IDENTICAL string from platform.system()/machine(); keep the two
|
||||
* mappings in lockstep ("<os>-<arch>": win|linux|macos + x64|arm64|x86). */
|
||||
#if defined(_WIN32)
|
||||
@@ -1134,9 +983,8 @@ static void scan_one_cache_dir(const char *dir) {
|
||||
/* Scan the namespaced gcc cache: gcc/<arch-abi>/cg<codegen-ver>/. The codegen
|
||||
* version segment means a build with new emitter output reads a FRESH directory
|
||||
* and never picks up a stale DLL (old versions coexist on disk, no migration).
|
||||
* The sljit/<arch-abi>/ namespace is reserved (no on-disk blobs: sljit re-JITs
|
||||
* from the coverage manifest; see SLJIT.md §5.3). (Pre-1.0: no legacy fallback —
|
||||
* older flat / unversioned caches are simply ignored and regenerated.) */
|
||||
* (Pre-1.0: no legacy fallback — older flat / unversioned caches are simply
|
||||
* ignored and regenerated.) */
|
||||
/* Hardening (never-again for the silent cg-tag read≠write drift): when the loader
|
||||
* finds ZERO shards under its OWN cg-tag, check whether a SIBLING cg<...> folder in
|
||||
* the same tier holds shards. If so, the autocompile wrote to a different codegen
|
||||
@@ -1379,196 +1227,9 @@ static void scan_cache_dir(void) {
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- Persisted sljit shard cache (Stage 2, SLJIT_PERSIST_CACHE.md) -------- */
|
||||
/* JIT'd sljit shards are serialized (position-independent LIR — Stage 1) and
|
||||
* written to <cache>/<game_id>/sljit/<arch-abi>/cg<N>/<entry8>_<crc8>.sljit; at
|
||||
* init they are deserialized + regenerated for THIS process and registered as
|
||||
* dll<0 candidates. The arch-abi + codegen-version + helper-order tags namespace
|
||||
* and invalidate stale blobs. Priority is unchanged (gcc > sljit > interp): a gcc
|
||||
* DLL covering the same live function still supersedes a reloaded shard via the
|
||||
* obsolete path. Safety is unchanged too: a reloaded shard re-earns trust each
|
||||
* session through the same per-dispatch crc re-hash + same-state diff gate. */
|
||||
#define SLJIT_BLOB_MAGIC 0x534A4C53u /* 'SLJS' */
|
||||
#define SLJIT_BLOB_FORMAT_VER 2u /* v2: serialized with debug info (options=0) */
|
||||
#define SLJIT_HELPER_ORDER_VER 1u /* bump if the SLJIT_HLP_* order changes */
|
||||
|
||||
typedef struct {
|
||||
uint32_t magic, format_ver, helper_order_ver;
|
||||
uint32_t entry_phys, code_lo, code_len, crc_code, blob_size;
|
||||
} SljitBlobHeader;
|
||||
|
||||
static int s_sljit_persist = 0; /* write JIT'd shards to disk */
|
||||
static uint32_t s_sljit_reloaded = 0; /* shards loaded from disk at init */
|
||||
static uint32_t s_sljit_persist_calls = 0; /* persist_sljit_shard entered */
|
||||
static uint32_t s_sljit_persist_writes = 0; /* blobs actually written */
|
||||
static int s_persist_dbg = 0; /* last persist exit reason (1..6) */
|
||||
static uint32_t s_reload_seen = 0; /* .sljit files matched by the scan */
|
||||
static uint32_t s_reload_hdrbad = 0; /* files rejected on header validation */
|
||||
static uint32_t s_reload_deserfail = 0; /* files that failed deserialize */
|
||||
uint32_t overlay_loader_sljit_reload_seen(void) { return s_reload_seen; }
|
||||
uint32_t overlay_loader_sljit_reload_hdrbad(void) { return s_reload_hdrbad; }
|
||||
uint32_t overlay_loader_sljit_reload_deserfail(void){ return s_reload_deserfail; }
|
||||
uint32_t overlay_loader_sljit_reloaded(void) { return s_sljit_reloaded; }
|
||||
uint32_t overlay_loader_sljit_persist_calls(void) { return s_sljit_persist_calls; }
|
||||
uint32_t overlay_loader_sljit_persist_writes(void) { return s_sljit_persist_writes; }
|
||||
int overlay_loader_sljit_persist_on(void) { return s_sljit_persist; }
|
||||
int overlay_loader_sljit_persist_dbg(void) { return s_persist_dbg; }
|
||||
|
||||
static void sljit_cache_dir(char *buf, size_t n) {
|
||||
snprintf(buf, n, "%s/%s/sljit/%s/cg%d_%08x",
|
||||
s_cache_dir, s_game_id, PSX_OVERLAY_ARCH_ABI, PSX_OVERLAY_CODEGEN_VER,
|
||||
(unsigned)PSX_OVERLAY_CODEGEN_HASH);
|
||||
}
|
||||
|
||||
/* Public: the resolved on-disk sljit shard dir, so the sljit_async debug command
|
||||
* can report exactly where the worker writes (no guessing about cache location). */
|
||||
void overlay_loader_sljit_cache_dir(char *buf, int n) {
|
||||
if (buf && n > 0) sljit_cache_dir(buf, (size_t)n);
|
||||
}
|
||||
int overlay_loader_sljit_async_on(void) { return g_sljit_async; }
|
||||
int overlay_loader_sljit_tier_enabled(void) { return g_sljit_tier_enabled; }
|
||||
|
||||
#ifdef _WIN32
|
||||
static void sljit_mkdir_p(const char *path) {
|
||||
char tmp[768];
|
||||
snprintf(tmp, sizeof(tmp), "%s", path);
|
||||
for (char *p = tmp + 1; *p; p++) {
|
||||
if (*p == '/' || *p == '\\') { char sep = *p; *p = '\0'; CreateDirectoryA(tmp, NULL); *p = sep; }
|
||||
}
|
||||
CreateDirectoryA(tmp, NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void persist_sljit_shard(uint32_t entry_phys, uint32_t lo, uint32_t len,
|
||||
const void *blob, unsigned long blob_size) {
|
||||
#ifdef _WIN32
|
||||
s_sljit_persist_calls++;
|
||||
if (!s_sljit_persist) { s_persist_dbg = 1; return; }
|
||||
if (!blob || blob_size == 0) { s_persist_dbg = 2; return; }
|
||||
if (len == 0) { s_persist_dbg = 3; return; }
|
||||
const uint8_t *ram = memory_get_ram_ptr();
|
||||
if (!ram) { s_persist_dbg = 4; return; }
|
||||
/* crc over the live bytes the shard was JIT'd from — identical to cand_crc()
|
||||
* for this single range, so the reloaded candidate's crc matches what
|
||||
* dispatch re-hashes. */
|
||||
uint32_t crc = crc32_update(0xFFFFFFFFu, ram + (lo & 0x1FFFFFFFu), len) ^ 0xFFFFFFFFu;
|
||||
char dir[768]; sljit_cache_dir(dir, sizeof(dir));
|
||||
sljit_mkdir_p(dir);
|
||||
char path[860];
|
||||
snprintf(path, sizeof(path), "%s/%08X_%08X.sljit", dir, entry_phys & 0x1FFFFFFFu, crc);
|
||||
FILE *f = fopen(path, "wb");
|
||||
if (!f) { s_persist_dbg = 5; return; }
|
||||
SljitBlobHeader hh;
|
||||
hh.magic = SLJIT_BLOB_MAGIC; hh.format_ver = SLJIT_BLOB_FORMAT_VER;
|
||||
hh.helper_order_ver = SLJIT_HELPER_ORDER_VER;
|
||||
hh.entry_phys = entry_phys & 0x1FFFFFFFu; hh.code_lo = lo & 0x1FFFFFFFu;
|
||||
hh.code_len = len; hh.crc_code = crc; hh.blob_size = (uint32_t)blob_size;
|
||||
fwrite(&hh, sizeof(hh), 1, f);
|
||||
fwrite(blob, 1, blob_size, f);
|
||||
fclose(f);
|
||||
s_sljit_persist_writes++;
|
||||
s_persist_dbg = 6;
|
||||
loader_log("sljit shard persisted %08X_%08X [%lu bytes]",
|
||||
entry_phys & 0x1FFFFFFFu, crc, blob_size);
|
||||
#else
|
||||
(void)entry_phys; (void)lo; (void)len; (void)blob; (void)blob_size;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Worker-thread publish (overlay_compile_worker.c): write a freshly-JIT'd shard
|
||||
* into the on-disk cache ATOMICALLY (temp + rename, so dispatch's scan never
|
||||
* reads a partial file) with an EXPLICIT crc (computed by the worker over the
|
||||
* snapshot's compiled range, NOT live RAM), then flag dispatch to rescan. Only
|
||||
* the filesystem + read-only-after-init cache-path globals are touched — never
|
||||
* the candidate table — so it is safe off the dispatch thread. */
|
||||
void overlay_loader_async_publish(uint32_t entry_phys, uint32_t lo, uint32_t len,
|
||||
uint32_t crc, const void *blob,
|
||||
unsigned long blob_size) {
|
||||
#ifdef _WIN32
|
||||
if (!s_sljit_persist || !blob || blob_size == 0 || len == 0) return;
|
||||
char dir[768]; sljit_cache_dir(dir, sizeof(dir));
|
||||
sljit_mkdir_p(dir);
|
||||
char path[860], tmp[920];
|
||||
snprintf(path, sizeof(path), "%s/%08X_%08X.sljit", dir, entry_phys & 0x1FFFFFFFu, crc);
|
||||
snprintf(tmp, sizeof(tmp), "%s.tmp%lu", path, (unsigned long)GetCurrentThreadId());
|
||||
FILE *f = fopen(tmp, "wb");
|
||||
if (!f) return;
|
||||
SljitBlobHeader hh;
|
||||
hh.magic = SLJIT_BLOB_MAGIC; hh.format_ver = SLJIT_BLOB_FORMAT_VER;
|
||||
hh.helper_order_ver = SLJIT_HELPER_ORDER_VER;
|
||||
hh.entry_phys = entry_phys & 0x1FFFFFFFu; hh.code_lo = lo & 0x1FFFFFFFu;
|
||||
hh.code_len = len; hh.crc_code = crc; hh.blob_size = (uint32_t)blob_size;
|
||||
int wok = (fwrite(&hh, sizeof hh, 1, f) == 1) &&
|
||||
(fwrite(blob, 1, blob_size, f) == blob_size);
|
||||
fclose(f);
|
||||
if (!wok) { remove(tmp); return; }
|
||||
if (!MoveFileExA(tmp, path, MOVEFILE_REPLACE_EXISTING)) { remove(tmp); return; }
|
||||
s_sljit_persist_writes++; /* telemetry-only; benign cross-thread incr */
|
||||
async_cache_dirty_exchange(1);
|
||||
#else
|
||||
(void)entry_phys; (void)lo; (void)len; (void)crc; (void)blob; (void)blob_size;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Idempotency for the on-miss rescan: a dispatch-thread-only set of .sljit
|
||||
* filenames already deserialized+registered, so re-running scan_sljit_cache_dir
|
||||
* after a worker publish never double-registers. */
|
||||
#define MAX_LOADED_BLOBS 8192
|
||||
static char s_loaded_blobs[MAX_LOADED_BLOBS][24]; /* "XXXXXXXX_XXXXXXXX.sljit" */
|
||||
static int s_loaded_blob_n;
|
||||
static int blob_already_loaded(const char *name) {
|
||||
for (int i = 0; i < s_loaded_blob_n; i++)
|
||||
if (!strcmp(s_loaded_blobs[i], name)) return 1;
|
||||
return 0;
|
||||
}
|
||||
static void blob_mark_loaded(const char *name) {
|
||||
if (s_loaded_blob_n < MAX_LOADED_BLOBS) {
|
||||
strncpy(s_loaded_blobs[s_loaded_blob_n], name, 23);
|
||||
s_loaded_blobs[s_loaded_blob_n][23] = '\0';
|
||||
s_loaded_blob_n++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Reload persisted shards at init: deserialize + regenerate + register. The crc
|
||||
* comes from the blob header (overlay RAM may not be loaded yet; dispatch
|
||||
* re-hashes live RAM against it before ever running the shard). */
|
||||
static void scan_sljit_cache_dir(void) {
|
||||
#ifdef _WIN32
|
||||
char dir[768]; sljit_cache_dir(dir, sizeof(dir));
|
||||
char pattern[860];
|
||||
snprintf(pattern, sizeof(pattern), "%s/*.sljit", dir);
|
||||
WIN32_FIND_DATAA fd;
|
||||
HANDLE fh = FindFirstFileA(pattern, &fd);
|
||||
if (fh == INVALID_HANDLE_VALUE) return;
|
||||
do {
|
||||
s_reload_seen++;
|
||||
if (blob_already_loaded(fd.cFileName)) continue; /* idempotent rescan */
|
||||
char full[900];
|
||||
snprintf(full, sizeof(full), "%s/%s", dir, fd.cFileName);
|
||||
FILE *f = fopen(full, "rb");
|
||||
if (!f) continue;
|
||||
SljitBlobHeader hd;
|
||||
if (fread(&hd, sizeof(hd), 1, f) != 1) { fclose(f); s_reload_hdrbad++; continue; }
|
||||
if (hd.magic != SLJIT_BLOB_MAGIC || hd.format_ver != SLJIT_BLOB_FORMAT_VER ||
|
||||
hd.helper_order_ver != SLJIT_HELPER_ORDER_VER ||
|
||||
hd.blob_size == 0 || hd.blob_size > (4u * 1024u * 1024u)) { fclose(f); s_reload_hdrbad++; continue; }
|
||||
void *blob = malloc(hd.blob_size);
|
||||
if (!blob) { fclose(f); continue; }
|
||||
size_t got = fread(blob, 1, hd.blob_size, f);
|
||||
fclose(f);
|
||||
if (got != hd.blob_size) { free(blob); s_reload_hdrbad++; continue; }
|
||||
OverlaySljitFn fn = overlay_sljit_deserialize(blob, hd.blob_size);
|
||||
free(blob);
|
||||
if (!fn) { s_reload_deserfail++; continue; }
|
||||
register_sljit_candidate(hd.entry_phys, (OverlayFn)fn,
|
||||
hd.code_lo, hd.code_len, hd.crc_code);
|
||||
s_sljit_reloaded++;
|
||||
blob_mark_loaded(fd.cFileName);
|
||||
} while (FindNextFileA(fh, &fd));
|
||||
FindClose(fh);
|
||||
if (s_sljit_reloaded) loader_log("sljit cache: reloaded %u shard(s)", s_sljit_reloaded);
|
||||
#endif
|
||||
}
|
||||
/* (sljit removed 2026-07-15: the persisted sljit shard cache — blob header,
|
||||
* persist/publish writers, on-disk reload scan, and their debug accessors —
|
||||
* lived here. No shards are produced or reloaded anymore.) */
|
||||
|
||||
/* True if the cache holds a DLL for this region compiled from an image with
|
||||
* this CRC (filename <addr8>_<crc8>.dll). Autocapture's "unseen" test. */
|
||||
@@ -1797,6 +1458,7 @@ static void overlay_dispatch_call_probed(CPUState *cpu, uint32_t addr, uint32_t
|
||||
|
||||
static void init_callbacks(void) {
|
||||
extern void psx_restore_state_escape(void);
|
||||
extern void psx_rfe_mark_escape(void);
|
||||
s_callbacks.dispatch_call = overlay_dispatch_call_probed;
|
||||
s_callbacks.check_interrupts = overlay_ci_wrapper;
|
||||
s_callbacks.check_interrupts_at = overlay_ci_at_wrapper;
|
||||
@@ -1814,6 +1476,8 @@ static void init_callbacks(void) {
|
||||
s_callbacks.log_call_entry = debug_server_log_call_entry;
|
||||
#endif
|
||||
s_callbacks.psx_restore_state_escape = psx_restore_state_escape;
|
||||
/* Return-from-exception mark (ABI v12): overlay `rfe` ops forward here. */
|
||||
s_callbacks.rfe_mark_escape = psx_rfe_mark_escape;
|
||||
/* Call-contract state (ABI v2): DLL code shares the runtime's bail
|
||||
* flag and counters through these pointers. */
|
||||
s_callbacks.call_bail_flag = &g_psx_call_bail;
|
||||
@@ -2051,22 +1715,8 @@ void overlay_loader_init(const char *cache_dir, const char *game_id) {
|
||||
scan_cache_dir();
|
||||
overlay_image_warm_init();
|
||||
overlay_image_warm_seed_boot_text();
|
||||
/* Persisted sljit shard cache (Stage 2): the cache is enabled (this init only
|
||||
* runs when [runtime] overlay_cache is on), so persist JIT'd shards and reload
|
||||
* any from a prior session now. Reloaded shards register as dll<0 candidates
|
||||
* and re-earn trust via the normal per-dispatch crc + diff gate. */
|
||||
{ const char *e = getenv("PSX_SLJIT_ENABLE"); if (e && *e && *e != '0') g_sljit_tier_enabled = 1; }
|
||||
if (g_sljit_tier_enabled) {
|
||||
s_sljit_persist = 1;
|
||||
scan_sljit_cache_dir();
|
||||
{ const char *e = getenv("PSX_SLJIT_SYNC"); if (e && *e && *e != '0') g_sljit_async = 0; }
|
||||
if (g_sljit_async) overlay_compile_worker_start(); /* off-thread JIT */
|
||||
} else {
|
||||
/* Tier disabled (default): no read, no generate. persist off so the worker
|
||||
* publish + persist_sljit_shard are inert even if reached. gcc > interp. */
|
||||
s_sljit_persist = 0;
|
||||
loader_log("sljit tier DISABLED (emitter bug; gcc>interp). PSX_SLJIT_ENABLE=1 to re-enable");
|
||||
}
|
||||
/* (sljit removed 2026-07-15: the persisted-shard reload + off-thread compile
|
||||
* worker were started here. Overlay misses fall to the interpreter.) */
|
||||
/* Pre-seed native-block bisection list from PSX_NATIVE_BLOCK (comma/space
|
||||
* separated hex addrs). Lets us route init-time overlay functions through the
|
||||
* sanctioned dirty-RAM interpreter from the FIRST boot instruction — the
|
||||
@@ -2149,44 +1799,13 @@ void overlay_loader_init(const char *cache_dir, const char *game_id) {
|
||||
loader_log("PSX_OVERLAY_DIFF set: native/interp shadow diff ON from boot");
|
||||
}
|
||||
}
|
||||
/* Live-mode policy is applied later via overlay_loader_apply_live_policy(),
|
||||
* AFTER code_provider_init resolves the backend (the default depends on it). */
|
||||
s_active = 1;
|
||||
}
|
||||
|
||||
/* Apply the sljit live-execution policy once the Tier-2 backend is resolved
|
||||
* (call AFTER code_provider_init). Default: live ON when the resolved backend is
|
||||
* sljit (the toolchain-less production model — JIT on miss, VALIDATE via the
|
||||
* same-state diff, promote to native only on a clean verify budget; device +
|
||||
* diverging shards stay on the interpreter), OFF otherwise. The
|
||||
* PSX_OVERLAY_SLJIT_LIVE env var overrides either way (0 forces off, nonzero
|
||||
* forces on) — a dev escape hatch. NOTE: "live" here is VALIDATED-live, not the
|
||||
* old blind path; see overlay_loader_dispatch's diff gate. */
|
||||
void overlay_loader_apply_live_policy(void) {
|
||||
/* DEFAULT-ON (validated): sljit is live whenever it is AVAILABLE, so it fills
|
||||
* gcc-absent overlay regions even on a gcc dev box (gcc > sljit > interp).
|
||||
* "Live" is VALIDATED-live — shards still route through the same-state diff
|
||||
* gate (see overlay_loader_dispatch) and device-touching / diverging shards
|
||||
* stay on the interpreter, so default-on cannot reintroduce the save-load
|
||||
* wedge. The PSX_OVERLAY_SLJIT_LIVE env var still overrides (0 forces off). */
|
||||
int live = (code_provider_sljit() != NULL) ? 1 : 0;
|
||||
/* Mutually exclusive (user preference, RECURSION_BUG.md §25): if gcc is the
|
||||
* active backend, do NOT run sljit live. The gcc autocompile produces
|
||||
* full-coverage CPS DLLs in the background; the gap before they land is the
|
||||
* interpreter (relatively fast), not sljit leaf shards. So gcc boxes are
|
||||
* gcc>interp; toolchain-less boxes are sljit>interp. PSX_OVERLAY_SLJIT_LIVE
|
||||
* still overrides for testing. */
|
||||
if (overlay_backend_active() == OVERLAY_BACKEND_GCC) live = 0;
|
||||
const char *e = getenv("PSX_OVERLAY_SLJIT_LIVE");
|
||||
if (e && *e) live = (*e != '0') ? 1 : 0;
|
||||
/* MASTER GATE: the sljit tier is disabled by default (emitter bug) — never go
|
||||
* live, regardless of backend/env, until PSX_SLJIT_ENABLE=1. */
|
||||
if (!g_sljit_tier_enabled) live = 0;
|
||||
s_sljit_live = live;
|
||||
loader_log("sljit live policy: active_backend=%s sljit_avail=%d env=%s -> live=%d",
|
||||
overlay_backend_name(overlay_backend_active()),
|
||||
code_provider_sljit() != NULL, e && *e ? e : "(unset)", live);
|
||||
}
|
||||
/* (sljit removed 2026-07-15: overlay_loader_apply_live_policy resolved the
|
||||
* sljit live-execution policy once the Tier-2 backend was known. With the tier
|
||||
* gone there is no live policy to apply; the interpreter is the overlay-miss
|
||||
* floor unconditionally.) */
|
||||
|
||||
void overlay_loader_check_cache(uint32_t load_addr, uint32_t size,
|
||||
const uint8_t *bytes) {
|
||||
@@ -2594,12 +2213,6 @@ retry_candidates:
|
||||
int lazy_exact = 0;
|
||||
int exact_needs_load = 0;
|
||||
if (head < 0 && s_active && overlay_cache_window_contains(phys)) {
|
||||
/* Pick up shards the background compile worker just published (idempotent
|
||||
* rescan; runs only when a publish has flipped the dirty flag). */
|
||||
if (g_sljit_tier_enabled && g_sljit_async && async_cache_dirty_exchange(0)) {
|
||||
scan_sljit_cache_dir();
|
||||
head = idx_head(phys);
|
||||
}
|
||||
lazy_exact = head < 0 && lazy_has_exact_entry(phys);
|
||||
/* A CPS continuation is normally not a registered function ENTRY. Its
|
||||
* already-loaded range owner must be checked before lazy discovery:
|
||||
@@ -2617,38 +2230,8 @@ retry_candidates:
|
||||
lazy_loaded = 1;
|
||||
goto retry_candidates;
|
||||
}
|
||||
/* sljit JIT-on-miss (SLJIT.md §7 step 5). Fires when the active backend is
|
||||
* sljit and EITHER the same-state differential is armed (dev validation
|
||||
* path — shards created then diffed vs interp) OR live mode is on (the
|
||||
* production model — shards created and run live, no per-shard diff).
|
||||
* Never inside a shadow run. */
|
||||
if (head < 0 && g_sljit_tier_enabled && (s_diff_mode || s_sljit_live) && !s_in_shadow &&
|
||||
code_provider_sljit() != NULL) {
|
||||
/* head < 0 == no gcc DLL (or any candidate) registered for this region
|
||||
* after the cache-load attempt above => gcc-absent => sljit fills the
|
||||
* gap (priority gcc > sljit > interp). */
|
||||
if (g_sljit_async) {
|
||||
/* OFF-THREAD: snapshot the region bytes + enqueue for the worker;
|
||||
* interpret THIS pass. The shard registers on a later miss via the
|
||||
* rescan above — no synchronous JIT spike on the dispatch thread. */
|
||||
if (!sljit_already_tried(phys)) {
|
||||
extern int dirty_ram_is_dirty(uint32_t phys);
|
||||
uint8_t *ram = memory_get_ram_ptr();
|
||||
if (ram && dirty_ram_is_dirty(phys)) { /* only JIT runtime code */
|
||||
sljit_mark_tried(phys);
|
||||
uint32_t snap = 8u * 1024u; /* SLJIT_MAX_FRAG_INSNS*4 */
|
||||
if (phys + snap > 2u * 1024u * 1024u)
|
||||
snap = 2u * 1024u * 1024u - phys;
|
||||
uint32_t crc = crc32_update(0xFFFFFFFFu, ram + phys, snap)
|
||||
^ 0xFFFFFFFFu;
|
||||
overlay_compile_worker_enqueue(addr, phys, ram + phys, snap, crc);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try_sljit_region(addr); /* legacy synchronous path (PSX_SLJIT_SYNC=1) */
|
||||
head = idx_head(phys);
|
||||
}
|
||||
}
|
||||
/* (sljit removed 2026-07-15: the JIT-on-miss gap-fill — off-thread
|
||||
* enqueue or synchronous compile — ran here. Misses fall to interp.) */
|
||||
}
|
||||
|
||||
/* CPS (§25) continuation re-entry: phys is not an overlay ENTRY, but it may
|
||||
@@ -2709,7 +2292,7 @@ retry_candidates:
|
||||
* too — the blocklist blocks LIVE native, not the sandbox). */
|
||||
int in_own_shadow = (s_in_shadow && (const void *)c == s_shadow_cand);
|
||||
if (!in_own_shadow) {
|
||||
int want_diff = s_diff_mode || (s_sljit_live && c->dll < 0);
|
||||
int want_diff = s_diff_mode;
|
||||
if (want_diff && addr < 0x10000u) want_diff = 0;
|
||||
if (want_diff && c->diff_passes < OVERLAY_DIFF_BUDGET) {
|
||||
if (_probe) s_cps_probe_outcome = 5;
|
||||
@@ -2812,16 +2395,10 @@ retry_candidates:
|
||||
* times it's trusted and falls through to normal execution, so the
|
||||
* diff cost stays bounded (a diverging candidate never reaches the
|
||||
* budget — it keeps being diff-gated and never runs native live).
|
||||
*
|
||||
* VALIDATED-LIVE: live sljit mode (s_sljit_live) is NOT a separate
|
||||
* "JIT and run blind" path — it routes its sljit SHARDS (dll < 0)
|
||||
* through the SAME diff gate, so a shard runs native live only after a
|
||||
* clean verify budget, and run_shadow_diff's interp-first pass computes
|
||||
* device_touch (device functions get pinned to interp, never double-
|
||||
* executing I/O — the save-load wedge). gcc candidates (dll >= 0) are
|
||||
* the trusted tier (validated at dev time) and run native directly;
|
||||
* they are diffed only in explicit dev diff mode. */
|
||||
int want_diff = s_diff_mode || (s_sljit_live && c->dll < 0);
|
||||
* gcc/DLL candidates are the trusted tier (validated at dev time) and
|
||||
* run native directly; they are diffed only in explicit dev diff mode
|
||||
* (PSX_OVERLAY_DIFF / overlay_diff cmd). */
|
||||
int want_diff = s_diff_mode;
|
||||
/* Kernel-window candidates (call gates 0xA0/0xB0/0xC0 and the RAM
|
||||
* kernel) are NOT shadow-diffable: the gates tail-jump via RAM
|
||||
* tables into kernel SERVICES whose behavior depends on scheduler/
|
||||
@@ -3023,9 +2600,6 @@ void overlay_loader_get_reload_debug(int *r0_valid, uint32_t *r0_writes,
|
||||
|
||||
int overlay_loader_registered_count(void) { return s_valid_count; }
|
||||
|
||||
/* sljit Tier-2 shards registered as candidates this session (diagnostics). */
|
||||
uint32_t overlay_loader_sljit_registered(void) { return s_sljit_registered; }
|
||||
|
||||
/* Dispatches that ran native via the unchanged-page-generation fast path,
|
||||
* i.e. skipped the per-dispatch code-range crc32 (overlay-cache v2 P2). High
|
||||
* values relative to reval_attempts mean the warm-cache hot path is cheap. */
|
||||
@@ -3035,27 +2609,8 @@ int overlay_loader_range_index_overflow(void) { return s_range_index_overflow; }
|
||||
int overlay_loader_lazy_manifest_count(void) { return s_lazy_man_n; }
|
||||
int overlay_loader_lazy_manifest_overflow(void) { return s_lazy_man_overflow; }
|
||||
|
||||
/* sljit shards superseded by a higher-priority gcc DLL (same content). */
|
||||
uint32_t overlay_loader_sljit_obsoleted(void) { return s_sljit_obsoleted; }
|
||||
|
||||
/* Force a one-shot sljit JIT attempt of the leaf function at `addr` from live
|
||||
* RAM and register it on success (bypasses the diff-mode gate — a probe). For
|
||||
* the sljit_try debug command. Returns via the result struct. */
|
||||
void overlay_loader_sljit_probe(uint32_t addr, OverlaySljitResult *out) {
|
||||
out->fn = NULL; out->code_lo = 0; out->code_len = 0; out->insns = 0;
|
||||
uint8_t *ram = memory_get_ram_ptr();
|
||||
if (!ram) return;
|
||||
/* Virtual entry so return_pc / jal targets carry the KSEG bits (see
|
||||
* try_sljit_region); byte offset is still (entry & 0x1FFFFFFF) = phys. */
|
||||
overlay_sljit_try_compile(addr, ram, 2u * 1024u * 1024u, 0u, out);
|
||||
if (out->fn) {
|
||||
register_sljit_candidate(addr & 0x1FFFFFFFu, (OverlayFn)out->fn,
|
||||
out->code_lo, out->code_len, 0);
|
||||
persist_sljit_shard(addr & 0x1FFFFFFFu, out->code_lo, out->code_len,
|
||||
out->serialized, out->serialized_size);
|
||||
}
|
||||
if (out->serialized) { overlay_sljit_free_serialized(out->serialized); out->serialized = NULL; }
|
||||
}
|
||||
/* (sljit removed 2026-07-15: overlay_loader_sljit_obsoleted and the
|
||||
* overlay_loader_sljit_probe one-shot JIT helper lived here.) */
|
||||
|
||||
/* ---- Native↔interp execution fingerprint differential (§5-E) ----------- */
|
||||
/* For each CANDIDATE function execution we record the FULL register file at
|
||||
@@ -3181,8 +2736,8 @@ static void run_shadow_diff(CPUState *cpu, Candidate *c, uint32_t addr) {
|
||||
if (g_shadow_mmio_hits != mmio0) {
|
||||
/* Device-touching: keep the interp result live (already in *cpu/ram/spad),
|
||||
* mark the candidate so it ALWAYS runs via the interpreter (never its
|
||||
* shard, never re-diffed). sljit covers the pure-compute majority; device
|
||||
* functions stay on the interpreter — safe by construction, no double I/O. */
|
||||
* shard, never re-diffed). Device functions stay on the interpreter —
|
||||
* safe by construction, no double I/O. */
|
||||
c->device_touch = 1;
|
||||
s_shadow_skipped_dev++;
|
||||
g_psx_call_bail = 0;
|
||||
@@ -3450,124 +3005,9 @@ int overlay_loader_call_native(CPUState *cpu, uint32_t addr) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ---- jal/jalr call helper for sljit shards (SLJIT.md §7) ---------------- */
|
||||
/* A JIT'd shard calls this at every jal/jalr site instead of open-coding the
|
||||
* call contract. It reproduces the dirty-RAM interpreter's call path EXACTLY
|
||||
* (see exec_one cases 0x03 / SPECIAL 0x09 and dispatch_nonlocal_call), so a
|
||||
* shard-issued call behaves identically to an interpreted one — including the
|
||||
* wild-return / bail unwind that the contract guards (Bug A/C/D family). The
|
||||
* one principled difference from the interpreter's local-dirty fast path: a
|
||||
* native shard cannot resume itself block-by-block, so a not-yet-native callee
|
||||
* is run as a UNIT via psx_dispatch_call (which handles interp/dirty callees to
|
||||
* the return contract) rather than the interpreter's pc-chain. See overlay_sljit.h. */
|
||||
static int psx_sljit_call_inner(CPUState *cpu, uint32_t target, uint32_t return_pc,
|
||||
int check_contract);
|
||||
int psx_sljit_call(CPUState *cpu, uint32_t target, uint32_t return_pc,
|
||||
int check_contract) {
|
||||
dirty_ram_xprobe_call_note(cpu, target, return_pc, 0);
|
||||
int r = psx_sljit_call_inner(cpu, target, return_pc, check_contract);
|
||||
dirty_ram_xprobe_call_note(cpu, target, return_pc, 1);
|
||||
return r;
|
||||
}
|
||||
static int psx_sljit_call_inner(CPUState *cpu, uint32_t target, uint32_t return_pc,
|
||||
int check_contract) {
|
||||
uint32_t site_sp = cpu->gpr[29]; /* sp at the call (after the delay slot) */
|
||||
#ifdef PSX_HAS_GAME_DISPATCH
|
||||
/* Skip the compiled game function when its target is no longer native-safe;
|
||||
* fall through to the native/interp paths that run the live RAM bytes. */
|
||||
extern int psx_game_text_native_ok(uint32_t addr);
|
||||
if (psx_game_text_native_ok(target)) {
|
||||
extern int psx_dispatch_game_compiled(CPUState *cpu, uint32_t addr);
|
||||
cpu->pc = 0;
|
||||
int prev_phase = g_exec_phase;
|
||||
g_exec_phase = 3;
|
||||
int _gc = psx_dispatch_game_compiled(cpu, target);
|
||||
g_exec_phase = prev_phase;
|
||||
if (_gc) {
|
||||
if (g_psx_call_bail) return 1;
|
||||
if (cpu->pc != 0) return 1;
|
||||
if (check_contract && psx_call_contract(cpu, return_pc, site_sp)) return 1;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
cpu->pc = 0;
|
||||
if (overlay_loader_call_native(cpu, target)) {
|
||||
if (g_psx_call_bail) return 1;
|
||||
if (cpu->pc != 0) return 1;
|
||||
if (check_contract && psx_call_contract(cpu, return_pc, site_sp)) return 1;
|
||||
return 0;
|
||||
}
|
||||
/* Not a resolved compiled/native unit: dispatch the callee as a unit (the
|
||||
* interpreter's nonlocal path — handles interp/dirty callees + the return
|
||||
* contract internally). */
|
||||
cpu->pc = 0;
|
||||
{
|
||||
int prev_phase = g_exec_phase;
|
||||
g_exec_phase = 3; /* compiled route; dirty/native callees re-tag inside */
|
||||
psx_dispatch_call(cpu, target, return_pc);
|
||||
g_exec_phase = prev_phase;
|
||||
}
|
||||
if (g_psx_call_bail) return 1;
|
||||
if (cpu->pc != 0) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* COP2/GTE helper — mirrors dirty_ram_interp.c case 0x12 + LWC2/SWC2. */
|
||||
void psx_sljit_cop2(CPUState *cpu, uint32_t insn) {
|
||||
uint32_t op = (insn >> 26) & 0x3Fu, rs = (insn >> 21) & 0x1Fu;
|
||||
uint32_t rt = (insn >> 16) & 0x1Fu, rd = (insn >> 11) & 0x1Fu;
|
||||
if (op == 0x12) { /* COP2 */
|
||||
uint32_t cop_op = rs;
|
||||
if (cop_op == 0x00) { cpu->gpr[rt] = gte_read_data(cpu, (uint8_t)rd); cpu->gpr[0] = 0; } /* MFC2 */
|
||||
else if (cop_op == 0x02) { cpu->gpr[rt] = gte_read_ctrl(cpu, (uint8_t)rd); cpu->gpr[0] = 0; } /* CFC2 */
|
||||
else if (cop_op == 0x04) { gte_write_data(cpu, (uint8_t)rd, cpu->gpr[rt]); } /* MTC2 */
|
||||
else if (cop_op == 0x06) { gte_write_ctrl(cpu, (uint8_t)rd, cpu->gpr[rt]); } /* CTC2 */
|
||||
else if (cop_op & 0x10) { gte_execute(cpu, insn & 0x1FFFFFFu); } /* GTE cmd */
|
||||
return;
|
||||
}
|
||||
int32_t simm = (int32_t)(int16_t)(insn & 0xFFFFu);
|
||||
uint32_t addr = cpu->gpr[rs] + (uint32_t)simm;
|
||||
if (op == 0x32) { gte_write_data(cpu, (uint8_t)rt, cpu->read_word(addr)); } /* LWC2 */
|
||||
else if (op == 0x3A) { cpu->write_word(addr, gte_read_data(cpu, (uint8_t)rt)); gte_precision_store_word(addr, (uint8_t)rt); } /* SWC2 */
|
||||
}
|
||||
|
||||
/* Unaligned load/store helper — mirrors dirty_ram_interp.c interp_lwl/lwr/swl/swr
|
||||
* + cases 0x22/0x26/0x2A/0x2E. */
|
||||
void psx_sljit_memx(CPUState *cpu, uint32_t insn) {
|
||||
uint32_t op = (insn >> 26) & 0x3Fu, rs = (insn >> 21) & 0x1Fu, rt = (insn >> 16) & 0x1Fu;
|
||||
int32_t simm = (int32_t)(int16_t)(insn & 0xFFFFu);
|
||||
uint32_t addr = cpu->gpr[rs] + (uint32_t)simm;
|
||||
uint32_t aligned = addr & ~3u;
|
||||
uint32_t word = cpu->read_word(aligned);
|
||||
uint32_t sh = addr & 3u, rtv = cpu->gpr[rt], v;
|
||||
switch (op) {
|
||||
case 0x22: /* LWL */
|
||||
switch (sh) { case 0: v = (rtv & 0x00FFFFFFu) | (word << 24); break;
|
||||
case 1: v = (rtv & 0x0000FFFFu) | (word << 16); break;
|
||||
case 2: v = (rtv & 0x000000FFu) | (word << 8); break;
|
||||
default: v = word; }
|
||||
cpu->gpr[rt] = v; cpu->gpr[0] = 0; break;
|
||||
case 0x26: /* LWR */
|
||||
switch (sh) { case 0: v = word; break;
|
||||
case 1: v = (rtv & 0xFF000000u) | (word >> 8); break;
|
||||
case 2: v = (rtv & 0xFFFF0000u) | (word >> 16); break;
|
||||
default: v = (rtv & 0xFFFFFF00u) | (word >> 24); }
|
||||
cpu->gpr[rt] = v; cpu->gpr[0] = 0; break;
|
||||
case 0x2A: /* SWL */
|
||||
switch (sh) { case 0: word = (word & 0xFFFFFF00u) | (rtv >> 24); break;
|
||||
case 1: word = (word & 0xFFFF0000u) | (rtv >> 16); break;
|
||||
case 2: word = (word & 0xFF000000u) | (rtv >> 8); break;
|
||||
default: word = rtv; }
|
||||
cpu->write_word(aligned, word); break;
|
||||
case 0x2E: /* SWR */
|
||||
switch (sh) { case 0: word = rtv; break;
|
||||
case 1: word = (word & 0x000000FFu) | (rtv << 8); break;
|
||||
case 2: word = (word & 0x0000FFFFu) | (rtv << 16); break;
|
||||
default: word = (word & 0x00FFFFFFu) | (rtv << 24); }
|
||||
cpu->write_word(aligned, word); break;
|
||||
}
|
||||
}
|
||||
/* (sljit removed 2026-07-15: the JIT-shard host helpers psx_sljit_call /
|
||||
* psx_sljit_cop2 / psx_sljit_memx lived here. They were called only by sljit-
|
||||
* emitted shards via the cpu->sljit_helpers table, which is no longer wired.) */
|
||||
|
||||
/* Write the whole fingerprint log to a file (no TCP size limit). Returns the
|
||||
* number of entries written, or -1 on open failure. */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,276 +0,0 @@
|
||||
/* sljit_emit_test.c — standalone parity test for the MIPS->sljit leaf emitter
|
||||
* (SLJIT.md §7 step 4, first slice). Builds tiny leaf functions as MIPS words,
|
||||
* JITs them via overlay_sljit_try_compile, runs the produced host code against a
|
||||
* fake CPUState + RAM, and checks results against hand-computed semantics — plus
|
||||
* the decline path (anything outside the slice returns fn==NULL).
|
||||
*
|
||||
* Self-contained: overlay_sljit.c depends only on sljit + the CPUState struct.
|
||||
* gcc -O2 -I runtime/include -I lib/sljit/sljit_src \
|
||||
* runtime/tests/sljit_emit_test.c runtime/src/overlay_sljit.c \
|
||||
* lib/sljit/sljit_src/sljitLir.c -o /tmp/sljit_emit_test && /tmp/sljit_emit_test
|
||||
*/
|
||||
#include "cpu_state.h"
|
||||
#include "overlay_sljit.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
/* ---- fake guest RAM + the cpu read/write callbacks --------------------- */
|
||||
#define RAM_MASK 0x1FFFFu
|
||||
static uint8_t g_ram[RAM_MASK + 1];
|
||||
|
||||
static uint32_t rd_word(uint32_t a){ uint32_t v; memcpy(&v, &g_ram[a & (RAM_MASK & ~3u)], 4); return v; }
|
||||
static void wr_word(uint32_t a, uint32_t v){ memcpy(&g_ram[a & (RAM_MASK & ~3u)], &v, 4); }
|
||||
static uint16_t rd_half(uint32_t a){ uint16_t v; memcpy(&v, &g_ram[a & (RAM_MASK & ~1u)], 2); return v; }
|
||||
static void wr_half(uint32_t a, uint16_t v){ memcpy(&g_ram[a & (RAM_MASK & ~1u)], &v, 2); }
|
||||
static uint8_t rd_byte(uint32_t a){ return g_ram[a & RAM_MASK]; }
|
||||
static void wr_byte(uint32_t a, uint8_t v){ g_ram[a & RAM_MASK] = v; }
|
||||
|
||||
static void cpu_init(CPUState *c){
|
||||
memset(c, 0, sizeof *c);
|
||||
c->read_word = rd_word; c->write_word = wr_word;
|
||||
c->read_half = rd_half; c->write_half = wr_half;
|
||||
c->read_byte = rd_byte; c->write_byte = wr_byte;
|
||||
}
|
||||
|
||||
/* Stand-in for the runtime call helper (overlay_loader.c) so jal/jalr shards
|
||||
* link in the harness. Records the call and simulates a trivial callee that
|
||||
* returns normally — except a sentinel target that simulates a transfer/bail
|
||||
* (return 1), which must make the shard return immediately. */
|
||||
static uint32_t g_call_target, g_call_return; static int g_call_check, g_call_count;
|
||||
int psx_sljit_call(CPUState *cpu, uint32_t target, uint32_t return_pc, int check_contract){
|
||||
g_call_target = target; g_call_return = return_pc; g_call_check = check_contract;
|
||||
g_call_count++;
|
||||
if (target == 0x8000DEA0u) { cpu->pc = 0x8000BEEFu; return 1; } /* simulate bail */
|
||||
cpu->gpr[2] = target; /* v0 = target (observable by the continuation) */
|
||||
cpu->pc = 0;
|
||||
return 0; /* continue */
|
||||
}
|
||||
/* Link stubs for the GTE/unaligned helpers (validated live, not in the harness). */
|
||||
void psx_sljit_cop2(CPUState *cpu, uint32_t insn){ (void)cpu; (void)insn; }
|
||||
void psx_sljit_memx(CPUState *cpu, uint32_t insn){ (void)cpu; (void)insn; }
|
||||
|
||||
/* ---- MIPS encoders ----------------------------------------------------- */
|
||||
#define ZERO 0
|
||||
#define V0 2
|
||||
#define V1 3
|
||||
#define A0 4
|
||||
#define A1 5
|
||||
#define T0 8
|
||||
#define RA 31
|
||||
static uint32_t R(uint32_t op,uint32_t rs,uint32_t rt,uint32_t rd,uint32_t sh,uint32_t fn){
|
||||
return (op<<26)|(rs<<21)|(rt<<16)|(rd<<11)|(sh<<6)|fn;
|
||||
}
|
||||
static uint32_t I(uint32_t op,uint32_t rs,uint32_t rt,uint32_t imm){
|
||||
return (op<<26)|(rs<<21)|(rt<<16)|(imm & 0xFFFFu);
|
||||
}
|
||||
static uint32_t Jal(uint32_t target_virt){ return (3u<<26)|((target_virt>>2)&0x03FFFFFFu); }
|
||||
#define JR_RA R(0,RA,0,0,0,0x08)
|
||||
#define NOP 0u
|
||||
|
||||
/* ---- harness ----------------------------------------------------------- */
|
||||
static int g_pass=0, g_fail=0;
|
||||
#define CHECK(cond, ...) do{ if(cond){g_pass++;} else {g_fail++; printf("FAIL: "); printf(__VA_ARGS__); printf("\n"); } }while(0)
|
||||
|
||||
static const uint32_t BASE = 0x80010000u; /* pretend vram of the fragment */
|
||||
|
||||
static OverlaySljitFn jit(uint32_t *w, int n){
|
||||
OverlaySljitResult r = {0};
|
||||
overlay_sljit_try_compile(BASE, (const uint8_t*)w, (uint32_t)(n*4), BASE, &r);
|
||||
return r.fn;
|
||||
}
|
||||
|
||||
int main(void){
|
||||
CPUState cpu;
|
||||
|
||||
/* 1. ADDIU v0, a0, 5 */
|
||||
{ uint32_t w[]={ I(9,A0,V0,5), JR_RA, NOP };
|
||||
OverlaySljitFn fn=jit(w,3); CHECK(fn,"addiu: declined");
|
||||
if(fn){ cpu_init(&cpu); cpu.gpr[A0]=100; fn(&cpu);
|
||||
CHECK(cpu.gpr[V0]==105,"addiu: v0=%u",cpu.gpr[V0]); } }
|
||||
|
||||
/* 2. SLL v0, a0, 3 */
|
||||
{ uint32_t w[]={ R(0,0,A0,V0,3,0x00), JR_RA, NOP };
|
||||
OverlaySljitFn fn=jit(w,3); CHECK(fn,"sll: declined");
|
||||
if(fn){ cpu_init(&cpu); cpu.gpr[A0]=0x11; fn(&cpu);
|
||||
CHECK(cpu.gpr[V0]==(0x11u<<3),"sll: v0=0x%X",cpu.gpr[V0]); } }
|
||||
|
||||
/* 3. LW v0, 8(a0) */
|
||||
{ uint32_t w[]={ I(0x23,A0,V0,8), JR_RA, NOP };
|
||||
OverlaySljitFn fn=jit(w,3); CHECK(fn,"lw: declined");
|
||||
if(fn){ cpu_init(&cpu); cpu.gpr[A0]=0x1000; wr_word(0x1008,0xDEADBEEF); fn(&cpu);
|
||||
CHECK(cpu.gpr[V0]==0xDEADBEEF,"lw: v0=0x%X",cpu.gpr[V0]); } }
|
||||
|
||||
/* 4. SW a1, 12(a0) */
|
||||
{ uint32_t w[]={ I(0x2B,A0,A1,12), JR_RA, NOP };
|
||||
OverlaySljitFn fn=jit(w,3); CHECK(fn,"sw: declined");
|
||||
if(fn){ cpu_init(&cpu); cpu.gpr[A0]=0x1000; cpu.gpr[A1]=0x12345678; fn(&cpu);
|
||||
CHECK(rd_word(0x100C)==0x12345678,"sw: mem=0x%X",rd_word(0x100C)); } }
|
||||
|
||||
/* 5a. SLT v0, a0, a1 (signed: -5 < 3 => 1) */
|
||||
{ uint32_t w[]={ R(0,A0,A1,V0,0,0x2A), JR_RA, NOP };
|
||||
OverlaySljitFn fn=jit(w,3); CHECK(fn,"slt: declined");
|
||||
if(fn){ cpu_init(&cpu); cpu.gpr[A0]=(uint32_t)-5; cpu.gpr[A1]=3; fn(&cpu);
|
||||
CHECK(cpu.gpr[V0]==1,"slt signed: v0=%u",cpu.gpr[V0]); } }
|
||||
/* 5b. SLTU v0, a0, a1 (unsigned: 0xFFFFFFFB > 3 => 0) */
|
||||
{ uint32_t w[]={ R(0,A0,A1,V0,0,0x2B), JR_RA, NOP };
|
||||
OverlaySljitFn fn=jit(w,3); CHECK(fn,"sltu: declined");
|
||||
if(fn){ cpu_init(&cpu); cpu.gpr[A0]=(uint32_t)-5; cpu.gpr[A1]=3; fn(&cpu);
|
||||
CHECK(cpu.gpr[V0]==0,"sltu: v0=%u",cpu.gpr[V0]); } }
|
||||
|
||||
/* 6a. LB sign-extends 0x80 -> 0xFFFFFF80 */
|
||||
{ uint32_t w[]={ I(0x20,A0,V0,0), JR_RA, NOP };
|
||||
OverlaySljitFn fn=jit(w,3); CHECK(fn,"lb: declined");
|
||||
if(fn){ cpu_init(&cpu); cpu.gpr[A0]=0x2000; wr_byte(0x2000,0x80); fn(&cpu);
|
||||
CHECK(cpu.gpr[V0]==0xFFFFFF80u,"lb: v0=0x%X",cpu.gpr[V0]); } }
|
||||
/* 6b. LBU zero-extends 0x80 -> 0x80 */
|
||||
{ uint32_t w[]={ I(0x24,A0,V0,0), JR_RA, NOP };
|
||||
OverlaySljitFn fn=jit(w,3); CHECK(fn,"lbu: declined");
|
||||
if(fn){ cpu_init(&cpu); cpu.gpr[A0]=0x2000; wr_byte(0x2000,0x80); fn(&cpu);
|
||||
CHECK(cpu.gpr[V0]==0x80u,"lbu: v0=0x%X",cpu.gpr[V0]); } }
|
||||
|
||||
/* 7. gpr[0] invariant: ADDIU zero,a0,5 must NOT write; OR v0,zero,a0 reads 0 */
|
||||
{ uint32_t w[]={ I(9,A0,ZERO,5), R(0,ZERO,A0,V0,0,0x25), JR_RA, NOP };
|
||||
OverlaySljitFn fn=jit(w,4); CHECK(fn,"zero-inv: declined");
|
||||
if(fn){ cpu_init(&cpu); cpu.gpr[A0]=0xABCD; fn(&cpu);
|
||||
CHECK(cpu.gpr[ZERO]==0,"zero-inv: gpr0=%u",cpu.gpr[ZERO]);
|
||||
CHECK(cpu.gpr[V0]==0xABCD,"zero-inv: v0=0x%X (or zero,a0)",cpu.gpr[V0]); } }
|
||||
|
||||
/* 8. multi-op leaf: ADDU t0,a0,a1 ; SLL v0,t0,2 */
|
||||
{ uint32_t w[]={ R(0,A0,A1,T0,0,0x21), R(0,0,T0,V0,2,0x00), JR_RA, NOP };
|
||||
OverlaySljitFn fn=jit(w,4); CHECK(fn,"multi: declined");
|
||||
if(fn){ cpu_init(&cpu); cpu.gpr[A0]=10; cpu.gpr[A1]=7; fn(&cpu);
|
||||
CHECK(cpu.gpr[V0]==((10u+7u)<<2),"multi: v0=%u",cpu.gpr[V0]); } }
|
||||
|
||||
/* 9. DECLINE cases — must return fn==NULL (fall to interpreter) */
|
||||
{ uint32_t w[]={ I(4,A0,A1,2), NOP, JR_RA, NOP }; /* BEQ */
|
||||
CHECK(jit(w,4)==NULL,"decline: BEQ accepted"); }
|
||||
{ uint32_t w[]={ R(0,V0,0,0,0,0x08), NOP }; /* JR v0 (non-ra) */
|
||||
CHECK(jit(w,2)==NULL,"decline: JR non-ra accepted"); }
|
||||
{ uint32_t w[]={ I(4,A0,A1,1), JR_RA, NOP }; /* control in delay slot */
|
||||
uint32_t w2[]={ JR_RA, I(4,A0,A1,1) };
|
||||
CHECK(jit(w2,2)==NULL,"decline: branch in jr delay slot accepted"); (void)w; }
|
||||
|
||||
/* 10. MULT (signed) + MFHI/MFLO: -3 * 5 = -15 => hi=0xFFFFFFFF lo=0xFFFFFFF1 */
|
||||
{ uint32_t w[]={ R(0,A0,A1,0,0,0x18), R(0,0,0,V0,0,0x10), R(0,0,0,V1,0,0x12), JR_RA, NOP };
|
||||
OverlaySljitFn fn=jit(w,5); CHECK(fn,"mult: declined");
|
||||
if(fn){ cpu_init(&cpu); cpu.gpr[A0]=(uint32_t)-3; cpu.gpr[A1]=5; fn(&cpu);
|
||||
CHECK(cpu.gpr[V0]==0xFFFFFFFFu,"mult hi: v0=0x%X",cpu.gpr[V0]);
|
||||
CHECK(cpu.gpr[V1]==0xFFFFFFF1u,"mult lo: v1=0x%X",cpu.gpr[V1]); } }
|
||||
|
||||
/* 11. MULTU (unsigned): 0xFFFFFFFF * 2 = 0x1FFFFFFFE => hi=1 lo=0xFFFFFFFE */
|
||||
{ uint32_t w[]={ R(0,A0,A1,0,0,0x19), R(0,0,0,V0,0,0x10), R(0,0,0,V1,0,0x12), JR_RA, NOP };
|
||||
OverlaySljitFn fn=jit(w,5); CHECK(fn,"multu: declined");
|
||||
if(fn){ cpu_init(&cpu); cpu.gpr[A0]=0xFFFFFFFFu; cpu.gpr[A1]=2; fn(&cpu);
|
||||
CHECK(cpu.gpr[V0]==1u,"multu hi: v0=0x%X",cpu.gpr[V0]);
|
||||
CHECK(cpu.gpr[V1]==0xFFFFFFFEu,"multu lo: v1=0x%X",cpu.gpr[V1]); } }
|
||||
|
||||
/* 12. Loop (backward branch): v0 = sum(1..a0)
|
||||
* 0 addiu v0,zero,0 ; 1 addiu t0,a0,0
|
||||
* 2 blez t0,end(+5) ; 3 nop
|
||||
* 4 addu v0,v0,t0 ; 5 addiu t0,t0,-1
|
||||
* 6 beq zero,zero,loop(-5) ; 7 nop ; 8 jr ra ; 9 nop */
|
||||
{ uint32_t w[]={ I(9,ZERO,V0,0), I(9,A0,T0,0),
|
||||
I(6,T0,0,5), NOP,
|
||||
R(0,V0,T0,V0,0,0x21), I(9,T0,T0,0xFFFF),
|
||||
I(4,ZERO,ZERO,(uint16_t)(-5)), NOP, JR_RA, NOP };
|
||||
OverlaySljitFn fn=jit(w,10); CHECK(fn,"loop: declined");
|
||||
if(fn){ cpu_init(&cpu); cpu.gpr[A0]=5; fn(&cpu);
|
||||
CHECK(cpu.gpr[V0]==15,"loop sum(1..5): v0=%u",cpu.gpr[V0]);
|
||||
cpu_init(&cpu); cpu.gpr[A0]=0; fn(&cpu);
|
||||
CHECK(cpu.gpr[V0]==0,"loop sum(1..0): v0=%u",cpu.gpr[V0]); } }
|
||||
|
||||
/* 13. Forward conditional: v0 = max(a0,a1) (signed)
|
||||
* 0 slt t0,a0,a1 ; 1 bne t0,zero,L1(+4) ; 2 nop
|
||||
* 3 addu v0,a0,zero ; 4 beq zero,zero,L2(+2) ; 5 nop
|
||||
* 6 L1: addu v0,a1,zero ; 7 L2: jr ra ; 8 nop */
|
||||
{ uint32_t w[]={ R(0,A0,A1,T0,0,0x2A), I(5,T0,ZERO,4), NOP,
|
||||
R(0,A0,ZERO,V0,0,0x21), I(4,ZERO,ZERO,2), NOP,
|
||||
R(0,A1,ZERO,V0,0,0x21), JR_RA, NOP };
|
||||
OverlaySljitFn fn=jit(w,9); CHECK(fn,"max: declined");
|
||||
if(fn){ cpu_init(&cpu); cpu.gpr[A0]=10; cpu.gpr[A1]=7; fn(&cpu);
|
||||
CHECK(cpu.gpr[V0]==10,"max(10,7): v0=%u",cpu.gpr[V0]);
|
||||
cpu_init(&cpu); cpu.gpr[A0]=3; cpu.gpr[A1]=8; fn(&cpu);
|
||||
CHECK(cpu.gpr[V0]==8,"max(3,8): v0=%u",cpu.gpr[V0]); } }
|
||||
|
||||
/* 14. Still-decline: branch target landing on a delay slot (the jr's) */
|
||||
{ uint32_t w[]={ I(4,A0,A1,2), NOP, JR_RA, NOP }; /* BEQ target=word3=jr ds */
|
||||
CHECK(jit(w,4)==NULL,"decline: branch into delay slot accepted"); }
|
||||
|
||||
/* 15. Still-decline: JR to non-$ra */
|
||||
{ uint32_t w[]={ R(0,V0,0,0,0,0x08), NOP };
|
||||
CHECK(jit(w,2)==NULL,"decline: JR non-ra accepted"); }
|
||||
|
||||
/* 16. DIV (signed): div a0,a1 ; mflo v0 ; mfhi v1 ; jr ra ; nop */
|
||||
{ uint32_t w[]={ R(0,A0,A1,0,0,0x1A), R(0,0,0,V0,0,0x12), R(0,0,0,V1,0,0x10), JR_RA, NOP };
|
||||
OverlaySljitFn fn=jit(w,5); CHECK(fn,"div: declined");
|
||||
if(fn){
|
||||
cpu_init(&cpu); cpu.gpr[A0]=(uint32_t)-20; cpu.gpr[A1]=3; fn(&cpu);
|
||||
CHECK(cpu.gpr[V0]==(uint32_t)-6,"div -20/3 lo: v0=0x%X",cpu.gpr[V0]);
|
||||
CHECK(cpu.gpr[V1]==(uint32_t)-2,"div -20%%3 hi: v1=0x%X",cpu.gpr[V1]);
|
||||
cpu_init(&cpu); cpu.gpr[A0]=5; cpu.gpr[A1]=0; fn(&cpu); /* /0, a>0 */
|
||||
CHECK(cpu.gpr[V0]==0xFFFFFFFFu,"div 5/0 lo: v0=0x%X",cpu.gpr[V0]);
|
||||
CHECK(cpu.gpr[V1]==5,"div 5/0 hi: v1=%u",cpu.gpr[V1]);
|
||||
cpu_init(&cpu); cpu.gpr[A0]=(uint32_t)-5; cpu.gpr[A1]=0; fn(&cpu);/* /0, a<0 */
|
||||
CHECK(cpu.gpr[V0]==1,"div -5/0 lo: v0=0x%X",cpu.gpr[V0]);
|
||||
cpu_init(&cpu); cpu.gpr[A0]=0x80000000u; cpu.gpr[A1]=(uint32_t)-1; fn(&cpu);/* overflow */
|
||||
CHECK(cpu.gpr[V0]==0x80000000u,"div INT_MIN/-1 lo: v0=0x%X",cpu.gpr[V0]);
|
||||
CHECK(cpu.gpr[V1]==0,"div INT_MIN/-1 hi: v1=%u",cpu.gpr[V1]); } }
|
||||
|
||||
/* 17. DIVU (unsigned): divu a0,a1 ; mflo v0 ; mfhi v1 */
|
||||
{ uint32_t w[]={ R(0,A0,A1,0,0,0x1B), R(0,0,0,V0,0,0x12), R(0,0,0,V1,0,0x10), JR_RA, NOP };
|
||||
OverlaySljitFn fn=jit(w,5); CHECK(fn,"divu: declined");
|
||||
if(fn){
|
||||
cpu_init(&cpu); cpu.gpr[A0]=20; cpu.gpr[A1]=3; fn(&cpu);
|
||||
CHECK(cpu.gpr[V0]==6 && cpu.gpr[V1]==2,"divu 20/3: v0=%u v1=%u",cpu.gpr[V0],cpu.gpr[V1]);
|
||||
cpu_init(&cpu); cpu.gpr[A0]=20; cpu.gpr[A1]=0; fn(&cpu); /* /0 */
|
||||
CHECK(cpu.gpr[V0]==0xFFFFFFFFu && cpu.gpr[V1]==20,"divu 20/0: v0=0x%X v1=%u",cpu.gpr[V0],cpu.gpr[V1]); } }
|
||||
|
||||
/* 18. J (forward absolute): skip the v0=99 store
|
||||
* 0 addiu v0,zero,1 ; 1 j ->word4 ; 2 nop ; 3 addiu v0,zero,99 (skipped)
|
||||
* 4 jr ra ; 5 nop */
|
||||
{ uint32_t tphys = (BASE & 0x1FFFFFFFu) + 4u*4u; /* word 4 */
|
||||
uint32_t ji = (2u<<26) | ((tphys>>2) & 0x03FFFFFFu);
|
||||
uint32_t w[]={ I(9,ZERO,V0,1), ji, NOP, I(9,ZERO,V0,99), JR_RA, NOP };
|
||||
OverlaySljitFn fn=jit(w,6); CHECK(fn,"J: declined");
|
||||
if(fn){ cpu_init(&cpu); fn(&cpu);
|
||||
CHECK(cpu.gpr[V0]==1,"J skip: v0=%u (want 1)",cpu.gpr[V0]); } }
|
||||
|
||||
/* 19. JAL: link $ra=return_pc (before delay), call helper, continuation runs
|
||||
* 0 jal 0x80012340 ; 1 nop ; 2 addiu a1,zero,0x22 ; 3 jr ra ; 4 nop */
|
||||
{ uint32_t tgt=0x80012340u;
|
||||
uint32_t w[]={ Jal(tgt), NOP, I(9,ZERO,A1,0x22), JR_RA, NOP };
|
||||
OverlaySljitFn fn=jit(w,5); CHECK(fn,"jal: declined");
|
||||
if(fn){ cpu_init(&cpu); g_call_count=0; fn(&cpu);
|
||||
CHECK(g_call_count==1,"jal: helper calls=%d",g_call_count);
|
||||
CHECK(g_call_target==tgt,"jal: target=0x%X want 0x%X",g_call_target,tgt);
|
||||
CHECK(g_call_return==BASE+8,"jal: return_pc=0x%X want 0x%X",g_call_return,BASE+8);
|
||||
CHECK(g_call_check==1,"jal: check=%d",g_call_check);
|
||||
CHECK(cpu.gpr[RA]==BASE+8,"jal: ra=0x%X want 0x%X",cpu.gpr[RA],BASE+8);
|
||||
CHECK(cpu.gpr[A1]==0x22,"jal: continuation didn't run (a1=0x%X)",cpu.gpr[A1]); } }
|
||||
|
||||
/* 20. JALR rd=$ra, rs=t0: dynamic target captured before delay slot
|
||||
* 0 lui t0,0x8001 ; 1 ori t0,t0,0x2340 ; 2 jalr ra,t0 ; 3 nop
|
||||
* 4 addiu a1,zero,0x55 ; 5 jr ra ; 6 nop */
|
||||
{ uint32_t tgt=0x80012340u;
|
||||
uint32_t w[]={ I(0x0F,0,T0,0x8001), I(0x0D,T0,T0,0x2340), R(0,T0,0,RA,0,0x09), NOP,
|
||||
I(9,ZERO,A1,0x55), JR_RA, NOP };
|
||||
OverlaySljitFn fn=jit(w,7); CHECK(fn,"jalr: declined");
|
||||
if(fn){ cpu_init(&cpu); g_call_count=0; fn(&cpu);
|
||||
CHECK(g_call_count==1,"jalr: helper calls=%d",g_call_count);
|
||||
CHECK(g_call_target==tgt,"jalr: target=0x%X want 0x%X",g_call_target,tgt);
|
||||
CHECK(g_call_check==1,"jalr: check=%d",g_call_check);
|
||||
CHECK(cpu.gpr[RA]==BASE+16,"jalr: ra=0x%X want 0x%X",cpu.gpr[RA],BASE+16);
|
||||
CHECK(cpu.gpr[A1]==0x55,"jalr: continuation didn't run (a1=0x%X)",cpu.gpr[A1]); } }
|
||||
|
||||
/* 21. JAL transfer/bail: helper returns 1 ⇒ shard returns, continuation skipped
|
||||
* 0 jal 0x8000DEA0 ; 1 nop ; 2 addiu a1,zero,0x99 (must NOT run) ; 3 jr ra ; 4 nop */
|
||||
{ uint32_t w[]={ Jal(0x8000DEA0u), NOP, I(9,ZERO,A1,0x99), JR_RA, NOP };
|
||||
OverlaySljitFn fn=jit(w,5); CHECK(fn,"jal-bail: declined");
|
||||
if(fn){ cpu_init(&cpu); cpu.gpr[A1]=0; fn(&cpu);
|
||||
CHECK(cpu.gpr[A1]==0,"jal-bail: continuation ran (a1=0x%X)",cpu.gpr[A1]); } }
|
||||
|
||||
printf("\nsljit_emit_test: %d passed, %d failed\n", g_pass, g_fail);
|
||||
return g_fail ? 1 : 0;
|
||||
}
|
||||
@@ -1,266 +0,0 @@
|
||||
/* sljit_offline_diff.c — offline JIT-vs-reference-interpreter differential over a
|
||||
* captured overlay image. For each candidate entry: JIT the fragment with
|
||||
* overlay_sljit_try_compile, run a reference MIPS interpreter over the SAME bytes
|
||||
* with the SAME memory + the SAME call stub, and compare the full register file at
|
||||
* return. Any mismatch is a codegen bug in that function (the live $sp-off-by-0x18
|
||||
* divergence at 0x12E478 traced to a callee shard corrupting $sp; this finds it
|
||||
* without the game).
|
||||
*
|
||||
* Build:
|
||||
* gcc -O2 -I runtime/include -I lib/sljit/sljit_src \
|
||||
* runtime/tests/sljit_offline_diff.c runtime/src/overlay_sljit.c \
|
||||
* lib/sljit/sljit_src/sljitLir.c -o /tmp/sljit_offline_diff && \
|
||||
* /tmp/sljit_offline_diff <capture.json> [entry_hex ...]
|
||||
*/
|
||||
#include "cpu_state.h"
|
||||
#include "overlay_sljit.h"
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/* decoded capture image (defined/loaded below; forward globals so ref_run can
|
||||
* read the size). */
|
||||
static uint8_t* g_img=NULL; static uint32_t g_base=0,g_imgsz=0;
|
||||
|
||||
/* ---- fake guest RAM (2 MB) shared by JIT + reference interp --------------- */
|
||||
static uint8_t g_ram[0x200000];
|
||||
static uint32_t MASK(uint32_t a){ return a & 0x1FFFFF; } /* fold KSEG + I/O into 2MB */
|
||||
static uint32_t rd_word(uint32_t a){ uint32_t v; memcpy(&v,&g_ram[MASK(a)&~3u],4); return v; }
|
||||
static void wr_word(uint32_t a,uint32_t v){ memcpy(&g_ram[MASK(a)&~3u],&v,4); }
|
||||
static uint16_t rd_half(uint32_t a){ uint16_t v; memcpy(&v,&g_ram[MASK(a)&~1u],2); return v; }
|
||||
static void wr_half(uint32_t a,uint16_t v){ memcpy(&g_ram[MASK(a)&~1u],&v,2); }
|
||||
static uint8_t rd_byte(uint32_t a){ return g_ram[MASK(a)]; }
|
||||
static void wr_byte(uint32_t a,uint8_t v){ g_ram[MASK(a)]=v; }
|
||||
static void cpu_wire(CPUState*c){ c->read_word=rd_word;c->write_word=wr_word;
|
||||
c->read_half=rd_half;c->write_half=wr_half;c->read_byte=rd_byte;c->write_byte=wr_byte; }
|
||||
|
||||
/* Deterministic, balanced call stub used by BOTH sides: a callee that does not
|
||||
* touch $sp, sets $v0 to a fixed value, restores nothing (the caller's frame is
|
||||
* intact), returns normally (no bail). Identical effect on JIT and interp, so any
|
||||
* divergence isolates the JIT's own codegen — NOT call semantics. */
|
||||
static int g_calls=0;
|
||||
int psx_sljit_call(CPUState*cpu,uint32_t target,uint32_t return_pc,int check){
|
||||
(void)check; g_calls++; cpu->gpr[2]=0x12340000u ^ target; cpu->gpr[31]=return_pc; cpu->pc=0; return 0; }
|
||||
void psx_sljit_cop2(CPUState*cpu,uint32_t insn){(void)cpu;(void)insn;}
|
||||
void psx_sljit_memx(CPUState*cpu,uint32_t insn){(void)cpu;(void)insn;}
|
||||
|
||||
/* ---- reference MIPS interpreter (parallels dirty_ram_interp for this subset) */
|
||||
static uint32_t f_op(uint32_t i){return i>>26;} static uint32_t f_rs(uint32_t i){return(i>>21)&31;}
|
||||
static uint32_t f_rt(uint32_t i){return(i>>16)&31;} static uint32_t f_rd(uint32_t i){return(i>>11)&31;}
|
||||
static uint32_t f_sh(uint32_t i){return(i>>6)&31;} static uint32_t f_fn(uint32_t i){return i&63;}
|
||||
static uint32_t f_imm(uint32_t i){return i&0xFFFF;} static int32_t f_simm(uint32_t i){return(int32_t)(int16_t)(i&0xFFFF);}
|
||||
|
||||
/* execute one non-control insn on the reference cpu (mirror of emit_one's intent) */
|
||||
static void ref_one(CPUState*c,uint32_t in){
|
||||
uint32_t op=f_op(in),rs=f_rs(in),rt=f_rt(in),rd=f_rd(in),sh=f_sh(in),fn=f_fn(in),imm=f_imm(in);
|
||||
int32_t simm=f_simm(in); uint32_t*g=c->gpr;
|
||||
#define W(n,v) do{ if(n)g[n]=(uint32_t)(v); }while(0)
|
||||
if(op==0){
|
||||
switch(fn){
|
||||
case 0x00: W(rd,g[rt]<<sh); break; case 0x02: W(rd,g[rt]>>sh); break;
|
||||
case 0x03: W(rd,(uint32_t)((int32_t)g[rt]>>sh)); break;
|
||||
case 0x04: W(rd,g[rt]<<(g[rs]&31)); break; case 0x06: W(rd,g[rt]>>(g[rs]&31)); break;
|
||||
case 0x07: W(rd,(uint32_t)((int32_t)g[rt]>>(g[rs]&31))); break;
|
||||
case 0x20: case 0x21: W(rd,g[rs]+g[rt]); break; case 0x22: case 0x23: W(rd,g[rs]-g[rt]); break;
|
||||
case 0x24: W(rd,g[rs]&g[rt]); break; case 0x25: W(rd,g[rs]|g[rt]); break;
|
||||
case 0x26: W(rd,g[rs]^g[rt]); break; case 0x27: W(rd,~(g[rs]|g[rt])); break;
|
||||
case 0x2a: W(rd,((int32_t)g[rs]<(int32_t)g[rt])?1:0); break;
|
||||
case 0x2b: W(rd,(g[rs]<g[rt])?1:0); break;
|
||||
case 0x10: W(rd,c->hi); break; case 0x12: W(rd,c->lo); break;
|
||||
case 0x11: c->hi=g[rs]; break; case 0x13: c->lo=g[rs]; break;
|
||||
case 0x18: { int64_t r=(int64_t)(int32_t)g[rs]*(int64_t)(int32_t)g[rt]; c->lo=(uint32_t)r; c->hi=(uint32_t)((uint64_t)r>>32);} break;
|
||||
case 0x19: { uint64_t r=(uint64_t)g[rs]*(uint64_t)g[rt]; c->lo=(uint32_t)r; c->hi=(uint32_t)(r>>32);} break;
|
||||
case 0x1a: { int32_t a=(int32_t)g[rs],b=(int32_t)g[rt]; if(b==0){c->lo=a<0?1:0xFFFFFFFF;c->hi=(uint32_t)a;} else if((uint32_t)a==0x80000000u&&b==-1){c->lo=0x80000000u;c->hi=0;} else {c->lo=(uint32_t)(a/b);c->hi=(uint32_t)(a%b);} } break;
|
||||
case 0x1b: { uint32_t a=g[rs],b=g[rt]; if(b==0){c->lo=0xFFFFFFFF;c->hi=a;} else {c->lo=a/b;c->hi=a%b;} } break;
|
||||
case 0x0f: break; /* sync */
|
||||
default: printf(" ref: unhandled special fn 0x%02X\n",fn); break;
|
||||
}
|
||||
} else switch(op){
|
||||
case 0x08: case 0x09: W(rt,g[rs]+(uint32_t)simm); break;
|
||||
case 0x0a: W(rt,((int32_t)g[rs]<simm)?1:0); break;
|
||||
case 0x0b: W(rt,(g[rs]<(uint32_t)simm)?1:0); break;
|
||||
case 0x0c: W(rt,g[rs]&imm); break; case 0x0d: W(rt,g[rs]|imm); break; case 0x0e: W(rt,g[rs]^imm); break;
|
||||
case 0x0f: W(rt,imm<<16); break;
|
||||
case 0x20: W(rt,(uint32_t)(int32_t)(int8_t)c->read_byte(g[rs]+simm)); break;
|
||||
case 0x21: W(rt,(uint32_t)(int32_t)(int16_t)c->read_half(g[rs]+simm)); break;
|
||||
case 0x23: W(rt,c->read_word(g[rs]+simm)); break;
|
||||
case 0x24: W(rt,c->read_byte(g[rs]+simm)); break;
|
||||
case 0x25: W(rt,c->read_half(g[rs]+simm)); break;
|
||||
case 0x28: c->write_byte(g[rs]+simm,(uint8_t)g[rt]); break;
|
||||
case 0x29: c->write_half(g[rs]+simm,(uint16_t)g[rt]); break;
|
||||
case 0x2b: c->write_word(g[rs]+simm,g[rt]); break;
|
||||
default: printf(" ref: unhandled op 0x%02X\n",op); break;
|
||||
}
|
||||
g[0]=0;
|
||||
#undef W
|
||||
}
|
||||
static int ref_is_ctrl(uint32_t in){ uint32_t op=f_op(in),fn=f_fn(in);
|
||||
if(op==0) return (fn==0x08||fn==0x09); if(op==1) return 1; return (op>=2&&op<=7); }
|
||||
/* Run the reference interp from entry until jr $ra returns (matches the shard's
|
||||
* fragment semantics). bytes/base define the image. Returns at jr $ra. */
|
||||
static const uint8_t*g_rimg=NULL; static uint32_t g_rbase=0,g_rsz=0;
|
||||
static int in_img(uint32_t pc){ uint32_t off=(pc&0x1FFFFFFF)-(g_rbase&0x1FFFFFFF); return off+8<=g_rsz && (pc&0x1FFFFFFF)>=(g_rbase&0x1FFFFFFF); }
|
||||
static uint32_t rimw(uint32_t pc){ return *(const uint32_t*)(g_rimg+((pc&0x1FFFFFFF)-(g_rbase&0x1FFFFFFF))); }
|
||||
static void ref_run(CPUState*c,const uint8_t*img,uint32_t base,uint32_t entry){
|
||||
g_rimg=img; g_rbase=base; g_rsz=g_imgsz;
|
||||
uint32_t pc=entry; int guard=0;
|
||||
while(guard++<100000){
|
||||
if(!in_img(pc)){ printf(" ref: pc 0x%08X left image — stop\n",pc); return; }
|
||||
uint32_t in=rimw(pc);
|
||||
uint32_t op=f_op(in),fn=f_fn(in),rs=f_rs(in),rt=f_rt(in),rd=f_rd(in);
|
||||
if(op==0&&fn==0x08){ /* jr rs */
|
||||
uint32_t tgt=c->gpr[rs];
|
||||
uint32_t ds=rimw(pc+4); ref_one(c,ds);
|
||||
if(rs==31) return; /* return */
|
||||
pc=tgt; continue; /* jr rX tail */
|
||||
}
|
||||
if(op==0&&fn==0x09){ /* jalr rd,rs */
|
||||
uint32_t tgt=c->gpr[rs]; uint32_t link=rd?rd:31; c->gpr[link]=pc+8;
|
||||
uint32_t ds=rimw(pc+4); ref_one(c,ds);
|
||||
psx_sljit_call(c,tgt,pc+8,(rd==0||rd==31)); pc+=8; continue;
|
||||
}
|
||||
if(op==3){ /* jal */
|
||||
uint32_t tgt=((pc+4)&0xF0000000)|((in&0x3FFFFFF)<<2); c->gpr[31]=pc+8;
|
||||
uint32_t ds=rimw(pc+4); ref_one(c,ds);
|
||||
psx_sljit_call(c,tgt,pc+8,1); pc+=8; continue;
|
||||
}
|
||||
if(op==2){ /* j */
|
||||
uint32_t tgt=((pc+4)&0xF0000000)|((in&0x3FFFFFF)<<2);
|
||||
uint32_t ds=rimw(pc+4); ref_one(c,ds);
|
||||
pc=tgt; continue;
|
||||
}
|
||||
if(op==1||(op>=4&&op<=7)){ /* branch */
|
||||
int take=0; int32_t simm=f_simm(in);
|
||||
if(op==4) take=(c->gpr[rs]==c->gpr[rt]);
|
||||
else if(op==5) take=(c->gpr[rs]!=c->gpr[rt]);
|
||||
else if(op==6) take=((int32_t)c->gpr[rs]<=0);
|
||||
else if(op==7) take=((int32_t)c->gpr[rs]>0);
|
||||
else { int lz=(rt==0||rt==0x10); if(rt==0x10||rt==0x11) c->gpr[31]=pc+8;
|
||||
take = lz?((int32_t)c->gpr[rs]<0):((int32_t)c->gpr[rs]>=0); }
|
||||
uint32_t ds=rimw(pc+4); ref_one(c,ds);
|
||||
pc = take ? (pc+4+(simm<<2)) : (pc+8); continue;
|
||||
}
|
||||
ref_one(c,in); pc+=4;
|
||||
}
|
||||
printf(" ref: guard tripped (no return)\n");
|
||||
}
|
||||
|
||||
/* ---- capture loader (minimal JSON: find base64 image covering an addr) ----- */
|
||||
static int b64dec(const char*s,uint8_t*out,int outcap){
|
||||
static int8_t T[256]; static int init=0; if(!init){memset(T,-1,256);
|
||||
const char*A="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
for(int i=0;i<64;i++)T[(unsigned char)A[i]]=i; init=1;}
|
||||
int n=0,val=0,bits=0;
|
||||
for(;*s&&*s!='"';s++){ if(*s=='='||T[(unsigned char)*s]<0)continue; val=(val<<6)|T[(unsigned char)*s]; bits+=6;
|
||||
if(bits>=8){bits-=8; if(n<outcap)out[n++]=(uint8_t)(val>>bits);} }
|
||||
return n;
|
||||
}
|
||||
/* crude: pull load_addr/size/bytes_b64 of the FIRST capture covering target */
|
||||
static int load_capture(const char*path,uint32_t target){
|
||||
FILE*f=fopen(path,"rb"); if(!f){printf("cannot open %s\n",path);return 0;}
|
||||
fseek(f,0,SEEK_END); long sz=ftell(f); fseek(f,0,SEEK_SET);
|
||||
char*buf=malloc(sz+1); fread(buf,1,sz,f); buf[sz]=0; fclose(f);
|
||||
char*p=buf;
|
||||
while((p=strstr(p,"\"load_addr\""))){
|
||||
char*la=strchr(p,':'); unsigned long load=strtoul(strchr(la,'"')+1,NULL,16);
|
||||
char*szp=strstr(p,"\"size\""); unsigned long size=szp?strtoul(strchr(szp,':')+1,NULL,10):0;
|
||||
char*bp=strstr(p,"\"bytes_b64\""); if(!bp)break;
|
||||
char*q=strchr(strchr(bp,':')+1,'"')+1;
|
||||
if(((load&0x1FFFFFFF)<=(target&0x1FFFFFFF))&&((target&0x1FFFFFFF)<(load&0x1FFFFFFF)+size)){
|
||||
g_img=malloc(size+16); g_imgsz=b64dec(q,g_img,size+16); g_base=(uint32_t)load;
|
||||
free(buf); printf("capture: base=0x%08X size=0x%lX decoded=0x%X\n",g_base,size,g_imgsz); return 1;
|
||||
}
|
||||
p=q;
|
||||
}
|
||||
free(buf); printf("no capture covers 0x%08X\n",target); return 0;
|
||||
}
|
||||
static uint32_t imgw(uint32_t addr){ return *(const uint32_t*)(g_img+((addr&0x1FFFFFFF)-(g_base&0x1FFFFFFF))); }
|
||||
|
||||
/* Test one entry: JIT it, run JIT + reference from identical state, compare. */
|
||||
static int g_fail=0,g_ok=0,g_decl=0;
|
||||
static void test_entry(uint32_t entry){
|
||||
OverlaySljitResult r={0};
|
||||
overlay_sljit_try_compile(entry,g_img,g_imgsz,g_base,&r);
|
||||
if(!r.fn){ g_decl++; return; }
|
||||
/* identical start state for both: a frame at 0x801FE000, sp below it, plus
|
||||
* pseudo-random but fixed gpr values so divergences surface. */
|
||||
CPUState cj,ci; memset(&cj,0,sizeof cj); cpu_wire(&cj);
|
||||
uint32_t sp=0x801FE358;
|
||||
for(int i=1;i<32;i++) cj.gpr[i]=0x01010101u*i + 0x100;
|
||||
cj.gpr[29]=sp; cj.gpr[31]=0x80012000u; cj.gpr[16]=0x801FC000u; /* s0 = object ptr */
|
||||
/* a plausible saved frame so epilogue loads are sane */
|
||||
wr_word(sp+0x14,0x80012000u); wr_word(sp+0x10,0x801FC000u);
|
||||
ci=cj; cpu_wire(&ci);
|
||||
int calls0=g_calls; r.fn(&cj); int jcalls=g_calls-calls0;
|
||||
calls0=g_calls; ref_run(&ci,g_img,g_base,entry); int icalls=g_calls-calls0;
|
||||
int diff=0; for(int i=1;i<32;i++) if(cj.gpr[i]!=ci.gpr[i]) diff=1;
|
||||
if(cj.hi!=ci.hi||cj.lo!=ci.lo) diff=1;
|
||||
if(diff){
|
||||
g_fail++;
|
||||
printf("DIVERGE 0x%08X (%u insns, jit_calls=%d ref_calls=%d):\n",entry,r.insns,jcalls,icalls);
|
||||
const char*rn[32]={"zero","at","v0","v1","a0","a1","a2","a3","t0","t1","t2","t3","t4","t5","t6","t7",
|
||||
"s0","s1","s2","s3","s4","s5","s6","s7","t8","t9","k0","k1","gp","sp","fp","ra"};
|
||||
for(int i=1;i<32;i++) if(cj.gpr[i]!=ci.gpr[i])
|
||||
printf(" %-3s jit=0x%08X ref=0x%08X (delta %d)\n",rn[i],cj.gpr[i],ci.gpr[i],(int)(cj.gpr[i]-ci.gpr[i]));
|
||||
if(cj.hi!=ci.hi) printf(" hi jit=0x%08X ref=0x%08X\n",cj.hi,ci.hi);
|
||||
if(cj.lo!=ci.lo) printf(" lo jit=0x%08X ref=0x%08X\n",cj.lo,ci.lo);
|
||||
} else g_ok++;
|
||||
}
|
||||
|
||||
/* DUMP mode: JIT a fragment with whichever emitter is linked, run it from a
|
||||
* fixed deterministic state + the shared balanced call stub, and print the final
|
||||
* register file on one line. Build this TU once with the new overlay_sljit.c and
|
||||
* once with the old (HEAD) one, then `diff` the outputs: any line that differs is
|
||||
* a function my register allocation changed — a definitive, reference-free bisect
|
||||
* (the old emitter is the validated baseline). */
|
||||
static void dump_entry(uint32_t entry){
|
||||
OverlaySljitResult r={0};
|
||||
overlay_sljit_try_compile(entry,g_img,g_imgsz,g_base,&r);
|
||||
if(!r.fn){ printf("0x%08X DECLINED\n",entry); return; }
|
||||
fprintf(stderr,"RUN 0x%08X insns=%u\n",entry,r.insns); fflush(stderr);
|
||||
CPUState c; memset(&c,0,sizeof c); cpu_wire(&c);
|
||||
for(int i=1;i<32;i++) c.gpr[i]=0x01010101u*i + 0x100;
|
||||
c.gpr[29]=0x801FE358; c.gpr[31]=0x80012000u; c.gpr[16]=0x801FC000u;
|
||||
wr_word(0x801FE358+0x14,0x80012000u); wr_word(0x801FE358+0x10,0x801FC000u);
|
||||
int calls0=g_calls; r.fn(&c); int calls=g_calls-calls0;
|
||||
printf("0x%08X insns=%u calls=%d sp=0x%08X",entry,r.insns,calls,c.gpr[29]);
|
||||
for(int i=1;i<32;i++) printf(" %08X",c.gpr[i]);
|
||||
printf(" hi=%08X lo=%08X\n",c.hi,c.lo);
|
||||
}
|
||||
|
||||
int main(int argc,char**argv){
|
||||
if(argc<2){ printf("usage: %s <capture.json> [--dump] [entry_hex ...]\n",argv[0]); return 2; }
|
||||
int dump=0, ai=2;
|
||||
if(argc>2 && !strcmp(argv[2],"--dump")){ dump=1; ai=3; }
|
||||
uint32_t deflt[]={0x8012E478,0x8012BC24,0x8012B424,0x8012B0A4,0x8012D2D0,0x8012C7E4,0x8012BD40,0x8012BEB0};
|
||||
int n; static uint32_t list[8192];
|
||||
if(argc>ai){ n=0; for(int i=ai;i<argc&&n<8192;i++) list[n++]=(uint32_t)strtoul(argv[i],NULL,16);
|
||||
if(!load_capture(argv[1],list[0])) return 1; }
|
||||
else if(dump){
|
||||
/* auto-enumerate: every jal target + every jalr-reachable entry in the
|
||||
* image (broad coverage for the old-vs-new diff). */
|
||||
if(!load_capture(argv[1],deflt[0])) return 1;
|
||||
n=0;
|
||||
for(uint32_t off=0; off+4<=g_imgsz; off+=4){
|
||||
uint32_t w=*(const uint32_t*)(g_img+off);
|
||||
if((w>>26)==3){ /* jal */
|
||||
uint32_t tgt=((g_base&0xF0000000)|((w&0x3FFFFFF)<<2));
|
||||
if(((tgt&0x1FFFFFFF)>=(g_base&0x1FFFFFFF))&&((tgt&0x1FFFFFFF)<(g_base&0x1FFFFFFF)+g_imgsz)){
|
||||
int dup=0; for(int k=0;k<n;k++) if(list[k]==tgt){dup=1;break;}
|
||||
if(!dup&&n<8192) list[n++]=tgt;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* sort ascending for stable diff */
|
||||
for(int i=0;i<n;i++)for(int j=i+1;j<n;j++) if(list[j]<list[i]){uint32_t t=list[i];list[i]=list[j];list[j]=t;}
|
||||
}
|
||||
else { n=sizeof(deflt)/4; memcpy(list,deflt,sizeof deflt); if(!load_capture(argv[1],list[0])) return 1; }
|
||||
|
||||
for(int i=0;i<n;i++){
|
||||
if(((list[i]&0x1FFFFFFF) < (g_base&0x1FFFFFFF)) || ((list[i]&0x1FFFFFFF) >= (g_base&0x1FFFFFFF)+g_imgsz)) continue;
|
||||
if(dump) dump_entry(list[i]); else test_entry(list[i]);
|
||||
}
|
||||
if(!dump) printf("\noffline_diff: %d ok, %d DIVERGE, %d declined\n",g_ok,g_fail,g_decl);
|
||||
return g_fail?1:0;
|
||||
}
|
||||
@@ -1,186 +0,0 @@
|
||||
/* sljit_sp_probe.c — reproduce the live $sp-off-by-0x18 divergence at 0x12E478.
|
||||
* Stresses the register allocator with patterns that the 66-case harness doesn't:
|
||||
* register pressure forcing eviction of $sp, $sp dirtied across a call (reset),
|
||||
* and stack adjust split by branches. Standalone like sljit_emit_test.c. */
|
||||
#include "cpu_state.h"
|
||||
#include "overlay_sljit.h"
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#define RAM_MASK 0x1FFFFFu
|
||||
static uint8_t g_ram[RAM_MASK + 1];
|
||||
static uint32_t rd_word(uint32_t a){ uint32_t v; memcpy(&v,&g_ram[a&(RAM_MASK&~3u)],4); return v; }
|
||||
static void wr_word(uint32_t a,uint32_t v){ memcpy(&g_ram[a&(RAM_MASK&~3u)],&v,4); }
|
||||
static uint16_t rd_half(uint32_t a){ uint16_t v; memcpy(&v,&g_ram[a&(RAM_MASK&~1u)],2); return v; }
|
||||
static void wr_half(uint32_t a,uint16_t v){ memcpy(&g_ram[a&(RAM_MASK&~1u)],&v,2); }
|
||||
static uint8_t rd_byte(uint32_t a){ return g_ram[a&RAM_MASK]; }
|
||||
static void wr_byte(uint32_t a,uint8_t v){ g_ram[a&RAM_MASK]=v; }
|
||||
static void cpu_init(CPUState*c){ memset(c,0,sizeof*c);
|
||||
c->read_word=rd_word;c->write_word=wr_word;c->read_half=rd_half;c->write_half=wr_half;
|
||||
c->read_byte=rd_byte;c->write_byte=wr_byte; }
|
||||
|
||||
/* call stub: a balanced callee (does not touch sp), returns normally. */
|
||||
int psx_sljit_call(CPUState*cpu,uint32_t target,uint32_t return_pc,int check){
|
||||
(void)target;(void)return_pc;(void)check; cpu->pc=0; return 0; }
|
||||
void psx_sljit_cop2(CPUState*cpu,uint32_t insn){(void)cpu;(void)insn;}
|
||||
void psx_sljit_memx(CPUState*cpu,uint32_t insn){(void)cpu;(void)insn;}
|
||||
|
||||
#define V0 2
|
||||
#define A0 4
|
||||
#define T0 8
|
||||
#define T1 9
|
||||
#define T2 10
|
||||
#define T3 11
|
||||
#define T4 12
|
||||
#define T5 13
|
||||
#define T6 14
|
||||
#define T7 15
|
||||
#define S0 16
|
||||
#define SP 29
|
||||
#define RA 31
|
||||
static uint32_t R(uint32_t op,uint32_t rs,uint32_t rt,uint32_t rd,uint32_t sh,uint32_t fn){
|
||||
return (op<<26)|(rs<<21)|(rt<<16)|(rd<<11)|(sh<<6)|fn; }
|
||||
static uint32_t I(uint32_t op,uint32_t rs,uint32_t rt,uint32_t imm){
|
||||
return (op<<26)|(rs<<21)|(rt<<16)|(imm&0xFFFFu); }
|
||||
static uint32_t Jal(uint32_t t){ return (3u<<26)|((t>>2)&0x03FFFFFFu); }
|
||||
#define JR_RA R(0,RA,0,0,0,0x08)
|
||||
#define NOP 0u
|
||||
static const uint32_t BASE=0x80010000u;
|
||||
static OverlaySljitFn jit(uint32_t*w,int n){ OverlaySljitResult r={0};
|
||||
overlay_sljit_try_compile(BASE,(const uint8_t*)w,(uint32_t)(n*4),BASE,&r); return r.fn; }
|
||||
static int g_pass=0,g_fail=0;
|
||||
#define CHECK(c,...) do{ if(c)g_pass++; else{g_fail++;printf("FAIL: ");printf(__VA_ARGS__);printf("\n");} }while(0)
|
||||
|
||||
int main(void){
|
||||
CPUState cpu;
|
||||
|
||||
/* P1: register-pressure eviction of $sp, then $sp restored.
|
||||
* addiu sp,sp,-0x18 ; load 8 temps (forces sp eviction) ; addiu sp,sp,0x18 ; jr ra
|
||||
* Expect: sp == entry sp (balanced). */
|
||||
{ uint32_t w[]={
|
||||
I(9,SP,SP,(uint16_t)-0x18),
|
||||
I(9,0,T0,1),I(9,0,T1,2),I(9,0,T2,3),I(9,0,T3,4),
|
||||
I(9,0,T4,5),I(9,0,T5,6),I(9,0,T6,7),I(9,0,T7,8),
|
||||
I(9,SP,SP,0x18),
|
||||
JR_RA,NOP };
|
||||
OverlaySljitFn fn=jit(w,sizeof(w)/4); CHECK(fn,"P1 declined");
|
||||
if(fn){ cpu_init(&cpu); cpu.gpr[SP]=0x801FE358; fn(&cpu);
|
||||
CHECK(cpu.gpr[SP]==0x801FE358,"P1 sp=0x%08X want 0x801FE358 (delta %d)",cpu.gpr[SP],(int)(cpu.gpr[SP]-0x801FE358)); } }
|
||||
|
||||
/* P2: $sp dirtied, then a jal (flush+reset), then $sp adjusted post-call.
|
||||
* addiu sp,sp,-0x18 ; sw ra,0x14(sp) ; jal foo ; nop ; lw ra,0x14(sp) ; addiu sp,sp,0x18 ; jr ra ; nop */
|
||||
{ uint32_t w[]={
|
||||
I(9,SP,SP,(uint16_t)-0x18),
|
||||
I(0x2B,SP,RA,0x14),
|
||||
Jal(0x80012340u), NOP,
|
||||
I(0x23,SP,RA,0x14),
|
||||
I(9,SP,SP,0x18),
|
||||
JR_RA,NOP };
|
||||
OverlaySljitFn fn=jit(w,sizeof(w)/4); CHECK(fn,"P2 declined");
|
||||
if(fn){ cpu_init(&cpu); cpu.gpr[SP]=0x801FE358; cpu.gpr[RA]=0xDEAD; fn(&cpu);
|
||||
CHECK(cpu.gpr[SP]==0x801FE358,"P2 sp=0x%08X want 0x801FE358 (delta %d)",cpu.gpr[SP],(int)(cpu.gpr[SP]-0x801FE358)); } }
|
||||
|
||||
/* P3: $sp adjust on one side of a conditional branch (fall-through keeps cache).
|
||||
* addiu sp,sp,-0x18 ; beq a0,zero,L ; nop ; (not taken) addiu t0,zero,1 ; L: addiu sp,sp,0x18 ; jr ra ; nop */
|
||||
{ uint32_t w[]={
|
||||
I(9,SP,SP,(uint16_t)-0x18),
|
||||
I(4,A0,0,2), NOP, /* beq a0,zero,+2 -> L (word index 4) */
|
||||
I(9,0,T0,1),
|
||||
I(9,SP,SP,0x18), /* L */
|
||||
JR_RA,NOP };
|
||||
OverlaySljitFn fn=jit(w,sizeof(w)/4); CHECK(fn,"P3 declined");
|
||||
if(fn){ cpu_init(&cpu); cpu.gpr[SP]=0x801FE358; cpu.gpr[A0]=0; fn(&cpu); /* taken */
|
||||
CHECK(cpu.gpr[SP]==0x801FE358,"P3 taken sp=0x%08X delta %d",cpu.gpr[SP],(int)(cpu.gpr[SP]-0x801FE358));
|
||||
cpu_init(&cpu); cpu.gpr[SP]=0x801FE358; cpu.gpr[A0]=1; fn(&cpu); /* not taken */
|
||||
CHECK(cpu.gpr[SP]==0x801FE358,"P3 nottaken sp=0x%08X delta %d",cpu.gpr[SP],(int)(cpu.gpr[SP]-0x801FE358)); } }
|
||||
|
||||
/* P4: heavy pressure + call + sp, many regs live across the call. */
|
||||
{ uint32_t w[]={
|
||||
I(9,SP,SP,(uint16_t)-0x18),
|
||||
I(9,0,S0,0x11),I(9,0,T0,0x22),I(9,0,T1,0x33),I(9,0,T2,0x44),
|
||||
I(9,0,T3,0x55),I(9,0,T4,0x66),I(9,0,T5,0x77),
|
||||
Jal(0x80012340u), NOP,
|
||||
R(0,S0,T0,V0,0,0x21), /* addu v0,s0,t0 (uses regs live across call) */
|
||||
I(9,SP,SP,0x18),
|
||||
JR_RA,NOP };
|
||||
OverlaySljitFn fn=jit(w,sizeof(w)/4); CHECK(fn,"P4 declined");
|
||||
if(fn){ cpu_init(&cpu); cpu.gpr[SP]=0x801FE358; fn(&cpu);
|
||||
CHECK(cpu.gpr[SP]==0x801FE358,"P4 sp=0x%08X delta %d",cpu.gpr[SP],(int)(cpu.gpr[SP]-0x801FE358)); } }
|
||||
|
||||
/* P5: epilogue IN the jr $ra delay slot (the most common idiom).
|
||||
* addiu sp,sp,-0x18 ; <work> ; jr ra ; addiu sp,sp,0x18 (delay) */
|
||||
{ uint32_t w[]={
|
||||
I(9,SP,SP,(uint16_t)-0x18),
|
||||
I(9,0,T0,1),I(9,0,T1,2),
|
||||
JR_RA, I(9,SP,SP,0x18) };
|
||||
OverlaySljitFn fn=jit(w,sizeof(w)/4); CHECK(fn,"P5 declined");
|
||||
if(fn){ cpu_init(&cpu); cpu.gpr[SP]=0x801FE358; fn(&cpu);
|
||||
CHECK(cpu.gpr[SP]==0x801FE358,"P5 sp=0x%08X delta %d",cpu.gpr[SP],(int)(cpu.gpr[SP]-0x801FE358)); } }
|
||||
|
||||
/* P6: epilogue in delay slot + register pressure (force sp eviction first). */
|
||||
{ uint32_t w[]={
|
||||
I(9,SP,SP,(uint16_t)-0x18),
|
||||
I(9,0,T0,1),I(9,0,T1,2),I(9,0,T2,3),I(9,0,T3,4),
|
||||
I(9,0,T4,5),I(9,0,T5,6),I(9,0,T6,7),I(9,0,T7,8),
|
||||
JR_RA, I(9,SP,SP,0x18) };
|
||||
OverlaySljitFn fn=jit(w,sizeof(w)/4); CHECK(fn,"P6 declined");
|
||||
if(fn){ cpu_init(&cpu); cpu.gpr[SP]=0x801FE358; fn(&cpu);
|
||||
CHECK(cpu.gpr[SP]==0x801FE358,"P6 sp=0x%08X delta %d",cpu.gpr[SP],(int)(cpu.gpr[SP]-0x801FE358)); } }
|
||||
|
||||
/* P7: branch DELAY SLOT adjusts sp (executes regardless of branch outcome).
|
||||
* addiu sp,sp,-0x18 ; beq a0,zero,L ; addiu sp,sp,0x18 (delay!) ; nop ; L: jr ra ; nop */
|
||||
{ uint32_t w[]={
|
||||
I(9,SP,SP,(uint16_t)-0x18),
|
||||
I(4,A0,0,2), /* beq a0,zero,->word4 (L) */
|
||||
I(9,SP,SP,0x18), /* delay slot: sp += 0x18 */
|
||||
NOP,
|
||||
JR_RA,NOP }; /* L (word4) */
|
||||
OverlaySljitFn fn=jit(w,sizeof(w)/4); CHECK(fn,"P7 declined");
|
||||
if(fn){ cpu_init(&cpu); cpu.gpr[SP]=0x801FE358; cpu.gpr[A0]=0; fn(&cpu); /* taken */
|
||||
CHECK(cpu.gpr[SP]==0x801FE358,"P7 taken sp=0x%08X delta %d",cpu.gpr[SP],(int)(cpu.gpr[SP]-0x801FE358));
|
||||
cpu_init(&cpu); cpu.gpr[SP]=0x801FE358; cpu.gpr[A0]=1; fn(&cpu); /* not taken */
|
||||
CHECK(cpu.gpr[SP]==0x801FE358,"P7 nottaken sp=0x%08X delta %d",cpu.gpr[SP],(int)(cpu.gpr[SP]-0x801FE358)); } }
|
||||
|
||||
/* P8: register-register sp adjust: addu sp, sp, t0 (t0 = -0x18 then +0x18). */
|
||||
{ uint32_t w[]={
|
||||
I(9,0,T0,(uint16_t)-0x18),
|
||||
R(0,SP,T0,SP,0,0x21), /* addu sp,sp,t0 (sp -= 0x18) */
|
||||
I(9,0,T0,0x18),
|
||||
R(0,SP,T0,SP,0,0x21), /* addu sp,sp,t0 (sp += 0x18) */
|
||||
JR_RA,NOP };
|
||||
OverlaySljitFn fn=jit(w,sizeof(w)/4); CHECK(fn,"P8 declined");
|
||||
if(fn){ cpu_init(&cpu); cpu.gpr[SP]=0x801FE358; fn(&cpu);
|
||||
CHECK(cpu.gpr[SP]==0x801FE358,"P8 sp=0x%08X delta %d",cpu.gpr[SP],(int)(cpu.gpr[SP]-0x801FE358)); } }
|
||||
|
||||
/* P9: two calls with sp live across both + epilogue in delay slot. */
|
||||
{ uint32_t w[]={
|
||||
I(9,SP,SP,(uint16_t)-0x18),
|
||||
I(0x2B,SP,RA,0x14),
|
||||
Jal(0x80012340u),NOP,
|
||||
Jal(0x80012380u),NOP,
|
||||
I(0x23,SP,RA,0x14),
|
||||
JR_RA, I(9,SP,SP,0x18) };
|
||||
OverlaySljitFn fn=jit(w,sizeof(w)/4); CHECK(fn,"P9 declined");
|
||||
if(fn){ cpu_init(&cpu); cpu.gpr[SP]=0x801FE358; cpu.gpr[RA]=0xBEEF; fn(&cpu);
|
||||
CHECK(cpu.gpr[SP]==0x801FE358,"P9 sp=0x%08X delta %d",cpu.gpr[SP],(int)(cpu.gpr[SP]-0x801FE358)); } }
|
||||
|
||||
/* P10: loop that touches sp-relative loads each iteration (backward branch),
|
||||
* epilogue after. addiu sp,sp,-0x18 ; li t0,3 ; L: lw t1,0(sp) ; addiu t0,t0,-1 ;
|
||||
* bne t0,zero,L ; nop ; addiu sp,sp,0x18 ; jr ra ; nop */
|
||||
{ uint32_t w[]={
|
||||
I(9,SP,SP,(uint16_t)-0x18),
|
||||
I(9,0,T0,3),
|
||||
I(0x23,SP,T1,0), /* L (word2): lw t1,0(sp) */
|
||||
I(9,T0,T0,(uint16_t)-1),
|
||||
I(5,T0,0,(uint16_t)-3), /* bne t0,zero,-3 -> word2 */
|
||||
NOP,
|
||||
I(9,SP,SP,0x18),
|
||||
JR_RA,NOP };
|
||||
OverlaySljitFn fn=jit(w,sizeof(w)/4); CHECK(fn,"P10 declined");
|
||||
if(fn){ cpu_init(&cpu); cpu.gpr[SP]=0x801FE358; fn(&cpu);
|
||||
CHECK(cpu.gpr[SP]==0x801FE358,"P10 sp=0x%08X delta %d",cpu.gpr[SP],(int)(cpu.gpr[SP]-0x801FE358)); } }
|
||||
|
||||
printf("\nsljit_sp_probe: %d passed, %d failed\n",g_pass,g_fail);
|
||||
return g_fail?1:0;
|
||||
}
|
||||
@@ -160,7 +160,7 @@ def cache_arch_abi() -> str:
|
||||
"""Canonical cache arch-abi tag, IDENTICAL to overlay_loader.c's
|
||||
PSX_OVERLAY_ARCH_ABI ("<os>-<arch>": win|linux|macos + x64|arm64|x86).
|
||||
gcc DLLs are namespaced under <game_id>/gcc/<arch-abi>/ so same-OS
|
||||
different-arch caches (and, later, sljit blobs) never comingle. Keep this
|
||||
different-arch caches never comingle. Keep this
|
||||
mapping in lockstep with overlay_loader.c."""
|
||||
if is_windows():
|
||||
os_tag = 'win'
|
||||
|
||||
Reference in New Issue
Block a user