2011-02-18 14:31:32 +00:00
|
|
|
/*
|
|
|
|
===========================================================================
|
|
|
|
Copyright (C) 2008 Przemyslaw Iskra <sparky@pld-linux.org>
|
|
|
|
|
|
|
|
This file is part of Quake III Arena source code.
|
|
|
|
|
|
|
|
Quake III Arena source code is free software; you can redistribute it
|
|
|
|
and/or modify it under the terms of the GNU General Public License as
|
|
|
|
published by the Free Software Foundation; either version 2 of the License,
|
|
|
|
or (at your option) any later version.
|
|
|
|
|
|
|
|
Quake III Arena source code is distributed in the hope that it will be
|
|
|
|
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with Quake III Arena source code; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
===========================================================================
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <sys/types.h> /* needed by sys/mman.h on OSX */
|
|
|
|
#include <sys/mman.h>
|
|
|
|
#include <sys/time.h>
|
|
|
|
#include <time.h>
|
|
|
|
#include <stddef.h>
|
|
|
|
|
|
|
|
#ifndef MAP_ANONYMOUS
|
|
|
|
# define MAP_ANONYMOUS MAP_ANON
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "vm_local.h"
|
|
|
|
#include "vm_powerpc_asm.h"
|
|
|
|
|
|
|
|
/*
|
|
|
|
* VM_TIMES enables showing information about time spent inside
|
|
|
|
* and outside generated code
|
|
|
|
*/
|
|
|
|
//#define VM_TIMES
|
|
|
|
#ifdef VM_TIMES
|
|
|
|
#include <sys/times.h>
|
|
|
|
static clock_t time_outside_vm = 0;
|
|
|
|
static clock_t time_total_vm = 0;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* exit() won't be called but use it because it is marked with noreturn */
|
2017-07-10 01:33:41 +00:00
|
|
|
#define DIE( reason ) Com_Error( ERR_DROP, "vm_powerpc compiler error: " reason )
|
2011-02-18 14:31:32 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* vm_powerpc uses large quantities of memory during compilation,
|
|
|
|
* Z_Malloc memory may not be enough for some big qvm files
|
|
|
|
*/
|
|
|
|
|
|
|
|
//#define VM_SYSTEM_MALLOC
|
|
|
|
#ifdef VM_SYSTEM_MALLOC
|
|
|
|
static inline void *
|
|
|
|
PPC_Malloc( size_t size )
|
|
|
|
{
|
|
|
|
void *mem = malloc( size );
|
|
|
|
if ( ! mem )
|
|
|
|
DIE( "Not enough memory" );
|
|
|
|
|
|
|
|
return mem;
|
|
|
|
}
|
|
|
|
# define PPC_Free free
|
|
|
|
#else
|
|
|
|
# define PPC_Malloc Z_Malloc
|
|
|
|
# define PPC_Free Z_Free
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
* optimizations:
|
|
|
|
* - hole: bubble optimization (OP_CONST+instruction)
|
|
|
|
* - copy: inline OP_BLOCK_COPY for lengths under 16/32 bytes
|
|
|
|
* - mask: use rlwinm instruction as dataMask
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef __OPTIMIZE__
|
|
|
|
# define OPTIMIZE_HOLE 1
|
|
|
|
# define OPTIMIZE_COPY 1
|
|
|
|
# define OPTIMIZE_MASK 1
|
|
|
|
#else
|
|
|
|
# define OPTIMIZE_HOLE 0
|
|
|
|
# define OPTIMIZE_COPY 0
|
|
|
|
# define OPTIMIZE_MASK 0
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
* SUPPORTED TARGETS:
|
|
|
|
* - Linux 32 bits
|
|
|
|
* ( http://refspecs.freestandards.org/elf/elfspec_ppc.pdf )
|
|
|
|
* * LR at r0 + 4
|
|
|
|
* * Local variable space not needed
|
|
|
|
* -> store caller safe regs at 16+
|
|
|
|
*
|
|
|
|
* - Linux 64 bits (not fully conformant)
|
|
|
|
* ( http://www.ibm.com/developerworks/linux/library/l-powasm4.html )
|
|
|
|
* * needs "official procedure descriptors" (only first function has one)
|
|
|
|
* * LR at r0 + 16
|
|
|
|
* * local variable space required, min 64 bytes, starts at 48
|
|
|
|
* -> store caller safe regs at 128+
|
|
|
|
*
|
|
|
|
* - OS X 32 bits
|
|
|
|
* ( http://developer.apple.com/documentation/DeveloperTools/Conceptual/LowLevelABI/Articles/32bitPowerPC.html )
|
|
|
|
* * LR at r0 + 8
|
|
|
|
* * local variable space required, min 32 bytes (?), starts at 24
|
|
|
|
* -> store caller safe regs at 64+
|
|
|
|
*
|
|
|
|
* - OS X 64 bits (completely untested)
|
|
|
|
* ( http://developer.apple.com/documentation/DeveloperTools/Conceptual/LowLevelABI/Articles/64bitPowerPC.html )
|
|
|
|
* * LR at r0 + 16
|
|
|
|
* * local variable space required, min 64 bytes (?), starts at 48
|
|
|
|
* -> store caller safe regs at 128+
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Select Length - first value on 32 bits, second on 64 */
|
|
|
|
#ifdef __PPC64__
|
|
|
|
# define SL( a, b ) (b)
|
|
|
|
#else
|
|
|
|
# define SL( a, b ) (a)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Select ABI - first for ELF, second for OS X */
|
|
|
|
#ifdef __ELF__
|
|
|
|
# define SA( a, b ) (a)
|
|
|
|
#else
|
|
|
|
# define SA( a, b ) (b)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#define ELF32 SL( SA( 1, 0 ), 0 )
|
|
|
|
#define ELF64 SL( 0, SA( 1, 0 ) )
|
|
|
|
#define OSX32 SL( SA( 0, 1 ), 0 )
|
|
|
|
#define OSX64 SL( 0, SA( 0, 1 ) )
|
|
|
|
|
|
|
|
/* native length load/store instructions ( L stands for long ) */
|
|
|
|
#define iSTLU SL( iSTWU, iSTDU )
|
|
|
|
#define iSTL SL( iSTW, iSTD )
|
|
|
|
#define iLL SL( iLWZ, iLD )
|
|
|
|
#define iLLX SL( iLWZX, iLDX )
|
|
|
|
|
|
|
|
/* register length */
|
|
|
|
#define GPRLEN SL( 4, 8 )
|
|
|
|
#define FPRLEN (8)
|
|
|
|
/* shift that many bits to obtain value miltiplied by GPRLEN */
|
|
|
|
#define GPRLEN_SHIFT SL( 2, 3 )
|
|
|
|
|
|
|
|
/* Link register position */
|
|
|
|
#define STACK_LR SL( SA( 4, 8 ), 16 )
|
|
|
|
/* register save position */
|
|
|
|
#define STACK_SAVE SL( SA( 16, 64 ), 128 )
|
|
|
|
/* temporary space, for float<->int exchange */
|
|
|
|
#define STACK_TEMP SL( SA( 8, 24 ), 48 )
|
|
|
|
/* red zone temporary space, used instead of STACK_TEMP if stack isn't
|
|
|
|
* prepared properly */
|
|
|
|
#define STACK_RTEMP (-16)
|
|
|
|
|
|
|
|
#if ELF64
|
|
|
|
/*
|
|
|
|
* Official Procedure Descriptor
|
|
|
|
* we need to prepare one for generated code if we want to call it
|
|
|
|
* as function
|
|
|
|
*/
|
|
|
|
typedef struct {
|
|
|
|
void *function;
|
|
|
|
void *toc;
|
|
|
|
void *env;
|
|
|
|
} opd_t;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* opcode information table:
|
|
|
|
* - length of immediate value
|
|
|
|
* - returned register type
|
|
|
|
* - required register(s) type
|
|
|
|
*/
|
|
|
|
#define opImm0 0x0000 /* no immediate */
|
|
|
|
#define opImm1 0x0001 /* 1 byte immadiate value after opcode */
|
|
|
|
#define opImm4 0x0002 /* 4 bytes immediate value after opcode */
|
|
|
|
|
|
|
|
#define opRet0 0x0000 /* returns nothing */
|
|
|
|
#define opRetI 0x0004 /* returns integer */
|
|
|
|
#define opRetF 0x0008 /* returns float */
|
|
|
|
#define opRetIF (opRetI | opRetF) /* returns integer or float */
|
|
|
|
|
|
|
|
#define opArg0 0x0000 /* requires nothing */
|
|
|
|
#define opArgI 0x0010 /* requires integer(s) */
|
|
|
|
#define opArgF 0x0020 /* requires float(s) */
|
|
|
|
#define opArgIF (opArgI | opArgF) /* requires integer or float */
|
|
|
|
|
|
|
|
#define opArg2I 0x0040 /* requires second argument, integer */
|
|
|
|
#define opArg2F 0x0080 /* requires second argument, float */
|
|
|
|
#define opArg2IF (opArg2I | opArg2F) /* requires second argument, integer or float */
|
|
|
|
|
|
|
|
static const unsigned char vm_opInfo[256] =
|
|
|
|
{
|
|
|
|
[OP_UNDEF] = opImm0,
|
|
|
|
[OP_IGNORE] = opImm0,
|
|
|
|
[OP_BREAK] = opImm0,
|
|
|
|
[OP_ENTER] = opImm4,
|
|
|
|
/* OP_LEAVE has to accept floats, they will be converted to ints */
|
|
|
|
[OP_LEAVE] = opImm4 | opRet0 | opArgIF,
|
|
|
|
/* only STORE4 and POP use values from OP_CALL,
|
|
|
|
* no need to convert floats back */
|
|
|
|
[OP_CALL] = opImm0 | opRetI | opArgI,
|
|
|
|
[OP_PUSH] = opImm0 | opRetIF,
|
|
|
|
[OP_POP] = opImm0 | opRet0 | opArgIF,
|
|
|
|
[OP_CONST] = opImm4 | opRetIF,
|
|
|
|
[OP_LOCAL] = opImm4 | opRetI,
|
|
|
|
[OP_JUMP] = opImm0 | opRet0 | opArgI,
|
|
|
|
|
|
|
|
[OP_EQ] = opImm4 | opRet0 | opArgI | opArg2I,
|
|
|
|
[OP_NE] = opImm4 | opRet0 | opArgI | opArg2I,
|
|
|
|
[OP_LTI] = opImm4 | opRet0 | opArgI | opArg2I,
|
|
|
|
[OP_LEI] = opImm4 | opRet0 | opArgI | opArg2I,
|
|
|
|
[OP_GTI] = opImm4 | opRet0 | opArgI | opArg2I,
|
|
|
|
[OP_GEI] = opImm4 | opRet0 | opArgI | opArg2I,
|
|
|
|
[OP_LTU] = opImm4 | opRet0 | opArgI | opArg2I,
|
|
|
|
[OP_LEU] = opImm4 | opRet0 | opArgI | opArg2I,
|
|
|
|
[OP_GTU] = opImm4 | opRet0 | opArgI | opArg2I,
|
|
|
|
[OP_GEU] = opImm4 | opRet0 | opArgI | opArg2I,
|
|
|
|
[OP_EQF] = opImm4 | opRet0 | opArgF | opArg2F,
|
|
|
|
[OP_NEF] = opImm4 | opRet0 | opArgF | opArg2F,
|
|
|
|
[OP_LTF] = opImm4 | opRet0 | opArgF | opArg2F,
|
|
|
|
[OP_LEF] = opImm4 | opRet0 | opArgF | opArg2F,
|
|
|
|
[OP_GTF] = opImm4 | opRet0 | opArgF | opArg2F,
|
|
|
|
[OP_GEF] = opImm4 | opRet0 | opArgF | opArg2F,
|
|
|
|
|
|
|
|
[OP_LOAD1] = opImm0 | opRetI | opArgI,
|
|
|
|
[OP_LOAD2] = opImm0 | opRetI | opArgI,
|
|
|
|
[OP_LOAD4] = opImm0 | opRetIF| opArgI,
|
|
|
|
[OP_STORE1] = opImm0 | opRet0 | opArgI | opArg2I,
|
|
|
|
[OP_STORE2] = opImm0 | opRet0 | opArgI | opArg2I,
|
|
|
|
[OP_STORE4] = opImm0 | opRet0 | opArgIF| opArg2I,
|
|
|
|
[OP_ARG] = opImm1 | opRet0 | opArgIF,
|
|
|
|
[OP_BLOCK_COPY] = opImm4 | opRet0 | opArgI | opArg2I,
|
|
|
|
|
|
|
|
[OP_SEX8] = opImm0 | opRetI | opArgI,
|
|
|
|
[OP_SEX16] = opImm0 | opRetI | opArgI,
|
|
|
|
[OP_NEGI] = opImm0 | opRetI | opArgI,
|
|
|
|
[OP_ADD] = opImm0 | opRetI | opArgI | opArg2I,
|
|
|
|
[OP_SUB] = opImm0 | opRetI | opArgI | opArg2I,
|
|
|
|
[OP_DIVI] = opImm0 | opRetI | opArgI | opArg2I,
|
|
|
|
[OP_DIVU] = opImm0 | opRetI | opArgI | opArg2I,
|
|
|
|
[OP_MODI] = opImm0 | opRetI | opArgI | opArg2I,
|
|
|
|
[OP_MODU] = opImm0 | opRetI | opArgI | opArg2I,
|
|
|
|
[OP_MULI] = opImm0 | opRetI | opArgI | opArg2I,
|
|
|
|
[OP_MULU] = opImm0 | opRetI | opArgI | opArg2I,
|
|
|
|
[OP_BAND] = opImm0 | opRetI | opArgI | opArg2I,
|
|
|
|
[OP_BOR] = opImm0 | opRetI | opArgI | opArg2I,
|
|
|
|
[OP_BXOR] = opImm0 | opRetI | opArgI | opArg2I,
|
|
|
|
[OP_BCOM] = opImm0 | opRetI | opArgI,
|
|
|
|
[OP_LSH] = opImm0 | opRetI | opArgI | opArg2I,
|
|
|
|
[OP_RSHI] = opImm0 | opRetI | opArgI | opArg2I,
|
|
|
|
[OP_RSHU] = opImm0 | opRetI | opArgI | opArg2I,
|
|
|
|
[OP_NEGF] = opImm0 | opRetF | opArgF,
|
|
|
|
[OP_ADDF] = opImm0 | opRetF | opArgF | opArg2F,
|
|
|
|
[OP_SUBF] = opImm0 | opRetF | opArgF | opArg2F,
|
|
|
|
[OP_DIVF] = opImm0 | opRetF | opArgF | opArg2F,
|
|
|
|
[OP_MULF] = opImm0 | opRetF | opArgF | opArg2F,
|
|
|
|
[OP_CVIF] = opImm0 | opRetF | opArgI,
|
|
|
|
[OP_CVFI] = opImm0 | opRetI | opArgF,
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* source instruction data
|
|
|
|
*/
|
|
|
|
typedef struct source_instruction_s source_instruction_t;
|
|
|
|
struct source_instruction_s {
|
|
|
|
// opcode
|
|
|
|
unsigned long int op;
|
|
|
|
|
|
|
|
// number of instruction
|
|
|
|
unsigned long int i_count;
|
|
|
|
|
|
|
|
// immediate value (if any)
|
|
|
|
union {
|
|
|
|
unsigned int i;
|
|
|
|
signed int si;
|
|
|
|
signed short ss[2];
|
|
|
|
unsigned short us[2];
|
|
|
|
unsigned char b;
|
|
|
|
} arg;
|
|
|
|
|
|
|
|
// required and returned registers
|
|
|
|
unsigned char regA1;
|
|
|
|
unsigned char regA2;
|
|
|
|
unsigned char regR;
|
|
|
|
unsigned char regPos;
|
|
|
|
|
|
|
|
// next instruction
|
|
|
|
source_instruction_t *next;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* read-only data needed by the generated code
|
|
|
|
*/
|
|
|
|
typedef struct VM_Data {
|
|
|
|
// length of this struct + data
|
|
|
|
size_t dataLength;
|
|
|
|
// compiled code size (in bytes)
|
|
|
|
// it only is code size, without the data
|
|
|
|
size_t codeLength;
|
|
|
|
|
|
|
|
// function pointers, no use to waste registers for them
|
|
|
|
long int (* AsmCall)( int, int );
|
2012-09-15 03:03:44 +00:00
|
|
|
void (* BlockCopy )( unsigned int, unsigned int, size_t );
|
2011-02-18 14:31:32 +00:00
|
|
|
|
|
|
|
// instruction pointers, rarely used so don't waste register
|
|
|
|
ppc_instruction_t *iPointers;
|
|
|
|
|
|
|
|
// data mask for load and store, not used if optimized
|
|
|
|
unsigned int dataMask;
|
|
|
|
|
|
|
|
// fixed number used to convert from integer to float
|
|
|
|
unsigned int floatBase; // 0x59800004
|
|
|
|
|
|
|
|
#if ELF64
|
|
|
|
// official procedure descriptor
|
|
|
|
opd_t opd;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// additional constants, for floating point OP_CONST
|
|
|
|
// this data has dynamic length, thus '0' here
|
|
|
|
unsigned int data[0];
|
|
|
|
} vm_data_t;
|
|
|
|
|
|
|
|
#ifdef offsetof
|
|
|
|
# define VM_Data_Offset( field ) offsetof( vm_data_t, field )
|
|
|
|
#else
|
|
|
|
# define OFFSET( structName, field ) \
|
|
|
|
( (void *)&(((structName *)NULL)->field) - NULL )
|
|
|
|
# define VM_Data_Offset( field ) OFFSET( vm_data_t, field )
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* functions used by generated code
|
|
|
|
*/
|
|
|
|
static long int
|
|
|
|
VM_AsmCall( int callSyscallInvNum, int callProgramStack )
|
|
|
|
{
|
|
|
|
vm_t *savedVM = currentVM;
|
|
|
|
long int i, ret;
|
|
|
|
#ifdef VM_TIMES
|
|
|
|
struct tms start_time, stop_time;
|
|
|
|
clock_t saved_time = time_outside_vm;
|
|
|
|
times( &start_time );
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// save the stack to allow recursive VM entry
|
|
|
|
currentVM->programStack = callProgramStack - 4;
|
|
|
|
|
|
|
|
// we need to convert ints to longs on 64bit powerpcs
|
|
|
|
if ( sizeof( intptr_t ) == sizeof( int ) ) {
|
|
|
|
intptr_t *argPosition = (intptr_t *)((byte *)currentVM->dataBase + callProgramStack + 4);
|
|
|
|
|
|
|
|
// generated code does not invert syscall number
|
|
|
|
argPosition[ 0 ] = -1 - callSyscallInvNum;
|
|
|
|
|
|
|
|
ret = currentVM->systemCall( argPosition );
|
|
|
|
} else {
|
ioquake3 resync to revision 2398 from 2369.
This is the last ioquake3 revision before ioquake3 changed from subversion to git at the beginning of 2013.
#5808 - Include and use .glsl in source (rend2)
#5812 - Use refdef's coordinates when drawing to screen shadow fbo, and separate depth texture and screen texture coordinates in glsl shaders.
Include Rend2 renderer in MacOSX bundle
Include OpenGL1 and Rend2 renderers in MacOSX UB
Include Rend2 renderer in NSIS installer.
Include OpenGL1 and Rend2 renderers in Loki Setup Installer.
Have NSIS uninstaller delete rend2.
Split light sample into direct and ambient parts when using deluxemaps or per-vertex light vectors. Fixes #5813.
Fix writting voip data in demos (broke in r2102).
Fix server ignoring client move commands if voip data is included.
Allow changing cl_voip without restarting.
Fix assert failing in CL_ParseVoip() while flipping cl_voip off and on.
Only declare var_SampleToView in lightall shader when it is actually used.
Fix a couple files not ending with a newline.
Fix clients being able to reset their player state and respawn using donedl.
Fix passing arg9 (qvm only), arg10, and arg11 to vmMain for native libs and non-i386 compiled or interpated qvms. (Currently they aren't use in vmMain in game, cgame, or ui.)
Fix passing args[11] to args[15] from vm to engine on ppc64 and sparc64. Some of the args are used by game bot prediction syscalls. May have been causing bugs. Note: This was fixed for x86_64 in r2163.
Fix reconnect command to work after leaving server. (#5794)
Fix dedicated server crashing when using MSG_ReadDelta*, though it only happens if someone modifies the engine. (#5449)
Makefile fixes for OpenBSD by Jonathan Gray. (#5728)
Save all arguments from connect for reconnect command.
Remove unnecessary localhost check from reconnect command.
Support r_srgb even without hardware support. Also tweak default autoexposure/tonemap settings to look good on both r_srgb 0 and 1.
Changed the MacOS-X build system to make UB's containing i386 and x86_64 arches and made make-macosx.sh not build UB's but only standard binaries
Fix spectator client being switched from follow to free after map_restart if following a client with a higher client number.
Fix client unlinking issue caused by ent->s.number being set to followed client's ps->clientNum after map_restart. Reported by Ensiform.
Changes from Ensiform:
- In G_AddBot, try to allocate clientNum before doing anything else.
- In G_AddBot, don't set SVF_BOT and inuse. It's done in ClientConnect, plus inuse causes ClientDisconnect to be run for no reason.
- In G_AddBot, only set skill in bot useinfo once.
- Avoid using cl->ps.clientNum to check if cl is a bot.
Fix bot skill format so it doesn't always have a space at the beginning of it.
More fixes to the macosx buildsystem. This removes the SDL Framework and makes use of a SDL library that is position independant. This also brings back PPC builds into the UB and also as a standa alone build choice.
Have make-macosx.sh require the user to specify which architecture she/he wants to build for and suggest building UB's if the user is unaware of what architectures are
Lets list all the valid options.
2017-07-09 21:21:12 +00:00
|
|
|
intptr_t args[MAX_VMSYSCALL_ARGS];
|
2011-02-18 14:31:32 +00:00
|
|
|
|
|
|
|
// generated code does not invert syscall number
|
|
|
|
args[0] = -1 - callSyscallInvNum;
|
|
|
|
|
|
|
|
int *argPosition = (int *)((byte *)currentVM->dataBase + callProgramStack + 4);
|
ioquake3 resync to revision 2398 from 2369.
This is the last ioquake3 revision before ioquake3 changed from subversion to git at the beginning of 2013.
#5808 - Include and use .glsl in source (rend2)
#5812 - Use refdef's coordinates when drawing to screen shadow fbo, and separate depth texture and screen texture coordinates in glsl shaders.
Include Rend2 renderer in MacOSX bundle
Include OpenGL1 and Rend2 renderers in MacOSX UB
Include Rend2 renderer in NSIS installer.
Include OpenGL1 and Rend2 renderers in Loki Setup Installer.
Have NSIS uninstaller delete rend2.
Split light sample into direct and ambient parts when using deluxemaps or per-vertex light vectors. Fixes #5813.
Fix writting voip data in demos (broke in r2102).
Fix server ignoring client move commands if voip data is included.
Allow changing cl_voip without restarting.
Fix assert failing in CL_ParseVoip() while flipping cl_voip off and on.
Only declare var_SampleToView in lightall shader when it is actually used.
Fix a couple files not ending with a newline.
Fix clients being able to reset their player state and respawn using donedl.
Fix passing arg9 (qvm only), arg10, and arg11 to vmMain for native libs and non-i386 compiled or interpated qvms. (Currently they aren't use in vmMain in game, cgame, or ui.)
Fix passing args[11] to args[15] from vm to engine on ppc64 and sparc64. Some of the args are used by game bot prediction syscalls. May have been causing bugs. Note: This was fixed for x86_64 in r2163.
Fix reconnect command to work after leaving server. (#5794)
Fix dedicated server crashing when using MSG_ReadDelta*, though it only happens if someone modifies the engine. (#5449)
Makefile fixes for OpenBSD by Jonathan Gray. (#5728)
Save all arguments from connect for reconnect command.
Remove unnecessary localhost check from reconnect command.
Support r_srgb even without hardware support. Also tweak default autoexposure/tonemap settings to look good on both r_srgb 0 and 1.
Changed the MacOS-X build system to make UB's containing i386 and x86_64 arches and made make-macosx.sh not build UB's but only standard binaries
Fix spectator client being switched from follow to free after map_restart if following a client with a higher client number.
Fix client unlinking issue caused by ent->s.number being set to followed client's ps->clientNum after map_restart. Reported by Ensiform.
Changes from Ensiform:
- In G_AddBot, try to allocate clientNum before doing anything else.
- In G_AddBot, don't set SVF_BOT and inuse. It's done in ClientConnect, plus inuse causes ClientDisconnect to be run for no reason.
- In G_AddBot, only set skill in bot useinfo once.
- Avoid using cl->ps.clientNum to check if cl is a bot.
Fix bot skill format so it doesn't always have a space at the beginning of it.
More fixes to the macosx buildsystem. This removes the SDL Framework and makes use of a SDL library that is position independant. This also brings back PPC builds into the UB and also as a standa alone build choice.
Have make-macosx.sh require the user to specify which architecture she/he wants to build for and suggest building UB's if the user is unaware of what architectures are
Lets list all the valid options.
2017-07-09 21:21:12 +00:00
|
|
|
for( i = 1; i < ARRAY_LEN(args); i++ )
|
2011-02-18 14:31:32 +00:00
|
|
|
args[ i ] = argPosition[ i ];
|
|
|
|
|
|
|
|
ret = currentVM->systemCall( args );
|
|
|
|
}
|
|
|
|
|
|
|
|
currentVM = savedVM;
|
|
|
|
|
|
|
|
#ifdef VM_TIMES
|
|
|
|
times( &stop_time );
|
|
|
|
time_outside_vm = saved_time + ( stop_time.tms_utime - start_time.tms_utime );
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* code-block descriptors
|
|
|
|
*/
|
|
|
|
typedef struct dest_instruction dest_instruction_t;
|
|
|
|
typedef struct symbolic_jump symbolic_jump_t;
|
|
|
|
|
|
|
|
struct symbolic_jump {
|
|
|
|
// number of source instruction it has to jump to
|
|
|
|
unsigned long int jump_to;
|
|
|
|
|
|
|
|
// jump condition true/false, (4*cr7+(eq|gt..))
|
|
|
|
long int bo, bi;
|
|
|
|
|
|
|
|
// extensions / modifiers (branch-link)
|
|
|
|
unsigned long ext;
|
|
|
|
|
2017-12-03 10:09:52 +00:00
|
|
|
// dest_instruction referring to this jump
|
2011-02-18 14:31:32 +00:00
|
|
|
dest_instruction_t *parent;
|
|
|
|
|
|
|
|
// next jump
|
|
|
|
symbolic_jump_t *nextJump;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct dest_instruction {
|
|
|
|
// position in the output chain
|
|
|
|
unsigned long int count;
|
|
|
|
|
|
|
|
// source instruction number
|
|
|
|
unsigned long int i_count;
|
|
|
|
|
|
|
|
// exact (for instructins), or maximum (for jump) length
|
|
|
|
unsigned short length;
|
|
|
|
|
|
|
|
dest_instruction_t *next;
|
|
|
|
|
|
|
|
// if the instruction is a jump than jump will be non NULL
|
|
|
|
symbolic_jump_t *jump;
|
|
|
|
|
|
|
|
// if jump is NULL than all the instructions will be here
|
|
|
|
ppc_instruction_t code[0];
|
|
|
|
};
|
|
|
|
|
|
|
|
// first and last instruction,
|
|
|
|
// di_first is a dummy instruction
|
|
|
|
static dest_instruction_t *di_first = NULL, *di_last = NULL;
|
|
|
|
// number of instructions
|
|
|
|
static unsigned long int di_count = 0;
|
|
|
|
// pointers needed to compute local jumps, those aren't pointers to
|
|
|
|
// actual instructions, just used to check how long the jump is going
|
|
|
|
// to be and whether it is positive or negative
|
|
|
|
static dest_instruction_t **di_pointers = NULL;
|
|
|
|
|
|
|
|
// output instructions which does not come from source code
|
|
|
|
// use false i_count value
|
|
|
|
#define FALSE_ICOUNT 0xffffffff
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* append specified instructions at the end of instruction chain
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
PPC_Append(
|
|
|
|
dest_instruction_t *di_now,
|
|
|
|
unsigned long int i_count
|
|
|
|
)
|
|
|
|
{
|
|
|
|
di_now->count = di_count++;
|
|
|
|
di_now->i_count = i_count;
|
|
|
|
di_now->next = NULL;
|
|
|
|
|
|
|
|
di_last->next = di_now;
|
|
|
|
di_last = di_now;
|
|
|
|
|
|
|
|
if ( i_count != FALSE_ICOUNT ) {
|
|
|
|
if ( ! di_pointers[ i_count ] )
|
|
|
|
di_pointers[ i_count ] = di_now;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* make space for instructions and append
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
PPC_AppendInstructions(
|
|
|
|
unsigned long int i_count,
|
|
|
|
size_t num_instructions,
|
|
|
|
const ppc_instruction_t *is
|
|
|
|
)
|
|
|
|
{
|
|
|
|
if ( num_instructions < 0 )
|
|
|
|
num_instructions = 0;
|
|
|
|
size_t iBytes = sizeof( ppc_instruction_t ) * num_instructions;
|
|
|
|
dest_instruction_t *di_now = PPC_Malloc( sizeof( dest_instruction_t ) + iBytes );
|
|
|
|
|
|
|
|
di_now->length = num_instructions;
|
|
|
|
di_now->jump = NULL;
|
|
|
|
|
|
|
|
if ( iBytes > 0 )
|
|
|
|
memcpy( &(di_now->code[0]), is, iBytes );
|
|
|
|
|
|
|
|
PPC_Append( di_now, i_count );
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* create symbolic jump and append
|
|
|
|
*/
|
|
|
|
static symbolic_jump_t *sj_first = NULL, *sj_last = NULL;
|
|
|
|
static void
|
|
|
|
PPC_PrepareJump(
|
|
|
|
unsigned long int i_count,
|
|
|
|
unsigned long int dest,
|
|
|
|
long int bo,
|
|
|
|
long int bi,
|
|
|
|
unsigned long int ext
|
|
|
|
)
|
|
|
|
{
|
|
|
|
dest_instruction_t *di_now = PPC_Malloc( sizeof( dest_instruction_t ) );
|
|
|
|
symbolic_jump_t *sj = PPC_Malloc( sizeof( symbolic_jump_t ) );
|
|
|
|
|
|
|
|
sj->jump_to = dest;
|
|
|
|
sj->bo = bo;
|
|
|
|
sj->bi = bi;
|
|
|
|
sj->ext = ext;
|
|
|
|
sj->parent = di_now;
|
|
|
|
sj->nextJump = NULL;
|
|
|
|
|
|
|
|
sj_last->nextJump = sj;
|
|
|
|
sj_last = sj;
|
|
|
|
|
|
|
|
di_now->length = (bo == branchAlways ? 1 : 2);
|
|
|
|
di_now->jump = sj;
|
|
|
|
|
|
|
|
PPC_Append( di_now, i_count );
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* simplyfy instruction emission
|
|
|
|
*/
|
|
|
|
#define emitStart( i_cnt ) \
|
|
|
|
unsigned long int i_count = i_cnt; \
|
|
|
|
size_t num_instructions = 0; \
|
|
|
|
long int force_emit = 0; \
|
|
|
|
ppc_instruction_t instructions[50];
|
|
|
|
|
|
|
|
#define pushIn( inst ) \
|
|
|
|
(instructions[ num_instructions++ ] = inst)
|
|
|
|
#define in( inst, args... ) pushIn( IN( inst, args ) )
|
|
|
|
|
|
|
|
#define emitEnd() \
|
|
|
|
do{ \
|
|
|
|
if ( num_instructions || force_emit ) \
|
|
|
|
PPC_AppendInstructions( i_count, num_instructions, instructions );\
|
|
|
|
num_instructions = 0; \
|
|
|
|
} while(0)
|
|
|
|
|
|
|
|
#define emitJump( dest, bo, bi, ext ) \
|
|
|
|
do { \
|
|
|
|
emitEnd(); \
|
|
|
|
PPC_PrepareJump( i_count, dest, bo, bi, ext ); \
|
|
|
|
} while(0)
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* definitions for creating .data section,
|
|
|
|
* used in cases where constant float is needed
|
|
|
|
*/
|
|
|
|
#define LOCAL_DATA_CHUNK 50
|
|
|
|
typedef struct local_data_s local_data_t;
|
|
|
|
struct local_data_s {
|
|
|
|
// number of data in this structure
|
|
|
|
long int count;
|
|
|
|
|
|
|
|
// data placeholder
|
|
|
|
unsigned int data[ LOCAL_DATA_CHUNK ];
|
|
|
|
|
|
|
|
// next chunk, if this one wasn't enough
|
|
|
|
local_data_t *next;
|
|
|
|
};
|
|
|
|
|
|
|
|
// first data chunk
|
|
|
|
static local_data_t *data_first = NULL;
|
|
|
|
// total number of data
|
|
|
|
static long int data_acc = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* append the data and return its offset
|
|
|
|
*/
|
|
|
|
static size_t
|
|
|
|
PPC_PushData( unsigned int datum )
|
|
|
|
{
|
|
|
|
local_data_t *d_now = data_first;
|
|
|
|
long int accumulated = 0;
|
|
|
|
|
|
|
|
// check whether we have this one already
|
|
|
|
do {
|
|
|
|
long int i;
|
|
|
|
for ( i = 0; i < d_now->count; i++ ) {
|
|
|
|
if ( d_now->data[ i ] == datum ) {
|
|
|
|
accumulated += i;
|
|
|
|
return VM_Data_Offset( data[ accumulated ] );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ( !d_now->next )
|
|
|
|
break;
|
|
|
|
|
|
|
|
accumulated += d_now->count;
|
|
|
|
d_now = d_now->next;
|
|
|
|
} while (1);
|
|
|
|
|
|
|
|
// not found, need to append
|
|
|
|
accumulated += d_now->count;
|
|
|
|
|
|
|
|
// last chunk is full, create new one
|
|
|
|
if ( d_now->count >= LOCAL_DATA_CHUNK ) {
|
|
|
|
d_now->next = PPC_Malloc( sizeof( local_data_t ) );
|
|
|
|
d_now = d_now->next;
|
|
|
|
d_now->count = 0;
|
|
|
|
d_now->next = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
d_now->data[ d_now->count ] = datum;
|
|
|
|
d_now->count += 1;
|
|
|
|
|
|
|
|
data_acc = accumulated + 1;
|
|
|
|
|
|
|
|
return VM_Data_Offset( data[ accumulated ] );
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* find leading zeros in dataMask to implement it with
|
|
|
|
* "rotate and mask" instruction
|
|
|
|
*/
|
|
|
|
static long int fastMaskHi = 0, fastMaskLo = 31;
|
|
|
|
static void
|
|
|
|
PPC_MakeFastMask( int mask )
|
|
|
|
{
|
|
|
|
#if defined( __GNUC__ ) && ( __GNUC__ >= 4 || ( __GNUC__ == 3 && __GNUC_MINOR__ >= 4 ) )
|
|
|
|
/* count leading zeros */
|
|
|
|
fastMaskHi = __builtin_clz( mask );
|
|
|
|
|
|
|
|
/* count trailing zeros */
|
|
|
|
fastMaskLo = 31 - __builtin_ctz( mask );
|
|
|
|
#else
|
|
|
|
fastMaskHi = 0;
|
|
|
|
while ( ( mask & ( 0x80000000 >> fastMaskHi ) ) == 0 )
|
|
|
|
fastMaskHi++;
|
|
|
|
|
|
|
|
fastMaskLo = 31;
|
|
|
|
while ( ( mask & ( 0x80000000 >> fastMaskLo ) ) == 0 )
|
|
|
|
fastMaskLo--;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* register definitions
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* registers which are global for generated code */
|
|
|
|
|
|
|
|
// pointer to VM_Data (constant)
|
|
|
|
#define rVMDATA r14
|
|
|
|
// vm->dataBase (constant)
|
|
|
|
#define rDATABASE r15
|
|
|
|
// programStack (variable)
|
|
|
|
#define rPSTACK r16
|
|
|
|
|
|
|
|
/*
|
|
|
|
* function local registers,
|
|
|
|
*
|
|
|
|
* normally only volatile registers are used, but if there aren't enough
|
2017-12-03 10:09:52 +00:00
|
|
|
* or function has to preserve some value while calling another one
|
2011-02-18 14:31:32 +00:00
|
|
|
* then caller safe registers are used as well
|
|
|
|
*/
|
|
|
|
static const long int gpr_list[] = {
|
|
|
|
/* caller safe registers, normally only one is used */
|
|
|
|
r24, r23, r22, r21,
|
|
|
|
r20, r19, r18, r17,
|
|
|
|
/* volatile registers (preferred),
|
|
|
|
* normally no more than 5 is used */
|
|
|
|
r3, r4, r5, r6,
|
|
|
|
r7, r8, r9, r10,
|
|
|
|
};
|
|
|
|
static const long int gpr_vstart = 8; /* position of first volatile register */
|
2011-07-26 08:52:24 +00:00
|
|
|
static const long int gpr_total = ARRAY_LEN( gpr_list );
|
2011-02-18 14:31:32 +00:00
|
|
|
|
|
|
|
static const long int fpr_list[] = {
|
|
|
|
/* static registers, normally none is used */
|
|
|
|
f20, f21, f19, f18,
|
|
|
|
f17, f16, f15, f14,
|
|
|
|
/* volatile registers (preferred),
|
|
|
|
* normally no more than 7 is used */
|
|
|
|
f0, f1, f2, f3,
|
|
|
|
f4, f5, f6, f7,
|
|
|
|
f8, f9, f10, f11,
|
|
|
|
f12, f13,
|
|
|
|
};
|
|
|
|
static const long int fpr_vstart = 8;
|
2011-07-26 08:52:24 +00:00
|
|
|
static const long int fpr_total = ARRAY_LEN( fpr_list );
|
2011-02-18 14:31:32 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* prepare some dummy structures and emit init code
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
PPC_CompileInit( void )
|
|
|
|
{
|
|
|
|
di_first = di_last = PPC_Malloc( sizeof( dest_instruction_t ) );
|
|
|
|
di_first->count = 0;
|
|
|
|
di_first->next = NULL;
|
|
|
|
di_first->jump = NULL;
|
|
|
|
|
|
|
|
sj_first = sj_last = PPC_Malloc( sizeof( symbolic_jump_t ) );
|
|
|
|
sj_first->nextJump = NULL;
|
|
|
|
|
|
|
|
data_first = PPC_Malloc( sizeof( local_data_t ) );
|
|
|
|
data_first->count = 0;
|
|
|
|
data_first->next = NULL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* init function:
|
|
|
|
* saves old values of global registers and sets our values
|
|
|
|
* function prototype is:
|
|
|
|
* int begin( void *data, int programStack, void *vm->dataBase )
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* first instruction must not be placed on instruction list */
|
|
|
|
emitStart( FALSE_ICOUNT );
|
|
|
|
|
|
|
|
long int stack = STACK_SAVE + 4 * GPRLEN;
|
|
|
|
|
|
|
|
in( iMFLR, r0 );
|
|
|
|
in( iSTLU, r1, -stack, r1 );
|
|
|
|
in( iSTL, rVMDATA, STACK_SAVE + 0 * GPRLEN, r1 );
|
|
|
|
in( iSTL, rPSTACK, STACK_SAVE + 1 * GPRLEN, r1 );
|
|
|
|
in( iSTL, rDATABASE, STACK_SAVE + 2 * GPRLEN, r1 );
|
|
|
|
in( iSTL, r0, stack + STACK_LR, r1 );
|
|
|
|
in( iMR, rVMDATA, r3 );
|
|
|
|
in( iMR, rPSTACK, r4 );
|
|
|
|
in( iMR, rDATABASE, r5 );
|
|
|
|
in( iBL, +4*8 ); // LINK JUMP: first generated instruction | XXX jump !
|
|
|
|
in( iLL, rVMDATA, STACK_SAVE + 0 * GPRLEN, r1 );
|
|
|
|
in( iLL, rPSTACK, STACK_SAVE + 1 * GPRLEN, r1 );
|
|
|
|
in( iLL, rDATABASE, STACK_SAVE + 2 * GPRLEN, r1 );
|
|
|
|
in( iLL, r0, stack + STACK_LR, r1 );
|
|
|
|
in( iMTLR, r0 );
|
|
|
|
in( iADDI, r1, r1, stack );
|
|
|
|
in( iBLR );
|
|
|
|
|
|
|
|
emitEnd();
|
|
|
|
}
|
|
|
|
|
|
|
|
// rFIRST is the copy of the top value on the opstack
|
|
|
|
#define rFIRST (gpr_list[ gpr_pos - 1])
|
|
|
|
// second value on the opstack
|
|
|
|
#define rSECOND (gpr_list[ gpr_pos - 2 ])
|
|
|
|
// temporary registers, not on the opstack
|
|
|
|
#define rTEMP(x) (gpr_list[ gpr_pos + x ])
|
|
|
|
#define rTMP rTEMP(0)
|
|
|
|
|
|
|
|
#define fFIRST (fpr_list[ fpr_pos - 1 ])
|
|
|
|
#define fSECOND (fpr_list[ fpr_pos - 2 ])
|
|
|
|
#define fTEMP(x) (fpr_list[ fpr_pos + x ])
|
|
|
|
#define fTMP fTEMP(0)
|
|
|
|
|
|
|
|
// register types
|
|
|
|
#define rTYPE_STATIC 0x01
|
|
|
|
#define rTYPE_FLOAT 0x02
|
|
|
|
|
|
|
|
// what type should this opcode return
|
|
|
|
#define RET_INT ( !(i_now->regR & rTYPE_FLOAT) )
|
|
|
|
#define RET_FLOAT ( i_now->regR & rTYPE_FLOAT )
|
|
|
|
// what type should it accept
|
|
|
|
#define ARG_INT ( ! i_now->regA1 )
|
|
|
|
#define ARG_FLOAT ( i_now->regA1 )
|
|
|
|
#define ARG2_INT ( ! i_now->regA2 )
|
|
|
|
#define ARG2_FLOAT ( i_now->regA2 )
|
|
|
|
|
|
|
|
/*
|
|
|
|
* emit OP_CONST, called if nothing has used the const value directly
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
PPC_EmitConst( source_instruction_t * const i_const )
|
|
|
|
{
|
|
|
|
emitStart( i_const->i_count );
|
|
|
|
|
|
|
|
if ( !(i_const->regR & rTYPE_FLOAT) ) {
|
|
|
|
// gpr_pos needed for "rFIRST" to work
|
|
|
|
long int gpr_pos = i_const->regPos;
|
|
|
|
|
|
|
|
if ( i_const->arg.si >= -0x8000 && i_const->arg.si < 0x8000 ) {
|
|
|
|
in( iLI, rFIRST, i_const->arg.si );
|
|
|
|
} else if ( i_const->arg.i < 0x10000 ) {
|
|
|
|
in( iLI, rFIRST, 0 );
|
|
|
|
in( iORI, rFIRST, rFIRST, i_const->arg.i );
|
|
|
|
} else {
|
|
|
|
in( iLIS, rFIRST, i_const->arg.ss[ 0 ] );
|
|
|
|
if ( i_const->arg.us[ 1 ] != 0 )
|
|
|
|
in( iORI, rFIRST, rFIRST, i_const->arg.us[ 1 ] );
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
// fpr_pos needed for "fFIRST" to work
|
|
|
|
long int fpr_pos = i_const->regPos;
|
|
|
|
|
|
|
|
// there's no good way to generate the data,
|
|
|
|
// just read it from data section
|
|
|
|
in( iLFS, fFIRST, PPC_PushData( i_const->arg.i ), rVMDATA );
|
|
|
|
}
|
|
|
|
|
|
|
|
emitEnd();
|
|
|
|
}
|
|
|
|
#define MAYBE_EMIT_CONST() if ( i_const ) PPC_EmitConst( i_const )
|
|
|
|
|
|
|
|
/*
|
|
|
|
* emit empty instruction, just sets the needed pointers
|
|
|
|
*/
|
|
|
|
static inline void
|
|
|
|
PPC_EmitNull( source_instruction_t * const i_null )
|
|
|
|
{
|
|
|
|
PPC_AppendInstructions( i_null->i_count, 0, NULL );
|
|
|
|
}
|
|
|
|
#define EMIT_FALSE_CONST() PPC_EmitNull( i_const )
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* analize function for register usage and whether it needs stack (r1) prepared
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
VM_AnalyzeFunction(
|
|
|
|
source_instruction_t * const i_first,
|
|
|
|
long int *prepareStack,
|
|
|
|
long int *gpr_start_pos,
|
|
|
|
long int *fpr_start_pos
|
|
|
|
)
|
|
|
|
{
|
|
|
|
source_instruction_t *i_now = i_first;
|
|
|
|
|
|
|
|
source_instruction_t *value_provider[20] = { NULL };
|
|
|
|
unsigned long int opstack_depth = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* first step:
|
|
|
|
* remember what codes returned some value and mark the value type
|
|
|
|
* when we get to know what it should be
|
|
|
|
*/
|
|
|
|
while ( (i_now = i_now->next) ) {
|
|
|
|
unsigned long int op = i_now->op;
|
|
|
|
unsigned long int opi = vm_opInfo[ op ];
|
|
|
|
|
|
|
|
if ( opi & opArgIF ) {
|
|
|
|
assert( opstack_depth > 0 );
|
|
|
|
|
|
|
|
opstack_depth--;
|
|
|
|
source_instruction_t *vp = value_provider[ opstack_depth ];
|
|
|
|
unsigned long int vpopi = vm_opInfo[ vp->op ];
|
|
|
|
|
|
|
|
if ( (opi & opArgI) && (vpopi & opRetI) ) {
|
|
|
|
// instruction accepts integer, provider returns integer
|
|
|
|
//vp->regR |= rTYPE_INT;
|
|
|
|
//i_now->regA1 = rTYPE_INT;
|
|
|
|
} else if ( (opi & opArgF) && (vpopi & opRetF) ) {
|
|
|
|
// instruction accepts float, provider returns float
|
|
|
|
vp->regR |= rTYPE_FLOAT; // use OR here - could be marked as static
|
|
|
|
i_now->regA1 = rTYPE_FLOAT;
|
|
|
|
} else {
|
|
|
|
// instruction arg type does not agree with
|
|
|
|
// provider return type
|
|
|
|
DIE( "unrecognized instruction combination" );
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
if ( opi & opArg2IF ) {
|
|
|
|
assert( opstack_depth > 0 );
|
|
|
|
|
|
|
|
opstack_depth--;
|
|
|
|
source_instruction_t *vp = value_provider[ opstack_depth ];
|
|
|
|
unsigned long int vpopi = vm_opInfo[ vp->op ];
|
|
|
|
|
|
|
|
if ( (opi & opArg2I) && (vpopi & opRetI) ) {
|
|
|
|
// instruction accepts integer, provider returns integer
|
|
|
|
//vp->regR |= rTYPE_INT;
|
|
|
|
//i_now->regA2 = rTYPE_INT;
|
|
|
|
} else if ( (opi & opArg2F) && (vpopi & opRetF) ) {
|
|
|
|
// instruction accepts float, provider returns float
|
|
|
|
vp->regR |= rTYPE_FLOAT; // use OR here - could be marked as static
|
|
|
|
i_now->regA2 = rTYPE_FLOAT;
|
|
|
|
} else {
|
|
|
|
// instruction arg type does not agree with
|
|
|
|
// provider return type
|
|
|
|
DIE( "unrecognized instruction combination" );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
( op == OP_CALL )
|
|
|
|
||
|
|
|
|
( op == OP_BLOCK_COPY && ( i_now->arg.i > SL( 16, 32 ) || !OPTIMIZE_COPY ) )
|
|
|
|
) {
|
|
|
|
long int i;
|
|
|
|
*prepareStack = 1;
|
|
|
|
// force caller safe registers so we won't have to save them
|
|
|
|
for ( i = 0; i < opstack_depth; i++ ) {
|
|
|
|
source_instruction_t *vp = value_provider[ i ];
|
|
|
|
vp->regR |= rTYPE_STATIC;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if ( opi & opRetIF ) {
|
|
|
|
value_provider[ opstack_depth ] = i_now;
|
|
|
|
opstack_depth++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* second step:
|
|
|
|
* now that we know register types; compute exactly how many registers
|
|
|
|
* of each type we need
|
|
|
|
*/
|
|
|
|
|
|
|
|
i_now = i_first;
|
|
|
|
long int needed_reg[4] = {0,0,0,0}, max_reg[4] = {0,0,0,0};
|
|
|
|
opstack_depth = 0;
|
|
|
|
while ( (i_now = i_now->next) ) {
|
|
|
|
unsigned long int op = i_now->op;
|
|
|
|
unsigned long int opi = vm_opInfo[ op ];
|
|
|
|
|
|
|
|
if ( opi & opArgIF ) {
|
|
|
|
assert( opstack_depth > 0 );
|
|
|
|
opstack_depth--;
|
|
|
|
source_instruction_t *vp = value_provider[ opstack_depth ];
|
|
|
|
|
|
|
|
needed_reg[ ( vp->regR & 2 ) ] -= 1;
|
|
|
|
if ( vp->regR & 1 ) // static
|
|
|
|
needed_reg[ ( vp->regR & 3 ) ] -= 1;
|
|
|
|
}
|
|
|
|
if ( opi & opArg2IF ) {
|
|
|
|
assert( opstack_depth > 0 );
|
|
|
|
opstack_depth--;
|
|
|
|
source_instruction_t *vp = value_provider[ opstack_depth ];
|
|
|
|
|
|
|
|
needed_reg[ ( vp->regR & 2 ) ] -= 1;
|
|
|
|
if ( vp->regR & 1 ) // static
|
|
|
|
needed_reg[ ( vp->regR & 3 ) ] -= 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( opi & opRetIF ) {
|
|
|
|
long int i;
|
|
|
|
value_provider[ opstack_depth ] = i_now;
|
|
|
|
opstack_depth++;
|
|
|
|
|
|
|
|
i = i_now->regR & 2;
|
|
|
|
needed_reg[ i ] += 1;
|
|
|
|
if ( max_reg[ i ] < needed_reg[ i ] )
|
|
|
|
max_reg[ i ] = needed_reg[ i ];
|
|
|
|
|
|
|
|
i = i_now->regR & 3;
|
|
|
|
if ( i & 1 ) {
|
|
|
|
needed_reg[ i ] += 1;
|
|
|
|
if ( max_reg[ i ] < needed_reg[ i ] )
|
|
|
|
max_reg[ i ] = needed_reg[ i ];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
long int gpr_start = gpr_vstart;
|
|
|
|
const long int gpr_volatile = gpr_total - gpr_vstart;
|
|
|
|
if ( max_reg[ 1 ] > 0 || max_reg[ 0 ] > gpr_volatile ) {
|
|
|
|
// max_reg[ 0 ] - all gprs needed
|
|
|
|
// max_reg[ 1 ] - static gprs needed
|
|
|
|
long int max = max_reg[ 0 ] - gpr_volatile;
|
|
|
|
if ( max_reg[ 1 ] > max )
|
|
|
|
max = max_reg[ 1 ];
|
|
|
|
if ( max > gpr_vstart ) {
|
|
|
|
/* error */
|
|
|
|
DIE( "Need more GPRs" );
|
|
|
|
}
|
|
|
|
|
|
|
|
gpr_start -= max;
|
|
|
|
|
|
|
|
// need stack to save caller safe registers
|
|
|
|
*prepareStack = 1;
|
|
|
|
}
|
|
|
|
*gpr_start_pos = gpr_start;
|
|
|
|
|
|
|
|
long int fpr_start = fpr_vstart;
|
|
|
|
const long int fpr_volatile = fpr_total - fpr_vstart;
|
|
|
|
if ( max_reg[ 3 ] > 0 || max_reg[ 2 ] > fpr_volatile ) {
|
|
|
|
// max_reg[ 2 ] - all fprs needed
|
|
|
|
// max_reg[ 3 ] - static fprs needed
|
|
|
|
long int max = max_reg[ 2 ] - fpr_volatile;
|
|
|
|
if ( max_reg[ 3 ] > max )
|
|
|
|
max = max_reg[ 3 ];
|
|
|
|
if ( max > fpr_vstart ) {
|
|
|
|
/* error */
|
|
|
|
DIE( "Need more FPRs" );
|
|
|
|
}
|
|
|
|
|
|
|
|
fpr_start -= max;
|
|
|
|
|
|
|
|
// need stack to save caller safe registers
|
|
|
|
*prepareStack = 1;
|
|
|
|
}
|
|
|
|
*fpr_start_pos = fpr_start;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* translate opcodes to ppc instructions,
|
|
|
|
* it works on functions, not on whole code at once
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
VM_CompileFunction( source_instruction_t * const i_first )
|
|
|
|
{
|
|
|
|
long int prepareStack = 0;
|
|
|
|
long int gpr_start_pos, fpr_start_pos;
|
|
|
|
|
|
|
|
VM_AnalyzeFunction( i_first, &prepareStack, &gpr_start_pos, &fpr_start_pos );
|
|
|
|
|
|
|
|
long int gpr_pos = gpr_start_pos, fpr_pos = fpr_start_pos;
|
|
|
|
|
|
|
|
// OP_CONST combines well with many opcodes so we treat it in a special way
|
|
|
|
source_instruction_t *i_const = NULL;
|
|
|
|
source_instruction_t *i_now = i_first;
|
|
|
|
|
|
|
|
// how big the stack has to be
|
|
|
|
long int save_space = STACK_SAVE;
|
|
|
|
{
|
|
|
|
if ( gpr_start_pos < gpr_vstart )
|
|
|
|
save_space += (gpr_vstart - gpr_start_pos) * GPRLEN;
|
|
|
|
save_space = ( save_space + 15 ) & ~0x0f;
|
|
|
|
|
|
|
|
if ( fpr_start_pos < fpr_vstart )
|
|
|
|
save_space += (fpr_vstart - fpr_start_pos) * FPRLEN;
|
|
|
|
save_space = ( save_space + 15 ) & ~0x0f;
|
|
|
|
}
|
|
|
|
|
|
|
|
long int stack_temp = prepareStack ? STACK_TEMP : STACK_RTEMP;
|
|
|
|
|
|
|
|
while ( (i_now = i_now->next) ) {
|
|
|
|
emitStart( i_now->i_count );
|
|
|
|
|
|
|
|
switch ( i_now->op )
|
|
|
|
{
|
|
|
|
default:
|
|
|
|
case OP_UNDEF:
|
|
|
|
case OP_IGNORE:
|
|
|
|
MAYBE_EMIT_CONST();
|
|
|
|
in( iNOP );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_BREAK:
|
|
|
|
MAYBE_EMIT_CONST();
|
|
|
|
// force SEGV
|
|
|
|
in( iLWZ, r0, 0, r0 );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_ENTER:
|
|
|
|
if ( i_const )
|
|
|
|
DIE( "Weird opcode order" );
|
|
|
|
|
|
|
|
// don't prepare stack if not needed
|
|
|
|
if ( prepareStack ) {
|
|
|
|
long int i, save_pos = STACK_SAVE;
|
|
|
|
|
|
|
|
in( iMFLR, r0 );
|
|
|
|
in( iSTLU, r1, -save_space, r1 );
|
|
|
|
in( iSTL, r0, save_space + STACK_LR, r1 );
|
|
|
|
|
|
|
|
/* save registers */
|
|
|
|
for ( i = gpr_start_pos; i < gpr_vstart; i++ ) {
|
|
|
|
in( iSTL, gpr_list[ i ], save_pos, r1 );
|
|
|
|
save_pos += GPRLEN;
|
|
|
|
}
|
|
|
|
save_pos = ( save_pos + 15 ) & ~0x0f;
|
|
|
|
|
|
|
|
for ( i = fpr_start_pos; i < fpr_vstart; i++ ) {
|
|
|
|
in( iSTFD, fpr_list[ i ], save_pos, r1 );
|
|
|
|
save_pos += FPRLEN;
|
|
|
|
}
|
|
|
|
prepareStack = 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
in( iADDI, rPSTACK, rPSTACK, - i_now->arg.si );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_LEAVE:
|
|
|
|
if ( i_const ) {
|
|
|
|
EMIT_FALSE_CONST();
|
|
|
|
|
|
|
|
if ( i_const->regR & rTYPE_FLOAT)
|
|
|
|
DIE( "constant float in OP_LEAVE" );
|
|
|
|
|
|
|
|
if ( i_const->arg.si >= -0x8000 && i_const->arg.si < 0x8000 ) {
|
|
|
|
in( iLI, r3, i_const->arg.si );
|
|
|
|
} else if ( i_const->arg.i < 0x10000 ) {
|
|
|
|
in( iLI, r3, 0 );
|
|
|
|
in( iORI, r3, r3, i_const->arg.i );
|
|
|
|
} else {
|
|
|
|
in( iLIS, r3, i_const->arg.ss[ 0 ] );
|
|
|
|
if ( i_const->arg.us[ 1 ] != 0 )
|
|
|
|
in( iORI, r3, r3, i_const->arg.us[ 1 ] );
|
|
|
|
}
|
|
|
|
gpr_pos--;
|
|
|
|
} else {
|
|
|
|
MAYBE_EMIT_CONST();
|
|
|
|
|
|
|
|
/* place return value in r3 */
|
|
|
|
if ( ARG_INT ) {
|
|
|
|
if ( rFIRST != r3 )
|
|
|
|
in( iMR, r3, rFIRST );
|
|
|
|
gpr_pos--;
|
|
|
|
} else {
|
|
|
|
in( iSTFS, fFIRST, stack_temp, r1 );
|
|
|
|
in( iLWZ, r3, stack_temp, r1 );
|
|
|
|
fpr_pos--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// don't undo stack if not prepared
|
|
|
|
if ( prepareStack >= 2 ) {
|
|
|
|
long int i, save_pos = STACK_SAVE;
|
|
|
|
|
|
|
|
in( iLL, r0, save_space + STACK_LR, r1 );
|
|
|
|
|
|
|
|
|
|
|
|
/* restore registers */
|
|
|
|
for ( i = gpr_start_pos; i < gpr_vstart; i++ ) {
|
|
|
|
in( iLL, gpr_list[ i ], save_pos, r1 );
|
|
|
|
save_pos += GPRLEN;
|
|
|
|
}
|
|
|
|
save_pos = ( save_pos + 15 ) & ~0x0f;
|
|
|
|
for ( i = fpr_start_pos; i < fpr_vstart; i++ ) {
|
|
|
|
in( iLFD, fpr_list[ i ], save_pos, r1 );
|
|
|
|
save_pos += FPRLEN;
|
|
|
|
}
|
|
|
|
|
|
|
|
in( iMTLR, r0 );
|
|
|
|
in( iADDI, r1, r1, save_space );
|
|
|
|
}
|
|
|
|
in( iADDI, rPSTACK, rPSTACK, i_now->arg.si);
|
|
|
|
in( iBLR );
|
|
|
|
assert( gpr_pos == gpr_start_pos );
|
|
|
|
assert( fpr_pos == fpr_start_pos );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_CALL:
|
|
|
|
if ( i_const ) {
|
|
|
|
EMIT_FALSE_CONST();
|
|
|
|
|
|
|
|
if ( i_const->arg.si >= 0 ) {
|
|
|
|
emitJump(
|
|
|
|
i_const->arg.i,
|
|
|
|
branchAlways, 0, branchExtLink
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
/* syscall */
|
|
|
|
in( iLL, r0, VM_Data_Offset( AsmCall ), rVMDATA );
|
|
|
|
|
|
|
|
in( iLI, r3, i_const->arg.si ); // negative value
|
|
|
|
in( iMR, r4, rPSTACK ); // push PSTACK on argument list
|
|
|
|
|
|
|
|
in( iMTCTR, r0 );
|
|
|
|
in( iBCTRL );
|
|
|
|
}
|
|
|
|
if ( rFIRST != r3 )
|
|
|
|
in( iMR, rFIRST, r3 );
|
|
|
|
} else {
|
|
|
|
MAYBE_EMIT_CONST();
|
|
|
|
|
|
|
|
in( iCMPWI, cr7, rFIRST, 0 );
|
|
|
|
in( iBLTm, cr7, +4*5 /* syscall */ ); // XXX jump !
|
|
|
|
/* instruction call */
|
|
|
|
|
|
|
|
// get instruction address
|
|
|
|
in( iLL, r0, VM_Data_Offset( iPointers ), rVMDATA );
|
|
|
|
in( iRLWINM, rFIRST, rFIRST, GPRLEN_SHIFT, 0, 31-GPRLEN_SHIFT ); // mul * GPRLEN
|
|
|
|
in( iLLX, r0, rFIRST, r0 ); // load pointer
|
|
|
|
|
|
|
|
in( iB, +4*(3 + (rFIRST != r3 ? 1 : 0) ) ); // XXX jump !
|
|
|
|
|
|
|
|
/* syscall */
|
|
|
|
in( iLL, r0, VM_Data_Offset( AsmCall ), rVMDATA ); // get asmCall pointer
|
|
|
|
/* rFIRST can be r3 or some static register */
|
|
|
|
if ( rFIRST != r3 )
|
|
|
|
in( iMR, r3, rFIRST ); // push OPSTACK top value on argument list
|
|
|
|
in( iMR, r4, rPSTACK ); // push PSTACK on argument list
|
|
|
|
|
|
|
|
/* common code */
|
|
|
|
in( iMTCTR, r0 );
|
|
|
|
in( iBCTRL );
|
|
|
|
|
|
|
|
if ( rFIRST != r3 )
|
|
|
|
in( iMR, rFIRST, r3 ); // push return value on the top of the opstack
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_PUSH:
|
|
|
|
MAYBE_EMIT_CONST();
|
|
|
|
if ( RET_INT )
|
|
|
|
gpr_pos++;
|
|
|
|
else
|
|
|
|
fpr_pos++;
|
|
|
|
/* no instructions here */
|
|
|
|
force_emit = 1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_POP:
|
|
|
|
MAYBE_EMIT_CONST();
|
|
|
|
if ( ARG_INT )
|
|
|
|
gpr_pos--;
|
|
|
|
else
|
|
|
|
fpr_pos--;
|
|
|
|
/* no instructions here */
|
|
|
|
force_emit = 1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_CONST:
|
|
|
|
MAYBE_EMIT_CONST();
|
|
|
|
/* nothing here */
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_LOCAL:
|
|
|
|
MAYBE_EMIT_CONST();
|
|
|
|
{
|
|
|
|
signed long int hi, lo;
|
|
|
|
hi = i_now->arg.ss[ 0 ];
|
|
|
|
lo = i_now->arg.ss[ 1 ];
|
|
|
|
if ( lo < 0 )
|
|
|
|
hi += 1;
|
|
|
|
|
|
|
|
gpr_pos++;
|
|
|
|
if ( hi == 0 ) {
|
|
|
|
in( iADDI, rFIRST, rPSTACK, lo );
|
|
|
|
} else {
|
|
|
|
in( iADDIS, rFIRST, rPSTACK, hi );
|
|
|
|
if ( lo != 0 )
|
|
|
|
in( iADDI, rFIRST, rFIRST, lo );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_JUMP:
|
|
|
|
if ( i_const ) {
|
|
|
|
EMIT_FALSE_CONST();
|
|
|
|
|
|
|
|
emitJump(
|
|
|
|
i_const->arg.i,
|
|
|
|
branchAlways, 0, 0
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
MAYBE_EMIT_CONST();
|
|
|
|
|
|
|
|
in( iLL, r0, VM_Data_Offset( iPointers ), rVMDATA );
|
|
|
|
in( iRLWINM, rFIRST, rFIRST, GPRLEN_SHIFT, 0, 31-GPRLEN_SHIFT ); // mul * GPRLEN
|
|
|
|
in( iLLX, r0, rFIRST, r0 ); // load pointer
|
|
|
|
in( iMTCTR, r0 );
|
|
|
|
in( iBCTR );
|
|
|
|
}
|
|
|
|
gpr_pos--;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_EQ:
|
|
|
|
case OP_NE:
|
|
|
|
if ( i_const && i_const->arg.si >= -0x8000 && i_const->arg.si < 0x10000 ) {
|
|
|
|
EMIT_FALSE_CONST();
|
|
|
|
if ( i_const->arg.si >= 0x8000 )
|
|
|
|
in( iCMPLWI, cr7, rSECOND, i_const->arg.i );
|
|
|
|
else
|
|
|
|
in( iCMPWI, cr7, rSECOND, i_const->arg.si );
|
|
|
|
} else {
|
|
|
|
MAYBE_EMIT_CONST();
|
|
|
|
in( iCMPW, cr7, rSECOND, rFIRST );
|
|
|
|
}
|
|
|
|
emitJump(
|
|
|
|
i_now->arg.i,
|
|
|
|
(i_now->op == OP_EQ ? branchTrue : branchFalse),
|
|
|
|
4*cr7+eq, 0
|
|
|
|
);
|
|
|
|
gpr_pos -= 2;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_LTI:
|
|
|
|
case OP_GEI:
|
|
|
|
if ( i_const && i_const->arg.si >= -0x8000 && i_const->arg.si < 0x8000 ) {
|
|
|
|
EMIT_FALSE_CONST();
|
|
|
|
in( iCMPWI, cr7, rSECOND, i_const->arg.si );
|
|
|
|
} else {
|
|
|
|
MAYBE_EMIT_CONST();
|
|
|
|
in( iCMPW, cr7, rSECOND, rFIRST );
|
|
|
|
}
|
|
|
|
emitJump(
|
|
|
|
i_now->arg.i,
|
|
|
|
( i_now->op == OP_LTI ? branchTrue : branchFalse ),
|
|
|
|
4*cr7+lt, 0
|
|
|
|
);
|
|
|
|
gpr_pos -= 2;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_GTI:
|
|
|
|
case OP_LEI:
|
|
|
|
if ( i_const && i_const->arg.si >= -0x8000 && i_const->arg.si < 0x8000 ) {
|
|
|
|
EMIT_FALSE_CONST();
|
|
|
|
in( iCMPWI, cr7, rSECOND, i_const->arg.si );
|
|
|
|
} else {
|
|
|
|
MAYBE_EMIT_CONST();
|
|
|
|
in( iCMPW, cr7, rSECOND, rFIRST );
|
|
|
|
}
|
|
|
|
emitJump(
|
|
|
|
i_now->arg.i,
|
|
|
|
( i_now->op == OP_GTI ? branchTrue : branchFalse ),
|
|
|
|
4*cr7+gt, 0
|
|
|
|
);
|
|
|
|
gpr_pos -= 2;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_LTU:
|
|
|
|
case OP_GEU:
|
|
|
|
if ( i_const && i_const->arg.i < 0x10000 ) {
|
|
|
|
EMIT_FALSE_CONST();
|
|
|
|
in( iCMPLWI, cr7, rSECOND, i_const->arg.i );
|
|
|
|
} else {
|
|
|
|
MAYBE_EMIT_CONST();
|
|
|
|
in( iCMPLW, cr7, rSECOND, rFIRST );
|
|
|
|
}
|
|
|
|
emitJump(
|
|
|
|
i_now->arg.i,
|
|
|
|
( i_now->op == OP_LTU ? branchTrue : branchFalse ),
|
|
|
|
4*cr7+lt, 0
|
|
|
|
);
|
|
|
|
gpr_pos -= 2;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_GTU:
|
|
|
|
case OP_LEU:
|
|
|
|
if ( i_const && i_const->arg.i < 0x10000 ) {
|
|
|
|
EMIT_FALSE_CONST();
|
|
|
|
in( iCMPLWI, cr7, rSECOND, i_const->arg.i );
|
|
|
|
} else {
|
|
|
|
MAYBE_EMIT_CONST();
|
|
|
|
in( iCMPLW, cr7, rSECOND, rFIRST );
|
|
|
|
}
|
|
|
|
emitJump(
|
|
|
|
i_now->arg.i,
|
|
|
|
( i_now->op == OP_GTU ? branchTrue : branchFalse ),
|
|
|
|
4*cr7+gt, 0
|
|
|
|
);
|
|
|
|
gpr_pos -= 2;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_EQF:
|
|
|
|
case OP_NEF:
|
|
|
|
MAYBE_EMIT_CONST();
|
|
|
|
in( iFCMPU, cr7, fSECOND, fFIRST );
|
|
|
|
emitJump(
|
|
|
|
i_now->arg.i,
|
|
|
|
( i_now->op == OP_EQF ? branchTrue : branchFalse ),
|
|
|
|
4*cr7+eq, 0
|
|
|
|
);
|
|
|
|
fpr_pos -= 2;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_LTF:
|
|
|
|
case OP_GEF:
|
|
|
|
MAYBE_EMIT_CONST();
|
|
|
|
in( iFCMPU, cr7, fSECOND, fFIRST );
|
|
|
|
emitJump(
|
|
|
|
i_now->arg.i,
|
|
|
|
( i_now->op == OP_LTF ? branchTrue : branchFalse ),
|
|
|
|
4*cr7+lt, 0
|
|
|
|
);
|
|
|
|
fpr_pos -= 2;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_GTF:
|
|
|
|
case OP_LEF:
|
|
|
|
MAYBE_EMIT_CONST();
|
|
|
|
in( iFCMPU, cr7, fSECOND, fFIRST );
|
|
|
|
emitJump(
|
|
|
|
i_now->arg.i,
|
|
|
|
( i_now->op == OP_GTF ? branchTrue : branchFalse ),
|
|
|
|
4*cr7+gt, 0
|
|
|
|
);
|
|
|
|
fpr_pos -= 2;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_LOAD1:
|
|
|
|
MAYBE_EMIT_CONST();
|
|
|
|
#if OPTIMIZE_MASK
|
|
|
|
in( iRLWINM, rFIRST, rFIRST, 0, fastMaskHi, fastMaskLo );
|
|
|
|
#else
|
|
|
|
in( iLWZ, r0, VM_Data_Offset( dataMask ), rVMDATA );
|
|
|
|
in( iAND, rFIRST, rFIRST, r0 );
|
|
|
|
#endif
|
|
|
|
in( iLBZX, rFIRST, rFIRST, rDATABASE );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_LOAD2:
|
|
|
|
MAYBE_EMIT_CONST();
|
|
|
|
#if OPTIMIZE_MASK
|
|
|
|
in( iRLWINM, rFIRST, rFIRST, 0, fastMaskHi, fastMaskLo );
|
|
|
|
#else
|
|
|
|
in( iLWZ, r0, VM_Data_Offset( dataMask ), rVMDATA );
|
|
|
|
in( iAND, rFIRST, rFIRST, r0 );
|
|
|
|
#endif
|
|
|
|
in( iLHZX, rFIRST, rFIRST, rDATABASE );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_LOAD4:
|
|
|
|
MAYBE_EMIT_CONST();
|
|
|
|
#if OPTIMIZE_MASK
|
|
|
|
in( iRLWINM, rFIRST, rFIRST, 0, fastMaskHi, fastMaskLo );
|
|
|
|
#else
|
|
|
|
in( iLWZ, r0, VM_Data_Offset( dataMask ), rVMDATA );
|
|
|
|
in( iAND, rFIRST, rFIRST, r0 );
|
|
|
|
#endif
|
|
|
|
if ( RET_INT ) {
|
|
|
|
in( iLWZX, rFIRST, rFIRST, rDATABASE );
|
|
|
|
} else {
|
|
|
|
fpr_pos++;
|
|
|
|
in( iLFSX, fFIRST, rFIRST, rDATABASE );
|
|
|
|
gpr_pos--;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_STORE1:
|
|
|
|
MAYBE_EMIT_CONST();
|
|
|
|
#if OPTIMIZE_MASK
|
|
|
|
in( iRLWINM, rSECOND, rSECOND, 0, fastMaskHi, fastMaskLo );
|
|
|
|
#else
|
|
|
|
in( iLWZ, r0, VM_Data_Offset( dataMask ), rVMDATA );
|
|
|
|
in( iAND, rSECOND, rSECOND, r0 );
|
|
|
|
#endif
|
|
|
|
in( iSTBX, rFIRST, rSECOND, rDATABASE );
|
|
|
|
gpr_pos -= 2;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_STORE2:
|
|
|
|
MAYBE_EMIT_CONST();
|
|
|
|
#if OPTIMIZE_MASK
|
|
|
|
in( iRLWINM, rSECOND, rSECOND, 0, fastMaskHi, fastMaskLo );
|
|
|
|
#else
|
|
|
|
in( iLWZ, r0, VM_Data_Offset( dataMask ), rVMDATA );
|
|
|
|
in( iAND, rSECOND, rSECOND, r0 );
|
|
|
|
#endif
|
|
|
|
in( iSTHX, rFIRST, rSECOND, rDATABASE );
|
|
|
|
gpr_pos -= 2;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_STORE4:
|
|
|
|
MAYBE_EMIT_CONST();
|
|
|
|
if ( ARG_INT ) {
|
|
|
|
#if OPTIMIZE_MASK
|
|
|
|
in( iRLWINM, rSECOND, rSECOND, 0, fastMaskHi, fastMaskLo );
|
|
|
|
#else
|
|
|
|
in( iLWZ, r0, VM_Data_Offset( dataMask ), rVMDATA );
|
|
|
|
in( iAND, rSECOND, rSECOND, r0 );
|
|
|
|
#endif
|
|
|
|
|
|
|
|
in( iSTWX, rFIRST, rSECOND, rDATABASE );
|
|
|
|
gpr_pos--;
|
|
|
|
} else {
|
|
|
|
#if OPTIMIZE_MASK
|
|
|
|
in( iRLWINM, rFIRST, rFIRST, 0, fastMaskHi, fastMaskLo );
|
|
|
|
#else
|
|
|
|
in( iLWZ, r0, VM_Data_Offset( dataMask ), rVMDATA );
|
|
|
|
in( iAND, rFIRST, rFIRST, r0 );
|
|
|
|
#endif
|
|
|
|
|
|
|
|
in( iSTFSX, fFIRST, rFIRST, rDATABASE );
|
|
|
|
fpr_pos--;
|
|
|
|
}
|
|
|
|
gpr_pos--;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_ARG:
|
|
|
|
MAYBE_EMIT_CONST();
|
|
|
|
in( iADDI, r0, rPSTACK, i_now->arg.b );
|
|
|
|
if ( ARG_INT ) {
|
|
|
|
in( iSTWX, rFIRST, rDATABASE, r0 );
|
|
|
|
gpr_pos--;
|
|
|
|
} else {
|
|
|
|
in( iSTFSX, fFIRST, rDATABASE, r0 );
|
|
|
|
fpr_pos--;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_BLOCK_COPY:
|
|
|
|
MAYBE_EMIT_CONST();
|
|
|
|
#if OPTIMIZE_COPY
|
|
|
|
if ( i_now->arg.i <= SL( 16, 32 ) ) {
|
|
|
|
/* block is very short so copy it in-place */
|
|
|
|
|
|
|
|
unsigned int len = i_now->arg.i;
|
|
|
|
unsigned int copied = 0, left = len;
|
|
|
|
|
|
|
|
in( iADD, rFIRST, rFIRST, rDATABASE );
|
|
|
|
in( iADD, rSECOND, rSECOND, rDATABASE );
|
|
|
|
|
|
|
|
if ( len >= GPRLEN ) {
|
|
|
|
long int i, words = len / GPRLEN;
|
|
|
|
in( iLL, r0, 0, rFIRST );
|
|
|
|
for ( i = 1; i < words; i++ )
|
|
|
|
in( iLL, rTEMP( i - 1 ), GPRLEN * i, rFIRST );
|
|
|
|
|
|
|
|
in( iSTL, r0, 0, rSECOND );
|
|
|
|
for ( i = 1; i < words; i++ )
|
|
|
|
in( iSTL, rTEMP( i - 1 ), GPRLEN * i, rSECOND );
|
|
|
|
|
|
|
|
copied += words * GPRLEN;
|
|
|
|
left -= copied;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( SL( 0, left >= 4 ) ) {
|
|
|
|
in( iLWZ, r0, copied+0, rFIRST );
|
|
|
|
in( iSTW, r0, copied+0, rSECOND );
|
|
|
|
copied += 4;
|
|
|
|
left -= 4;
|
|
|
|
}
|
|
|
|
if ( left >= 4 ) {
|
|
|
|
DIE("Bug in OP_BLOCK_COPY");
|
|
|
|
}
|
|
|
|
if ( left == 3 ) {
|
|
|
|
in( iLHZ, r0, copied+0, rFIRST );
|
|
|
|
in( iLBZ, rTMP, copied+2, rFIRST );
|
|
|
|
in( iSTH, r0, copied+0, rSECOND );
|
|
|
|
in( iSTB, rTMP, copied+2, rSECOND );
|
|
|
|
} else if ( left == 2 ) {
|
|
|
|
in( iLHZ, r0, copied+0, rFIRST );
|
|
|
|
in( iSTH, r0, copied+0, rSECOND );
|
|
|
|
} else if ( left == 1 ) {
|
|
|
|
in( iLBZ, r0, copied+0, rFIRST );
|
|
|
|
in( iSTB, r0, copied+0, rSECOND );
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
unsigned long int r5_ori = 0;
|
|
|
|
if ( i_now->arg.si >= -0x8000 && i_now->arg.si < 0x8000 ) {
|
|
|
|
in( iLI, r5, i_now->arg.si );
|
|
|
|
} else if ( i_now->arg.i < 0x10000 ) {
|
|
|
|
in( iLI, r5, 0 );
|
|
|
|
r5_ori = i_now->arg.i;
|
|
|
|
} else {
|
|
|
|
in( iLIS, r5, i_now->arg.ss[ 0 ] );
|
|
|
|
r5_ori = i_now->arg.us[ 1 ];
|
|
|
|
}
|
|
|
|
|
|
|
|
in( iLL, r0, VM_Data_Offset( BlockCopy ), rVMDATA ); // get blockCopy pointer
|
|
|
|
|
|
|
|
if ( r5_ori )
|
|
|
|
in( iORI, r5, r5, r5_ori );
|
|
|
|
|
|
|
|
in( iMTCTR, r0 );
|
|
|
|
|
|
|
|
if ( rFIRST != r4 )
|
|
|
|
in( iMR, r4, rFIRST );
|
|
|
|
if ( rSECOND != r3 )
|
|
|
|
in( iMR, r3, rSECOND );
|
|
|
|
|
|
|
|
in( iBCTRL );
|
|
|
|
}
|
|
|
|
|
|
|
|
gpr_pos -= 2;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_SEX8:
|
|
|
|
MAYBE_EMIT_CONST();
|
|
|
|
in( iEXTSB, rFIRST, rFIRST );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_SEX16:
|
|
|
|
MAYBE_EMIT_CONST();
|
|
|
|
in( iEXTSH, rFIRST, rFIRST );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_NEGI:
|
|
|
|
MAYBE_EMIT_CONST();
|
|
|
|
in( iNEG, rFIRST, rFIRST );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_ADD:
|
|
|
|
if ( i_const ) {
|
|
|
|
EMIT_FALSE_CONST();
|
|
|
|
|
|
|
|
signed short int hi, lo;
|
|
|
|
hi = i_const->arg.ss[ 0 ];
|
|
|
|
lo = i_const->arg.ss[ 1 ];
|
|
|
|
if ( lo < 0 )
|
|
|
|
hi += 1;
|
|
|
|
|
|
|
|
if ( hi != 0 )
|
|
|
|
in( iADDIS, rSECOND, rSECOND, hi );
|
|
|
|
if ( lo != 0 )
|
|
|
|
in( iADDI, rSECOND, rSECOND, lo );
|
|
|
|
|
|
|
|
// if both are zero no instruction will be written
|
|
|
|
if ( hi == 0 && lo == 0 )
|
|
|
|
force_emit = 1;
|
|
|
|
} else {
|
|
|
|
MAYBE_EMIT_CONST();
|
|
|
|
in( iADD, rSECOND, rSECOND, rFIRST );
|
|
|
|
}
|
|
|
|
gpr_pos--;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_SUB:
|
|
|
|
MAYBE_EMIT_CONST();
|
|
|
|
in( iSUB, rSECOND, rSECOND, rFIRST );
|
|
|
|
gpr_pos--;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_DIVI:
|
|
|
|
MAYBE_EMIT_CONST();
|
|
|
|
in( iDIVW, rSECOND, rSECOND, rFIRST );
|
|
|
|
gpr_pos--;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_DIVU:
|
|
|
|
MAYBE_EMIT_CONST();
|
|
|
|
in( iDIVWU, rSECOND, rSECOND, rFIRST );
|
|
|
|
gpr_pos--;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_MODI:
|
|
|
|
MAYBE_EMIT_CONST();
|
|
|
|
in( iDIVW, r0, rSECOND, rFIRST );
|
|
|
|
in( iMULLW, r0, r0, rFIRST );
|
|
|
|
in( iSUB, rSECOND, rSECOND, r0 );
|
|
|
|
gpr_pos--;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_MODU:
|
|
|
|
MAYBE_EMIT_CONST();
|
|
|
|
in( iDIVWU, r0, rSECOND, rFIRST );
|
|
|
|
in( iMULLW, r0, r0, rFIRST );
|
|
|
|
in( iSUB, rSECOND, rSECOND, r0 );
|
|
|
|
gpr_pos--;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_MULI:
|
|
|
|
case OP_MULU:
|
|
|
|
MAYBE_EMIT_CONST();
|
|
|
|
in( iMULLW, rSECOND, rSECOND, rFIRST );
|
|
|
|
gpr_pos--;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_BAND:
|
|
|
|
MAYBE_EMIT_CONST();
|
|
|
|
in( iAND, rSECOND, rSECOND, rFIRST );
|
|
|
|
gpr_pos--;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_BOR:
|
|
|
|
MAYBE_EMIT_CONST();
|
|
|
|
in( iOR, rSECOND, rSECOND, rFIRST );
|
|
|
|
gpr_pos--;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_BXOR:
|
|
|
|
MAYBE_EMIT_CONST();
|
|
|
|
in( iXOR, rSECOND, rSECOND, rFIRST );
|
|
|
|
gpr_pos--;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_BCOM:
|
|
|
|
MAYBE_EMIT_CONST();
|
|
|
|
in( iNOT, rFIRST, rFIRST );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_LSH:
|
|
|
|
MAYBE_EMIT_CONST();
|
|
|
|
in( iSLW, rSECOND, rSECOND, rFIRST );
|
|
|
|
gpr_pos--;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_RSHI:
|
|
|
|
MAYBE_EMIT_CONST();
|
|
|
|
in( iSRAW, rSECOND, rSECOND, rFIRST );
|
|
|
|
gpr_pos--;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_RSHU:
|
|
|
|
MAYBE_EMIT_CONST();
|
|
|
|
in( iSRW, rSECOND, rSECOND, rFIRST );
|
|
|
|
gpr_pos--;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_NEGF:
|
|
|
|
MAYBE_EMIT_CONST();
|
|
|
|
in( iFNEG, fFIRST, fFIRST );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_ADDF:
|
|
|
|
MAYBE_EMIT_CONST();
|
|
|
|
in( iFADDS, fSECOND, fSECOND, fFIRST );
|
|
|
|
fpr_pos--;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_SUBF:
|
|
|
|
MAYBE_EMIT_CONST();
|
|
|
|
in( iFSUBS, fSECOND, fSECOND, fFIRST );
|
|
|
|
fpr_pos--;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_DIVF:
|
|
|
|
MAYBE_EMIT_CONST();
|
|
|
|
in( iFDIVS, fSECOND, fSECOND, fFIRST );
|
|
|
|
fpr_pos--;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_MULF:
|
|
|
|
MAYBE_EMIT_CONST();
|
|
|
|
in( iFMULS, fSECOND, fSECOND, fFIRST );
|
|
|
|
fpr_pos--;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_CVIF:
|
|
|
|
MAYBE_EMIT_CONST();
|
|
|
|
fpr_pos++;
|
|
|
|
in( iXORIS, rFIRST, rFIRST, 0x8000 );
|
|
|
|
in( iLIS, r0, 0x4330 );
|
|
|
|
in( iSTW, rFIRST, stack_temp + 4, r1 );
|
|
|
|
in( iSTW, r0, stack_temp, r1 );
|
|
|
|
in( iLFS, fTMP, VM_Data_Offset( floatBase ), rVMDATA );
|
|
|
|
in( iLFD, fFIRST, stack_temp, r1 );
|
|
|
|
in( iFSUB, fFIRST, fFIRST, fTMP );
|
|
|
|
in( iFRSP, fFIRST, fFIRST );
|
|
|
|
gpr_pos--;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_CVFI:
|
|
|
|
MAYBE_EMIT_CONST();
|
|
|
|
gpr_pos++;
|
|
|
|
in( iFCTIWZ, fFIRST, fFIRST );
|
|
|
|
in( iSTFD, fFIRST, stack_temp, r1 );
|
|
|
|
in( iLWZ, rFIRST, stack_temp + 4, r1 );
|
|
|
|
fpr_pos--;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
i_const = NULL;
|
|
|
|
|
|
|
|
if ( i_now->op != OP_CONST ) {
|
|
|
|
// emit the instructions if it isn't OP_CONST
|
|
|
|
emitEnd();
|
|
|
|
} else {
|
|
|
|
// mark in what register the value should be saved
|
|
|
|
if ( RET_INT )
|
|
|
|
i_now->regPos = ++gpr_pos;
|
|
|
|
else
|
|
|
|
i_now->regPos = ++fpr_pos;
|
|
|
|
|
|
|
|
#if OPTIMIZE_HOLE
|
|
|
|
i_const = i_now;
|
|
|
|
#else
|
|
|
|
PPC_EmitConst( i_now );
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ( i_const )
|
|
|
|
DIE( "left (unused) OP_CONST" );
|
|
|
|
|
|
|
|
{
|
|
|
|
// free opcode information, don't free first dummy one
|
|
|
|
source_instruction_t *i_next = i_first->next;
|
|
|
|
while ( i_next ) {
|
|
|
|
i_now = i_next;
|
|
|
|
i_next = i_now->next;
|
|
|
|
PPC_Free( i_now );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* check which jumps are short enough to use signed 16bit immediate branch
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
PPC_ShrinkJumps( void )
|
|
|
|
{
|
|
|
|
symbolic_jump_t *sj_now = sj_first;
|
|
|
|
while ( (sj_now = sj_now->nextJump) ) {
|
|
|
|
if ( sj_now->bo == branchAlways )
|
|
|
|
// non-conditional branch has 26bit immediate
|
|
|
|
sj_now->parent->length = 1;
|
|
|
|
|
|
|
|
else {
|
|
|
|
dest_instruction_t *di = di_pointers[ sj_now->jump_to ];
|
|
|
|
dest_instruction_t *ji = sj_now->parent;
|
|
|
|
long int jump_length = 0;
|
|
|
|
if ( ! di )
|
|
|
|
DIE( "No instruction to jump to" );
|
|
|
|
if ( ji->count > di->count ) {
|
|
|
|
do {
|
|
|
|
jump_length += di->length;
|
|
|
|
} while ( ( di = di->next ) != ji );
|
|
|
|
} else {
|
|
|
|
jump_length = 1;
|
|
|
|
while ( ( ji = ji->next ) != di )
|
|
|
|
jump_length += ji->length;
|
|
|
|
}
|
|
|
|
if ( jump_length < 0x2000 )
|
|
|
|
// jump is short, use normal instruction
|
|
|
|
sj_now->parent->length = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* puts all the data in one place, it consists of many different tasks
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
PPC_ComputeCode( vm_t *vm )
|
|
|
|
{
|
|
|
|
dest_instruction_t *di_now = di_first;
|
|
|
|
|
|
|
|
unsigned long int codeInstructions = 0;
|
|
|
|
// count total instruciton number
|
|
|
|
while ( (di_now = di_now->next ) )
|
|
|
|
codeInstructions += di_now->length;
|
|
|
|
|
|
|
|
size_t codeLength = sizeof( vm_data_t )
|
|
|
|
+ sizeof( unsigned int ) * data_acc
|
|
|
|
+ sizeof( ppc_instruction_t ) * codeInstructions;
|
|
|
|
|
|
|
|
// get the memory for the generated code, smarter ppcs need the
|
|
|
|
// mem to be marked as executable (whill change later)
|
|
|
|
unsigned char *dataAndCode = mmap( NULL, codeLength,
|
|
|
|
PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0 );
|
|
|
|
|
2011-07-26 08:52:24 +00:00
|
|
|
if (dataAndCode == MAP_FAILED)
|
2011-02-18 14:31:32 +00:00
|
|
|
DIE( "Not enough memory" );
|
|
|
|
|
|
|
|
ppc_instruction_t *codeNow, *codeBegin;
|
|
|
|
codeNow = codeBegin = (ppc_instruction_t *)( dataAndCode + VM_Data_Offset( data[ data_acc ] ) );
|
|
|
|
|
|
|
|
ppc_instruction_t nop = IN( iNOP );
|
|
|
|
|
|
|
|
// copy instructions to the destination
|
|
|
|
// fills the jump instructions with nops
|
|
|
|
// saves pointers of all instructions
|
|
|
|
di_now = di_first;
|
|
|
|
while ( (di_now = di_now->next ) ) {
|
|
|
|
unsigned long int i_count = di_now->i_count;
|
|
|
|
if ( i_count != FALSE_ICOUNT ) {
|
|
|
|
if ( ! di_pointers[ i_count ] )
|
|
|
|
di_pointers[ i_count ] = (void *) codeNow;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( di_now->jump == NULL ) {
|
|
|
|
memcpy( codeNow, &(di_now->code[0]), di_now->length * sizeof( ppc_instruction_t ) );
|
|
|
|
codeNow += di_now->length;
|
|
|
|
} else {
|
|
|
|
long int i;
|
|
|
|
symbolic_jump_t *sj;
|
|
|
|
for ( i = 0; i < di_now->length; i++ )
|
|
|
|
codeNow[ i ] = nop;
|
|
|
|
codeNow += di_now->length;
|
|
|
|
|
|
|
|
sj = di_now->jump;
|
|
|
|
// save position of jumping instruction
|
|
|
|
sj->parent = (void *)(codeNow - 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// compute the jumps and write corresponding instructions
|
|
|
|
symbolic_jump_t *sj_now = sj_first;
|
|
|
|
while ( (sj_now = sj_now->nextJump ) ) {
|
|
|
|
ppc_instruction_t *jumpFrom = (void *) sj_now->parent;
|
|
|
|
ppc_instruction_t *jumpTo = (void *) di_pointers[ sj_now->jump_to ];
|
|
|
|
signed long int jumpLength = jumpTo - jumpFrom;
|
|
|
|
|
|
|
|
// if jump is short, just write it
|
|
|
|
if ( jumpLength >= - 8192 && jumpLength < 8192 ) {
|
|
|
|
powerpc_iname_t branchConditional = sj_now->ext & branchExtLink ? iBCL : iBC;
|
|
|
|
*jumpFrom = IN( branchConditional, sj_now->bo, sj_now->bi, jumpLength * 4 );
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// jump isn't short so write it as two instructions
|
|
|
|
//
|
|
|
|
// the letter one is a non-conditional branch instruction which
|
|
|
|
// accepts immediate values big enough (26 bits)
|
|
|
|
*jumpFrom = IN( (sj_now->ext & branchExtLink ? iBL : iB), jumpLength * 4 );
|
|
|
|
if ( sj_now->bo == branchAlways )
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// there should have been additional space prepared for this case
|
|
|
|
if ( jumpFrom[ -1 ] != nop )
|
|
|
|
DIE( "additional space for long jump not prepared" );
|
|
|
|
|
|
|
|
// invert instruction condition
|
|
|
|
long int bo = 0;
|
|
|
|
switch ( sj_now->bo ) {
|
|
|
|
case branchTrue:
|
|
|
|
bo = branchFalse;
|
|
|
|
break;
|
|
|
|
case branchFalse:
|
|
|
|
bo = branchTrue;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
DIE( "unrecognized branch type" );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// the former instruction is an inverted conditional branch which
|
|
|
|
// jumps over the non-conditional one
|
|
|
|
jumpFrom[ -1 ] = IN( iBC, bo, sj_now->bi, +2*4 );
|
|
|
|
}
|
|
|
|
|
|
|
|
vm->codeBase = dataAndCode;
|
|
|
|
vm->codeLength = codeLength;
|
|
|
|
|
|
|
|
vm_data_t *data = (vm_data_t *)dataAndCode;
|
|
|
|
|
|
|
|
#if ELF64
|
|
|
|
// prepare Official Procedure Descriptor for the generated code
|
|
|
|
// and retrieve real function pointer for helper functions
|
|
|
|
|
|
|
|
opd_t *ac = (void *)VM_AsmCall, *bc = (void *)VM_BlockCopy;
|
|
|
|
data->opd.function = codeBegin;
|
|
|
|
// trick it into using the same TOC
|
|
|
|
// this way we won't have to switch TOC before calling AsmCall or BlockCopy
|
|
|
|
data->opd.toc = ac->toc;
|
|
|
|
data->opd.env = ac->env;
|
|
|
|
|
|
|
|
data->AsmCall = ac->function;
|
|
|
|
data->BlockCopy = bc->function;
|
|
|
|
#else
|
|
|
|
data->AsmCall = VM_AsmCall;
|
|
|
|
data->BlockCopy = VM_BlockCopy;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
data->dataMask = vm->dataMask;
|
|
|
|
data->iPointers = (ppc_instruction_t *)vm->instructionPointers;
|
|
|
|
data->dataLength = VM_Data_Offset( data[ data_acc ] );
|
|
|
|
data->codeLength = ( codeNow - codeBegin ) * sizeof( ppc_instruction_t );
|
|
|
|
data->floatBase = 0x59800004;
|
|
|
|
|
|
|
|
|
|
|
|
/* write dynamic data (float constants) */
|
|
|
|
{
|
|
|
|
local_data_t *d_next, *d_now = data_first;
|
|
|
|
long int accumulated = 0;
|
|
|
|
|
|
|
|
do {
|
|
|
|
long int i;
|
|
|
|
for ( i = 0; i < d_now->count; i++ )
|
|
|
|
data->data[ accumulated + i ] = d_now->data[ i ];
|
|
|
|
|
|
|
|
accumulated += d_now->count;
|
|
|
|
d_next = d_now->next;
|
|
|
|
PPC_Free( d_now );
|
|
|
|
|
|
|
|
if ( !d_next )
|
|
|
|
break;
|
|
|
|
d_now = d_next;
|
|
|
|
} while (1);
|
|
|
|
data_first = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* free most of the compilation memory */
|
|
|
|
{
|
|
|
|
di_now = di_first->next;
|
|
|
|
PPC_Free( di_first );
|
|
|
|
PPC_Free( sj_first );
|
|
|
|
|
|
|
|
while ( di_now ) {
|
|
|
|
di_first = di_now->next;
|
|
|
|
if ( di_now->jump )
|
|
|
|
PPC_Free( di_now->jump );
|
|
|
|
PPC_Free( di_now );
|
|
|
|
di_now = di_first;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
VM_Destroy_Compiled( vm_t *self )
|
|
|
|
{
|
|
|
|
if ( self->codeBase ) {
|
|
|
|
if ( munmap( self->codeBase, self->codeLength ) )
|
|
|
|
Com_Printf( S_COLOR_RED "Memory unmap failed, possible memory leak\n" );
|
|
|
|
}
|
|
|
|
self->codeBase = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
VM_Compile( vm_t *vm, vmHeader_t *header )
|
|
|
|
{
|
|
|
|
long int pc = 0;
|
|
|
|
unsigned long int i_count;
|
|
|
|
char* code;
|
|
|
|
struct timeval tvstart = {0, 0};
|
|
|
|
source_instruction_t *i_first /* dummy */, *i_last = NULL, *i_now;
|
|
|
|
|
|
|
|
vm->compiled = qfalse;
|
|
|
|
|
|
|
|
gettimeofday(&tvstart, NULL);
|
|
|
|
|
|
|
|
PPC_MakeFastMask( vm->dataMask );
|
|
|
|
|
|
|
|
i_first = PPC_Malloc( sizeof( source_instruction_t ) );
|
|
|
|
i_first->next = NULL;
|
|
|
|
|
|
|
|
// realloc instructionPointers with correct size
|
|
|
|
// use Z_Malloc so vm.c will be able to free the memory
|
|
|
|
if ( sizeof( void * ) != sizeof( int ) ) {
|
|
|
|
Z_Free( vm->instructionPointers );
|
|
|
|
vm->instructionPointers = Z_Malloc( header->instructionCount * sizeof( void * ) );
|
|
|
|
}
|
|
|
|
di_pointers = (void *)vm->instructionPointers;
|
|
|
|
memset( di_pointers, 0, header->instructionCount * sizeof( void * ) );
|
|
|
|
|
|
|
|
|
|
|
|
PPC_CompileInit();
|
|
|
|
|
|
|
|
/*
|
|
|
|
* read the input program
|
|
|
|
* divide it into functions and send each function to compiler
|
|
|
|
*/
|
|
|
|
code = (char *)header + header->codeOffset;
|
|
|
|
for ( i_count = 0; i_count < header->instructionCount; ++i_count )
|
|
|
|
{
|
|
|
|
unsigned char op = code[ pc++ ];
|
|
|
|
|
|
|
|
if ( op == OP_ENTER ) {
|
|
|
|
if ( i_first->next )
|
|
|
|
VM_CompileFunction( i_first );
|
|
|
|
i_first->next = NULL;
|
|
|
|
i_last = i_first;
|
|
|
|
}
|
|
|
|
|
|
|
|
i_now = PPC_Malloc( sizeof( source_instruction_t ) );
|
|
|
|
i_now->op = op;
|
|
|
|
i_now->i_count = i_count;
|
|
|
|
i_now->arg.i = 0;
|
|
|
|
i_now->regA1 = 0;
|
|
|
|
i_now->regA2 = 0;
|
|
|
|
i_now->regR = 0;
|
|
|
|
i_now->regPos = 0;
|
|
|
|
i_now->next = NULL;
|
|
|
|
|
|
|
|
if ( vm_opInfo[op] & opImm4 ) {
|
|
|
|
union {
|
|
|
|
unsigned char b[4];
|
|
|
|
unsigned int i;
|
|
|
|
} c = { { code[ pc + 3 ], code[ pc + 2 ], code[ pc + 1 ], code[ pc + 0 ] }, };
|
|
|
|
|
|
|
|
i_now->arg.i = c.i;
|
|
|
|
pc += 4;
|
|
|
|
} else if ( vm_opInfo[op] & opImm1 ) {
|
|
|
|
i_now->arg.b = code[ pc++ ];
|
|
|
|
}
|
|
|
|
|
|
|
|
i_last->next = i_now;
|
|
|
|
i_last = i_now;
|
|
|
|
}
|
|
|
|
VM_CompileFunction( i_first );
|
|
|
|
PPC_Free( i_first );
|
|
|
|
|
|
|
|
PPC_ShrinkJumps();
|
|
|
|
memset( di_pointers, 0, header->instructionCount * sizeof( void * ) );
|
|
|
|
PPC_ComputeCode( vm );
|
|
|
|
|
|
|
|
/* check for uninitialized pointers */
|
|
|
|
#ifdef DEBUG_VM
|
|
|
|
long int i;
|
|
|
|
for ( i = 0; i < header->instructionCount; i++ )
|
|
|
|
if ( di_pointers[ i ] == 0 )
|
|
|
|
Com_Printf( S_COLOR_RED "Pointer %ld not initialized !\n", i );
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* mark memory as executable and not writeable */
|
|
|
|
if ( mprotect( vm->codeBase, vm->codeLength, PROT_READ|PROT_EXEC ) ) {
|
|
|
|
|
|
|
|
// it has failed, make sure memory is unmapped before throwing the error
|
|
|
|
VM_Destroy_Compiled( vm );
|
|
|
|
DIE( "mprotect failed" );
|
|
|
|
}
|
|
|
|
|
|
|
|
vm->destroy = VM_Destroy_Compiled;
|
|
|
|
vm->compiled = qtrue;
|
|
|
|
|
|
|
|
{
|
|
|
|
struct timeval tvdone = {0, 0};
|
|
|
|
struct timeval dur = {0, 0};
|
|
|
|
Com_Printf( "VM file %s compiled to %i bytes of code (%p - %p)\n",
|
|
|
|
vm->name, vm->codeLength, vm->codeBase, vm->codeBase+vm->codeLength );
|
|
|
|
|
|
|
|
gettimeofday(&tvdone, NULL);
|
|
|
|
timersub(&tvdone, &tvstart, &dur);
|
|
|
|
Com_Printf( "compilation took %lu.%06lu seconds\n",
|
|
|
|
(long unsigned int)dur.tv_sec, (long unsigned int)dur.tv_usec );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
VM_CallCompiled( vm_t *vm, int *args )
|
|
|
|
{
|
|
|
|
int retVal;
|
|
|
|
int *argPointer;
|
|
|
|
|
|
|
|
vm_data_t *vm_dataAndCode = (void *)( vm->codeBase );
|
|
|
|
int programStack = vm->programStack;
|
|
|
|
int stackOnEntry = programStack;
|
|
|
|
|
|
|
|
byte *image = vm->dataBase;
|
|
|
|
|
|
|
|
currentVM = vm;
|
|
|
|
|
|
|
|
vm->currentlyInterpreting = qtrue;
|
|
|
|
|
ioquake3 resync to revision 2398 from 2369.
This is the last ioquake3 revision before ioquake3 changed from subversion to git at the beginning of 2013.
#5808 - Include and use .glsl in source (rend2)
#5812 - Use refdef's coordinates when drawing to screen shadow fbo, and separate depth texture and screen texture coordinates in glsl shaders.
Include Rend2 renderer in MacOSX bundle
Include OpenGL1 and Rend2 renderers in MacOSX UB
Include Rend2 renderer in NSIS installer.
Include OpenGL1 and Rend2 renderers in Loki Setup Installer.
Have NSIS uninstaller delete rend2.
Split light sample into direct and ambient parts when using deluxemaps or per-vertex light vectors. Fixes #5813.
Fix writting voip data in demos (broke in r2102).
Fix server ignoring client move commands if voip data is included.
Allow changing cl_voip without restarting.
Fix assert failing in CL_ParseVoip() while flipping cl_voip off and on.
Only declare var_SampleToView in lightall shader when it is actually used.
Fix a couple files not ending with a newline.
Fix clients being able to reset their player state and respawn using donedl.
Fix passing arg9 (qvm only), arg10, and arg11 to vmMain for native libs and non-i386 compiled or interpated qvms. (Currently they aren't use in vmMain in game, cgame, or ui.)
Fix passing args[11] to args[15] from vm to engine on ppc64 and sparc64. Some of the args are used by game bot prediction syscalls. May have been causing bugs. Note: This was fixed for x86_64 in r2163.
Fix reconnect command to work after leaving server. (#5794)
Fix dedicated server crashing when using MSG_ReadDelta*, though it only happens if someone modifies the engine. (#5449)
Makefile fixes for OpenBSD by Jonathan Gray. (#5728)
Save all arguments from connect for reconnect command.
Remove unnecessary localhost check from reconnect command.
Support r_srgb even without hardware support. Also tweak default autoexposure/tonemap settings to look good on both r_srgb 0 and 1.
Changed the MacOS-X build system to make UB's containing i386 and x86_64 arches and made make-macosx.sh not build UB's but only standard binaries
Fix spectator client being switched from follow to free after map_restart if following a client with a higher client number.
Fix client unlinking issue caused by ent->s.number being set to followed client's ps->clientNum after map_restart. Reported by Ensiform.
Changes from Ensiform:
- In G_AddBot, try to allocate clientNum before doing anything else.
- In G_AddBot, don't set SVF_BOT and inuse. It's done in ClientConnect, plus inuse causes ClientDisconnect to be run for no reason.
- In G_AddBot, only set skill in bot useinfo once.
- Avoid using cl->ps.clientNum to check if cl is a bot.
Fix bot skill format so it doesn't always have a space at the beginning of it.
More fixes to the macosx buildsystem. This removes the SDL Framework and makes use of a SDL library that is position independant. This also brings back PPC builds into the UB and also as a standa alone build choice.
Have make-macosx.sh require the user to specify which architecture she/he wants to build for and suggest building UB's if the user is unaware of what architectures are
Lets list all the valid options.
2017-07-09 21:21:12 +00:00
|
|
|
programStack -= ( 8 + 4 * MAX_VMMAIN_ARGS );
|
2011-02-18 14:31:32 +00:00
|
|
|
argPointer = (int *)&image[ programStack + 8 ];
|
ioquake3 resync to revision 2398 from 2369.
This is the last ioquake3 revision before ioquake3 changed from subversion to git at the beginning of 2013.
#5808 - Include and use .glsl in source (rend2)
#5812 - Use refdef's coordinates when drawing to screen shadow fbo, and separate depth texture and screen texture coordinates in glsl shaders.
Include Rend2 renderer in MacOSX bundle
Include OpenGL1 and Rend2 renderers in MacOSX UB
Include Rend2 renderer in NSIS installer.
Include OpenGL1 and Rend2 renderers in Loki Setup Installer.
Have NSIS uninstaller delete rend2.
Split light sample into direct and ambient parts when using deluxemaps or per-vertex light vectors. Fixes #5813.
Fix writting voip data in demos (broke in r2102).
Fix server ignoring client move commands if voip data is included.
Allow changing cl_voip without restarting.
Fix assert failing in CL_ParseVoip() while flipping cl_voip off and on.
Only declare var_SampleToView in lightall shader when it is actually used.
Fix a couple files not ending with a newline.
Fix clients being able to reset their player state and respawn using donedl.
Fix passing arg9 (qvm only), arg10, and arg11 to vmMain for native libs and non-i386 compiled or interpated qvms. (Currently they aren't use in vmMain in game, cgame, or ui.)
Fix passing args[11] to args[15] from vm to engine on ppc64 and sparc64. Some of the args are used by game bot prediction syscalls. May have been causing bugs. Note: This was fixed for x86_64 in r2163.
Fix reconnect command to work after leaving server. (#5794)
Fix dedicated server crashing when using MSG_ReadDelta*, though it only happens if someone modifies the engine. (#5449)
Makefile fixes for OpenBSD by Jonathan Gray. (#5728)
Save all arguments from connect for reconnect command.
Remove unnecessary localhost check from reconnect command.
Support r_srgb even without hardware support. Also tweak default autoexposure/tonemap settings to look good on both r_srgb 0 and 1.
Changed the MacOS-X build system to make UB's containing i386 and x86_64 arches and made make-macosx.sh not build UB's but only standard binaries
Fix spectator client being switched from follow to free after map_restart if following a client with a higher client number.
Fix client unlinking issue caused by ent->s.number being set to followed client's ps->clientNum after map_restart. Reported by Ensiform.
Changes from Ensiform:
- In G_AddBot, try to allocate clientNum before doing anything else.
- In G_AddBot, don't set SVF_BOT and inuse. It's done in ClientConnect, plus inuse causes ClientDisconnect to be run for no reason.
- In G_AddBot, only set skill in bot useinfo once.
- Avoid using cl->ps.clientNum to check if cl is a bot.
Fix bot skill format so it doesn't always have a space at the beginning of it.
More fixes to the macosx buildsystem. This removes the SDL Framework and makes use of a SDL library that is position independant. This also brings back PPC builds into the UB and also as a standa alone build choice.
Have make-macosx.sh require the user to specify which architecture she/he wants to build for and suggest building UB's if the user is unaware of what architectures are
Lets list all the valid options.
2017-07-09 21:21:12 +00:00
|
|
|
memcpy( argPointer, args, 4 * MAX_VMMAIN_ARGS );
|
2011-02-18 14:31:32 +00:00
|
|
|
argPointer[ -1 ] = 0;
|
|
|
|
argPointer[ -2 ] = -1;
|
|
|
|
|
|
|
|
#ifdef VM_TIMES
|
|
|
|
struct tms start_time, stop_time;
|
|
|
|
clock_t time_diff;
|
|
|
|
|
|
|
|
times( &start_time );
|
|
|
|
time_outside_vm = 0;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* call generated code */
|
|
|
|
{
|
|
|
|
int ( *entry )( void *, int, void * );
|
|
|
|
#ifdef __PPC64__
|
|
|
|
entry = (void *)&(vm_dataAndCode->opd);
|
|
|
|
#else
|
|
|
|
entry = (void *)(vm->codeBase + vm_dataAndCode->dataLength);
|
|
|
|
#endif
|
|
|
|
retVal = entry( vm->codeBase, programStack, vm->dataBase );
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef VM_TIMES
|
|
|
|
times( &stop_time );
|
|
|
|
time_diff = stop_time.tms_utime - start_time.tms_utime;
|
|
|
|
time_total_vm += time_diff - time_outside_vm;
|
|
|
|
if ( time_diff > 100 ) {
|
|
|
|
printf( "App clock: %ld, vm total: %ld, vm this: %ld, vm real: %ld, vm out: %ld\n"
|
|
|
|
"Inside VM %f%% of app time\n",
|
|
|
|
stop_time.tms_utime,
|
|
|
|
time_total_vm,
|
|
|
|
time_diff,
|
|
|
|
time_diff - time_outside_vm,
|
|
|
|
time_outside_vm,
|
|
|
|
(double)100 * time_total_vm / stop_time.tms_utime );
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
vm->programStack = stackOnEntry;
|
|
|
|
vm->currentlyInterpreting = qfalse;
|
|
|
|
|
|
|
|
return retVal;
|
|
|
|
}
|