diff options
author | Tim Angus <tim@ngus.net> | 2009-10-03 15:17:16 +0000 |
---|---|---|
committer | Tim Angus <tim@ngus.net> | 2013-01-03 00:16:38 +0000 |
commit | 6d2ffb4c637a49983bc6ce22b68ccec0ed09e0f4 (patch) | |
tree | ff15343e4a2ae5a2512c1c21e05a3821a46f10da /src/qcommon | |
parent | e9e52d0b7ec9bae071534df7581126d69d3e9bf8 (diff) |
* Merge ioq3-r1637
Diffstat (limited to 'src/qcommon')
-rw-r--r-- | src/qcommon/cmd.c | 10 | ||||
-rw-r--r-- | src/qcommon/common.c | 46 | ||||
-rw-r--r-- | src/qcommon/cvar.c | 69 | ||||
-rw-r--r-- | src/qcommon/files.c | 29 | ||||
-rw-r--r-- | src/qcommon/net_ip.c | 259 | ||||
-rw-r--r-- | src/qcommon/q_math.c | 330 | ||||
-rw-r--r-- | src/qcommon/q_platform.h | 9 | ||||
-rw-r--r-- | src/qcommon/q_shared.h | 14 | ||||
-rw-r--r-- | src/qcommon/qcommon.h | 14 | ||||
-rw-r--r-- | src/qcommon/vm.c | 11 | ||||
-rw-r--r-- | src/qcommon/vm_sparc.c | 1648 | ||||
-rw-r--r-- | src/qcommon/vm_sparc.h | 78 | ||||
-rw-r--r-- | src/qcommon/vm_x86.c | 26 | ||||
-rw-r--r-- | src/qcommon/vm_x86_64.c | 2 |
14 files changed, 2055 insertions, 490 deletions
diff --git a/src/qcommon/cmd.c b/src/qcommon/cmd.c index e04f314c..ecf747d8 100644 --- a/src/qcommon/cmd.c +++ b/src/qcommon/cmd.c @@ -53,6 +53,8 @@ bind g "cmd use rocket ; +attack ; wait ; -attack ; cmd use blaster" void Cmd_Wait_f( void ) { if ( Cmd_Argc() == 2 ) { cmd_wait = atoi( Cmd_Argv( 1 ) ); + if ( cmd_wait < 0 ) + cmd_wait = 1; // ignore the argument } else { cmd_wait = 1; } @@ -177,7 +179,7 @@ void Cbuf_Execute (void) while (cmd_text.cursize) { - if ( cmd_wait ) { + if ( cmd_wait > 0 ) { // skip out while text still remains in buffer, leaving it // for next frame cmd_wait--; @@ -296,11 +298,7 @@ Just prints the rest of the line to the console */ void Cmd_Echo_f (void) { - int i; - - for (i=1 ; i<Cmd_Argc() ; i++) - Com_Printf ("%s ",Cmd_Argv(i)); - Com_Printf ("\n"); + Com_Printf ("%s\n", Cmd_Args()); } diff --git a/src/qcommon/common.c b/src/qcommon/common.c index ef9ee76a..b97626ed 100644 --- a/src/qcommon/common.c +++ b/src/qcommon/common.c @@ -41,8 +41,6 @@ int demo_protocols[] = #define MIN_COMHUNKMEGS 128 #define DEF_COMHUNKMEGS 128 #define DEF_COMZONEMEGS 24 -#define XSTRING(x) STRING(x) -#define STRING(x) #x #define DEF_COMHUNKMEGS_S XSTRING(DEF_COMHUNKMEGS) #define DEF_COMZONEMEGS_S XSTRING(DEF_COMZONEMEGS) @@ -1398,9 +1396,11 @@ void Com_InitSmallZoneMemory( void ) { void Com_InitZoneMemory( void ) { cvar_t *cv; - //FIXME: 05/01/06 com_zoneMegs is useless right now as neither q3config.cfg nor - // Com_StartupVariable have been executed by this point. The net result is that - // s_zoneTotal will always be set to the default value. + // Please note: com_zoneMegs can only be set on the command line, and + // not in q3config.cfg or Com_StartupVariable, as they haven't been + // executed by this point. It's a chicken and egg problem. We need the + // memory manager configured to handle those places where you would + // configure the memory manager. // allocate the random block zone cv = Cvar_Get( "com_zoneMegs", DEF_COMZONEMEGS_S, CVAR_LATCH | CVAR_ARCHIVE ); @@ -2378,6 +2378,21 @@ static void Com_DetectAltivec(void) } } +/* +================= +Com_InitRand +Seed the random number generator, if possible with an OS supplied random seed. +================= +*/ +static void Com_InitRand(void) +{ + unsigned int seed; + + if(Sys_RandomBytes((byte *) &seed, sizeof(seed))) + srand(seed); + else + srand(time(NULL)); +} /* ================= @@ -2386,6 +2401,7 @@ Com_Init */ void Com_Init( char *commandLine ) { char *s; + int qport; Com_Printf( "%s %s %s\n", Q3_VERSION, PLATFORM_STRING, __DATE__ ); @@ -2397,8 +2413,11 @@ void Com_Init( char *commandLine ) { Com_Memset( &eventQueue[ 0 ], 0, MAX_QUEUED_EVENTS * sizeof( sysEvent_t ) ); Com_Memset( &sys_packetReceived[ 0 ], 0, MAX_MSGLEN * sizeof( byte ) ); - // do this before anything else decides to push events - Com_InitPushEvent(); + // initialize the weak pseudo-random number generator for use later. + Com_InitRand(); + + // do this before anything else decides to push events + Com_InitPushEvent(); Com_InitSmallZoneMemory(); Cvar_Init (); @@ -2410,12 +2429,12 @@ void Com_Init( char *commandLine ) { // Swap_Init (); Cbuf_Init (); - Com_InitZoneMemory(); - Cmd_Init (); - // override anything from the config files with command line args Com_StartupVariable( NULL ); + Com_InitZoneMemory(); + Cmd_Init (); + // get the developer cvar set as early as possible Com_StartupVariable( "developer" ); @@ -2501,7 +2520,11 @@ void Com_Init( char *commandLine ) { com_version = Cvar_Get ("version", s, CVAR_ROM | CVAR_SERVERINFO ); Sys_Init(); - Netchan_Init( Com_Milliseconds() & 0xffff ); // pick a port value that should be nice and random + + // Pick a random port value + Com_RandomBytes( (byte*)&qport, sizeof(int) ); + Netchan_Init( qport & 0xffff ); + VM_Init(); SV_Init(); @@ -3171,7 +3194,6 @@ void Com_RandomBytes( byte *string, int len ) return; Com_Printf( "Com_RandomBytes: using weak randomization\n" ); - srand( time( 0 ) ); for( i = 0; i < len; i++ ) string[i] = (unsigned char)( rand() % 255 ); } diff --git a/src/qcommon/cvar.c b/src/qcommon/cvar.c index c26ba543..0af10587 100644 --- a/src/qcommon/cvar.c +++ b/src/qcommon/cvar.c @@ -306,9 +306,9 @@ cvar_t *Cvar_Get( const char *var_name, const char *var_value, int flags ) { cvar_t *var; long hash; - if ( !var_name || ! var_value ) { + if ( !var_name || ! var_value ) { Com_Error( ERR_FATAL, "Cvar_Get: NULL parameter" ); - } + } if ( !Cvar_ValidateString( var_name ) ) { Com_Printf("invalid cvar name string: %s\n", var_name ); @@ -616,15 +616,15 @@ void Cvar_SetCheatState( void ) { // set all default vars to the safe value for ( var = cvar_vars ; var ; var = var->next ) { if ( var->flags & CVAR_CHEAT ) { - // the CVAR_LATCHED|CVAR_CHEAT vars might escape the reset here - // because of a different var->latchedString - if (var->latchedString) - { - Z_Free(var->latchedString); - var->latchedString = NULL; - } + // the CVAR_LATCHED|CVAR_CHEAT vars might escape the reset here + // because of a different var->latchedString + if (var->latchedString) + { + Z_Free(var->latchedString); + var->latchedString = NULL; + } if (strcmp(var->resetString,var->string)) { - Cvar_Set( var->name, var->resetString ); + Cvar_Set( var->name, var->resetString ); } } } @@ -653,7 +653,7 @@ qboolean Cvar_Command( void ) { } // set the value if forcing isn't required - Cvar_Set2 (v->name, Cmd_Argv(1), qfalse); + Cvar_Set2 (v->name, Cmd_Args(), qfalse); return qtrue; } @@ -691,21 +691,44 @@ void Cvar_Print_f(void) ============ Cvar_Toggle_f -Toggles a cvar for easy single key binding +Toggles a cvar for easy single key binding, optionally through a list of +given values ============ */ void Cvar_Toggle_f( void ) { - int v; + int i, c = Cmd_Argc(); + char *curval; - if ( Cmd_Argc() != 2 ) { - Com_Printf ("usage: toggle <variable>\n"); + if(c < 2) { + Com_Printf("usage: toggle <variable> [value1, value2, ...]\n"); + return; + } + + if(c == 2) { + Cvar_Set2(Cmd_Argv(1), va("%d", + !Cvar_VariableValue(Cmd_Argv(1))), + qfalse); + return; + } + + if(c == 3) { + Com_Printf("toggle: nothing to toggle to\n"); return; } - v = Cvar_VariableValue( Cmd_Argv( 1 ) ); - v = !v; + curval = Cvar_VariableString(Cmd_Argv(1)); - Cvar_Set2 (Cmd_Argv(1), va("%i", v), qfalse); + // don't bother checking the last arg for a match since the desired + // behaviour is the same as no match (set to the first argument) + for(i = 2; i + 1 < c; i++) { + if(strcmp(curval, Cmd_Argv(i)) == 0) { + Cvar_Set2(Cmd_Argv(1), Cmd_Argv(i + 1), qfalse); + return; + } + } + + // fallback + Cvar_Set2(Cmd_Argv(1), Cmd_Argv(2), qfalse); } /* @@ -840,6 +863,11 @@ void Cvar_List_f( void ) { } else { Com_Printf(" "); } + if (var->flags & CVAR_SYSTEMINFO) { + Com_Printf("s"); + } else { + Com_Printf(" "); + } if (var->flags & CVAR_USERINFO) { Com_Printf("U"); } else { @@ -870,6 +898,11 @@ void Cvar_List_f( void ) { } else { Com_Printf(" "); } + if (var->flags & CVAR_USER_CREATED) { + Com_Printf("?"); + } else { + Com_Printf(" "); + } Com_Printf (" %s \"%s\"\n", var->name, var->string); } diff --git a/src/qcommon/files.c b/src/qcommon/files.c index 6b407c20..db7e79d4 100644 --- a/src/qcommon/files.c +++ b/src/qcommon/files.c @@ -479,7 +479,7 @@ FS_CreatePath Creates any directories needed to store the given filename ============ */ -static qboolean FS_CreatePath (char *OSPath) { +qboolean FS_CreatePath (char *OSPath) { char *ofs; // make absolutely sure that it can't back up the path @@ -502,18 +502,19 @@ static qboolean FS_CreatePath (char *OSPath) { /* ================= -FS_FilenameIsExecutable +FS_CheckFilenameIsNotExecutable ERR_FATAL if trying to maniuplate a file with the platform library extension ================= */ -static void FS_FilenameIsExecutable( const char *filename, const char *function ) +static void FS_CheckFilenameIsNotExecutable( const char *filename, + const char *function ) { // Check if the filename ends with the library extension - if( !Q_stricmp( filename + strlen( filename ) - strlen( DLL_EXT ), DLL_EXT ) ) + if( !Q_stricmp( COM_GetExtension( filename ), DLL_EXT ) ) { - Com_Error( ERR_FATAL, "%s: Not allowed to write '%s' due to %s extension\n", - function, filename, DLL_EXT ); + Com_Error( ERR_FATAL, "%s: Not allowed to manipulate '%s' due " + "to %s extension\n", function, filename, DLL_EXT ); } } @@ -532,7 +533,7 @@ static void FS_CopyFile( char *fromOSPath, char *toOSPath ) { Com_Printf( "copy %s to %s\n", fromOSPath, toOSPath ); - FS_FilenameIsExecutable( toOSPath, __func__ ); + FS_CheckFilenameIsNotExecutable( toOSPath, __func__ ); if (strstr(fromOSPath, "journal.dat") || strstr(fromOSPath, "journaldata.dat")) { Com_Printf( "Ignoring journal files\n"); @@ -575,7 +576,7 @@ FS_Remove =========== */ void FS_Remove( const char *osPath ) { - FS_FilenameIsExecutable( osPath, __func__ ); + FS_CheckFilenameIsNotExecutable( osPath, __func__ ); remove( osPath ); } @@ -587,7 +588,7 @@ FS_HomeRemove =========== */ void FS_HomeRemove( const char *homePath ) { - FS_FilenameIsExecutable( homePath, __func__ ); + FS_CheckFilenameIsNotExecutable( homePath, __func__ ); remove( FS_BuildOSPath( fs_homepath->string, fs_gamedir, homePath ) ); @@ -666,7 +667,7 @@ fileHandle_t FS_SV_FOpenFileWrite( const char *filename ) { Com_Printf( "FS_SV_FOpenFileWrite: %s\n", ospath ); } - FS_FilenameIsExecutable( ospath, __func__ ); + FS_CheckFilenameIsNotExecutable( ospath, __func__ ); if( FS_CreatePath( ospath ) ) { return 0; @@ -777,7 +778,7 @@ void FS_SV_Rename( const char *from, const char *to ) { Com_Printf( "FS_SV_Rename: %s --> %s\n", from_ospath, to_ospath ); } - FS_FilenameIsExecutable( to_ospath, __func__ ); + FS_CheckFilenameIsNotExecutable( to_ospath, __func__ ); if (rename( from_ospath, to_ospath )) { // Failed, try copying it and deleting the original @@ -811,7 +812,7 @@ void FS_Rename( const char *from, const char *to ) { Com_Printf( "FS_Rename: %s --> %s\n", from_ospath, to_ospath ); } - FS_FilenameIsExecutable( to_ospath, __func__ ); + FS_CheckFilenameIsNotExecutable( to_ospath, __func__ ); if (rename( from_ospath, to_ospath )) { // Failed, try copying it and deleting the original @@ -874,7 +875,7 @@ fileHandle_t FS_FOpenFileWrite( const char *filename ) { Com_Printf( "FS_FOpenFileWrite: %s\n", ospath ); } - FS_FilenameIsExecutable( ospath, __func__ ); + FS_CheckFilenameIsNotExecutable( ospath, __func__ ); if( FS_CreatePath( ospath ) ) { return 0; @@ -922,7 +923,7 @@ fileHandle_t FS_FOpenFileAppend( const char *filename ) { Com_Printf( "FS_FOpenFileAppend: %s\n", ospath ); } - FS_FilenameIsExecutable( ospath, __func__ ); + FS_CheckFilenameIsNotExecutable( ospath, __func__ ); if( FS_CreatePath( ospath ) ) { return 0; diff --git a/src/qcommon/net_ip.c b/src/qcommon/net_ip.c index e7a52996..9ad4a821 100644 --- a/src/qcommon/net_ip.c +++ b/src/qcommon/net_ip.c @@ -92,13 +92,6 @@ typedef int SOCKET; static qboolean usingSocks = qfalse; static int networkingEnabled = 0; -#define NET_ENABLEV4 0x01 -#define NET_ENABLEV6 0x02 -// if this flag is set, always attempt ipv6 connections instead of ipv4 if a v6 address is found. -#define NET_PRIOV6 0x04 -// disables ipv6 multicast support if set. -#define NET_DISABLEMCAST 0x08 - static cvar_t *net_enabled; static cvar_t *net_socksEnabled; @@ -274,7 +267,9 @@ Sys_StringToSockaddr */ static qboolean Sys_StringToSockaddr(const char *s, struct sockaddr *sadr, int sadr_len, sa_family_t family) { - struct addrinfo hints, *res = NULL, *search; + struct addrinfo hints; + struct addrinfo *res = NULL; + struct addrinfo *search = NULL; struct addrinfo *hintsp; int retval; @@ -284,8 +279,6 @@ static qboolean Sys_StringToSockaddr(const char *s, struct sockaddr *sadr, int s hintsp = &hints; hintsp->ai_family = family; hintsp->ai_socktype = SOCK_DGRAM; - // FIXME: we should set "->ai_flags" to AI_PASSIVE if we intend - // to use this structure for a bind() - instead of a sendto() retval = getaddrinfo(s, NULL, hintsp, &res); @@ -294,18 +287,20 @@ static qboolean Sys_StringToSockaddr(const char *s, struct sockaddr *sadr, int s if(family == AF_UNSPEC) { // Decide here and now which protocol family to use - if((net_enabled->integer & NET_ENABLEV6) && (net_enabled->integer & NET_PRIOV6)) - search = SearchAddrInfo(res, AF_INET6); + if(net_enabled->integer & NET_PRIOV6) + { + if(net_enabled->integer & NET_ENABLEV6) + search = SearchAddrInfo(res, AF_INET6); + + if(!search && (net_enabled->integer & NET_ENABLEV4)) + search = SearchAddrInfo(res, AF_INET); + } else - search = SearchAddrInfo(res, AF_INET); - - if(!search) { - if((net_enabled->integer & NET_ENABLEV6) && - (net_enabled->integer & NET_PRIOV6) && - (net_enabled->integer & NET_ENABLEV4)) + if(net_enabled->integer & NET_ENABLEV4) search = SearchAddrInfo(res, AF_INET); - else if(net_enabled->integer & NET_ENABLEV6) + + if(!search && (net_enabled->integer & NET_ENABLEV6)) search = SearchAddrInfo(res, AF_INET6); } } @@ -348,7 +343,8 @@ static void Sys_SockaddrToString(char *dest, int destlen, struct sockaddr *input else inputlen = sizeof(struct sockaddr_in); - getnameinfo(input, inputlen, dest, destlen, NULL, 0, NI_NUMERICHOST); + if(getnameinfo(input, inputlen, dest, destlen, NULL, 0, NI_NUMERICHOST) && destlen > 0) + *dest = '\0'; } /* @@ -382,47 +378,96 @@ qboolean Sys_StringToAdr( const char *s, netadr_t *a, netadrtype_t family ) { /* =================== -NET_CompareBaseAdr +NET_CompareBaseAdrMask -Compares without the port +Compare without port, and up to the bit number given in netmask. =================== */ -qboolean NET_CompareBaseAdr (netadr_t a, netadr_t b) +qboolean NET_CompareBaseAdrMask(netadr_t a, netadr_t b, int netmask) { + qboolean differed; + byte cmpmask, *addra, *addrb; + int curbyte; + if (a.type != b.type) return qfalse; if (a.type == NA_LOOPBACK) return qtrue; - if (a.type == NA_IP) + if(a.type == NA_IP) { - if(!memcmp(a.ip, b.ip, sizeof(a.ip))) - return qtrue; + addra = (byte *) &a.ip; + addrb = (byte *) &b.ip; - return qfalse; + if(netmask < 0 || netmask > 32) + netmask = 32; } - - if (a.type == NA_IP6) + else if(a.type == NA_IP6) { - if(!memcmp(a.ip6, b.ip6, sizeof(a.ip6)) && a.scope_id == b.scope_id) - return qtrue; + addra = (byte *) &a.ip6; + addrb = (byte *) &b.ip6; + if(netmask < 0 || netmask > 128) + netmask = 128; + } + else + { + Com_Printf ("NET_CompareBaseAdr: bad address type\n"); return qfalse; } - Com_Printf ("NET_CompareBaseAdr: bad address type\n"); + differed = qfalse; + curbyte = 0; + + while(netmask > 7) + { + if(addra[curbyte] != addrb[curbyte]) + { + differed = qtrue; + break; + } + + curbyte++; + netmask -= 8; + } + + if(differed) + return qfalse; + + if(netmask) + { + cmpmask = (1 << netmask) - 1; + cmpmask <<= 8 - netmask; + + if((addra[curbyte] & cmpmask) == (addrb[curbyte] & cmpmask)) + return qtrue; + } + else + return qtrue; + return qfalse; } + +/* +=================== +NET_CompareBaseAdr + +Compares without the port +=================== +*/ +qboolean NET_CompareBaseAdr (netadr_t a, netadr_t b) +{ + return NET_CompareBaseAdrMask(a, b, -1); +} + const char *NET_AdrToString (netadr_t a) { static char s[NET_ADDRSTRMAXLEN]; if (a.type == NA_LOOPBACK) - { Com_sprintf (s, sizeof(s), "loopback"); - } else if (a.type == NA_IP || a.type == NA_IP6) { struct sockaddr_storage sadr; @@ -440,16 +485,11 @@ const char *NET_AdrToStringwPort (netadr_t a) static char s[NET_ADDRSTRMAXLEN]; if (a.type == NA_LOOPBACK) - { Com_sprintf (s, sizeof(s), "loopback"); - } - else if (a.type == NA_IP || a.type == NA_IP6) - { - if(a.type == NA_IP) - Com_sprintf(s, sizeof(s), "%s:%hu", NET_AdrToString(a), ntohs(a.port)); - else if(a.type == NA_IP6) - Com_sprintf(s, sizeof(s), "[%s]:%hu", NET_AdrToString(a), ntohs(a.port)); - } + else if(a.type == NA_IP) + Com_sprintf(s, sizeof(s), "%s:%hu", NET_AdrToString(a), ntohs(a.port)); + else if(a.type == NA_IP6) + Com_sprintf(s, sizeof(s), "[%s]:%hu", NET_AdrToString(a), ntohs(a.port)); return s; } @@ -886,7 +926,7 @@ int NET_IP6Socket( char *net_interface, int port, struct sockaddr_in6 *bindto, i #ifdef IPV6_V6ONLY { - int i; + int i = 1; // ipv4 addresses should not be allowed to connect via this socket. if(setsockopt(newsocket, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &i, sizeof(i)) == SOCKET_ERROR) @@ -1278,9 +1318,6 @@ void NET_GetLocalAddress( void ) { char hostname[256]; struct addrinfo hint; struct addrinfo *res = NULL; - struct addrinfo *search; - struct sockaddr_in mask4; - struct sockaddr_in6 mask6; if(gethostname( hostname, 256 ) == SOCKET_ERROR) return; @@ -1292,29 +1329,36 @@ void NET_GetLocalAddress( void ) { hint.ai_family = AF_UNSPEC; hint.ai_socktype = SOCK_DGRAM; - if(getaddrinfo(hostname, NULL, &hint, &res)) - return; - - /* On operating systems where it's more difficult to find out the configured interfaces, we'll just assume a - * netmask with all bits set. */ - - memset(&mask4, 0, sizeof(mask4)); - memset(&mask6, 0, sizeof(mask6)); - mask4.sin_family = AF_INET; - memset(&mask4.sin_addr.s_addr, 0xFF, sizeof(mask4.sin_addr.s_addr)); - mask6.sin6_family = AF_INET6; - memset(&mask6.sin6_addr, 0xFF, sizeof(mask6.sin6_addr)); - - // add all IPs from returned list. - for(search = res; search; search = search->ai_next) + if(!getaddrinfo(hostname, NULL, &hint, &res)) { - if(search->ai_family == AF_INET) - NET_AddLocalAddress("", search->ai_addr, (struct sockaddr *) &mask4); - else if(search->ai_family == AF_INET6) - NET_AddLocalAddress("", search->ai_addr, (struct sockaddr *) &mask6); + struct sockaddr_in mask4; + struct sockaddr_in6 mask6; + struct addrinfo *search; + + /* On operating systems where it's more difficult to find out the configured interfaces, we'll just assume a + * netmask with all bits set. */ + + memset(&mask4, 0, sizeof(mask4)); + memset(&mask6, 0, sizeof(mask6)); + mask4.sin_family = AF_INET; + memset(&mask4.sin_addr.s_addr, 0xFF, sizeof(mask4.sin_addr.s_addr)); + mask6.sin6_family = AF_INET6; + memset(&mask6.sin6_addr, 0xFF, sizeof(mask6.sin6_addr)); + + // add all IPs from returned list. + for(search = res; search; search = search->ai_next) + { + if(search->ai_family == AF_INET) + NET_AddLocalAddress("", search->ai_addr, (struct sockaddr *) &mask4); + else if(search->ai_family == AF_INET6) + NET_AddLocalAddress("", search->ai_addr, (struct sockaddr *) &mask6); + } + + Sys_ShowIP(); } - Sys_ShowIP(); + if(res) + freeaddrinfo(res); } #endif @@ -1329,11 +1373,6 @@ void NET_OpenIP( void ) { int port; int port6; - net_ip = Cvar_Get( "net_ip", "0.0.0.0", CVAR_LATCH ); - net_ip6 = Cvar_Get( "net_ip6", "::", CVAR_LATCH ); - net_port = Cvar_Get( "net_port", va( "%i", PORT_SERVER ), CVAR_LATCH ); - net_port6 = Cvar_Get( "net_port6", va( "%i", PORT_SERVER ), CVAR_LATCH ); - port = net_port->integer; port6 = net_port6->integer; @@ -1397,14 +1436,8 @@ NET_GetCvars ==================== */ static qboolean NET_GetCvars( void ) { - qboolean modified; - - modified = qfalse; + int modified; - if( net_enabled && net_enabled->modified ) { - modified = qtrue; - } - #ifdef DEDICATED // I want server owners to explicitly turn on ipv6 support. net_enabled = Cvar_Get( "net_enabled", "1", CVAR_LATCH | CVAR_ARCHIVE ); @@ -1413,45 +1446,55 @@ static qboolean NET_GetCvars( void ) { * used if available due to ping */ net_enabled = Cvar_Get( "net_enabled", "3", CVAR_LATCH | CVAR_ARCHIVE ); #endif + modified = net_enabled->modified; + net_enabled->modified = qfalse; + + net_ip = Cvar_Get( "net_ip", "0.0.0.0", CVAR_LATCH ); + modified += net_ip->modified; + net_ip->modified = qfalse; + + net_ip6 = Cvar_Get( "net_ip6", "::", CVAR_LATCH ); + modified += net_ip6->modified; + net_ip6->modified = qfalse; + + net_port = Cvar_Get( "net_port", va( "%i", PORT_SERVER ), CVAR_LATCH ); + modified += net_port->modified; + net_port->modified = qfalse; + + net_port6 = Cvar_Get( "net_port6", va( "%i", PORT_SERVER ), CVAR_LATCH ); + modified += net_port6->modified; + net_port6->modified = qfalse; // Some cvars for configuring multicast options which facilitates scanning for servers on local subnets. - if( net_mcast6addr && net_mcast6addr->modified ) { - modified = qtrue; - } net_mcast6addr = Cvar_Get( "net_mcast6addr", NET_MULTICAST_IP6, CVAR_LATCH | CVAR_ARCHIVE ); + modified += net_mcast6addr->modified; + net_mcast6addr->modified = qfalse; - if( net_mcast6iface && net_mcast6iface->modified ) { - modified = qtrue; - } net_mcast6iface = Cvar_Get( "net_mcast6iface", "0", CVAR_LATCH | CVAR_ARCHIVE ); + modified += net_mcast6iface->modified; + net_mcast6iface->modified = qfalse; - if( net_socksEnabled && net_socksEnabled->modified ) { - modified = qtrue; - } net_socksEnabled = Cvar_Get( "net_socksEnabled", "0", CVAR_LATCH | CVAR_ARCHIVE ); + modified += net_socksEnabled->modified; + net_socksEnabled->modified = qfalse; - if( net_socksServer && net_socksServer->modified ) { - modified = qtrue; - } net_socksServer = Cvar_Get( "net_socksServer", "", CVAR_LATCH | CVAR_ARCHIVE ); + modified += net_socksServer->modified; + net_socksServer->modified = qfalse; - if( net_socksPort && net_socksPort->modified ) { - modified = qtrue; - } net_socksPort = Cvar_Get( "net_socksPort", "1080", CVAR_LATCH | CVAR_ARCHIVE ); + modified += net_socksPort->modified; + net_socksPort->modified = qfalse; - if( net_socksUsername && net_socksUsername->modified ) { - modified = qtrue; - } net_socksUsername = Cvar_Get( "net_socksUsername", "", CVAR_LATCH | CVAR_ARCHIVE ); + modified += net_socksUsername->modified; + net_socksUsername->modified = qfalse; - if( net_socksPassword && net_socksPassword->modified ) { - modified = qtrue; - } net_socksPassword = Cvar_Get( "net_socksPassword", "", CVAR_LATCH | CVAR_ARCHIVE ); + modified += net_socksPassword->modified; + net_socksPassword->modified = qfalse; - - return modified; + return modified ? qtrue : qfalse; } @@ -1555,10 +1598,9 @@ void NET_Init( void ) { Com_Printf( "Winsock Initialized\n" ); #endif - // this is really just to get the cvars registered - NET_GetCvars(); - NET_Config( qtrue ); + + Cmd_AddCommand ("net_restart", NET_Restart_f); } @@ -1608,8 +1650,7 @@ void NET_Sleep( int msec ) { { FD_SET(ip_socket, &fdset); - if(ip_socket > highestfd) - highestfd = ip_socket; + highestfd = ip_socket; } if(ip6_socket != INVALID_SOCKET) { @@ -1621,7 +1662,7 @@ void NET_Sleep( int msec ) { timeout.tv_sec = msec/1000; timeout.tv_usec = (msec%1000)*1000; - select(ip_socket+1, &fdset, NULL, NULL, &timeout); + select(highestfd + 1, &fdset, NULL, NULL, &timeout); } @@ -1630,6 +1671,6 @@ void NET_Sleep( int msec ) { NET_Restart_f ==================== */ -void NET_Restart( void ) { +void NET_Restart_f( void ) { NET_Config( networkingEnabled ); } diff --git a/src/qcommon/q_math.c b/src/qcommon/q_math.c index e7924073..ba910a37 100644 --- a/src/qcommon/q_math.c +++ b/src/qcommon/q_math.c @@ -696,50 +696,14 @@ void SetPlaneSignbits (cplane_t *out) { BoxOnPlaneSide Returns 1, 2, or 1 + 2 - -// this is the slow, general version -int BoxOnPlaneSide2 (vec3_t emins, vec3_t emaxs, struct cplane_s *p) -{ - int i; - float dist1, dist2; - int sides; - vec3_t corners[2]; - - for (i=0 ; i<3 ; i++) - { - if (p->normal[i] < 0) - { - corners[0][i] = emins[i]; - corners[1][i] = emaxs[i]; - } - else - { - corners[1][i] = emins[i]; - corners[0][i] = emaxs[i]; - } - } - dist1 = DotProduct (p->normal, corners[0]) - p->dist; - dist2 = DotProduct (p->normal, corners[1]) - p->dist; - sides = 0; - if (dist1 >= 0) - sides = 1; - if (dist2 < 0) - sides |= 2; - - return sides; -} - ================== */ - -#if !id386 - -int BoxOnPlaneSide (vec3_t emins, vec3_t emaxs, struct cplane_s *p) +int BoxOnPlaneSide(vec3_t emins, vec3_t emaxs, struct cplane_s *p) { - float dist1, dist2; - int sides; + float dist[2]; + int sides, b, i; -// fast axial cases + // fast axial cases if (p->type < 3) { if (p->dist <= emins[p->type]) @@ -749,291 +713,27 @@ int BoxOnPlaneSide (vec3_t emins, vec3_t emaxs, struct cplane_s *p) return 3; } -// general case - switch (p->signbits) + // general case + dist[0] = dist[1] = 0; + if (p->signbits < 8) // >= 8: default case is original code (dist[0]=dist[1]=0) { - case 0: - dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; - dist2 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; - break; - case 1: - dist1 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; - dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; - break; - case 2: - dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; - dist2 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; - break; - case 3: - dist1 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; - dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; - break; - case 4: - dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; - dist2 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; - break; - case 5: - dist1 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; - dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; - break; - case 6: - dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; - dist2 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; - break; - case 7: - dist1 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; - dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; - break; - default: - dist1 = dist2 = 0; // shut up compiler - break; + for (i=0 ; i<3 ; i++) + { + b = (p->signbits >> i) & 1; + dist[ b] += p->normal[i]*emaxs[i]; + dist[!b] += p->normal[i]*emins[i]; + } } sides = 0; - if (dist1 >= p->dist) + if (dist[0] >= p->dist) sides = 1; - if (dist2 < p->dist) + if (dist[1] < p->dist) sides |= 2; return sides; } -#elif __GNUC__ -// use matha.s -#else -#pragma warning( disable: 4035 ) - -__declspec( naked ) int BoxOnPlaneSide (vec3_t emins, vec3_t emaxs, struct cplane_s *p) -{ - static int bops_initialized; - static int Ljmptab[8]; - - __asm { - - push ebx - - cmp bops_initialized, 1 - je initialized - mov bops_initialized, 1 - - mov Ljmptab[0*4], offset Lcase0 - mov Ljmptab[1*4], offset Lcase1 - mov Ljmptab[2*4], offset Lcase2 - mov Ljmptab[3*4], offset Lcase3 - mov Ljmptab[4*4], offset Lcase4 - mov Ljmptab[5*4], offset Lcase5 - mov Ljmptab[6*4], offset Lcase6 - mov Ljmptab[7*4], offset Lcase7 - -initialized: - - mov edx,dword ptr[4+12+esp] - mov ecx,dword ptr[4+4+esp] - xor eax,eax - mov ebx,dword ptr[4+8+esp] - mov al,byte ptr[17+edx] - cmp al,8 - jge Lerror - fld dword ptr[0+edx] - fld st(0) - jmp dword ptr[Ljmptab+eax*4] -Lcase0: - fmul dword ptr[ebx] - fld dword ptr[0+4+edx] - fxch st(2) - fmul dword ptr[ecx] - fxch st(2) - fld st(0) - fmul dword ptr[4+ebx] - fld dword ptr[0+8+edx] - fxch st(2) - fmul dword ptr[4+ecx] - fxch st(2) - fld st(0) - fmul dword ptr[8+ebx] - fxch st(5) - faddp st(3),st(0) - fmul dword ptr[8+ecx] - fxch st(1) - faddp st(3),st(0) - fxch st(3) - faddp st(2),st(0) - jmp LSetSides -Lcase1: - fmul dword ptr[ecx] - fld dword ptr[0+4+edx] - fxch st(2) - fmul dword ptr[ebx] - fxch st(2) - fld st(0) - fmul dword ptr[4+ebx] - fld dword ptr[0+8+edx] - fxch st(2) - fmul dword ptr[4+ecx] - fxch st(2) - fld st(0) - fmul dword ptr[8+ebx] - fxch st(5) - faddp st(3),st(0) - fmul dword ptr[8+ecx] - fxch st(1) - faddp st(3),st(0) - fxch st(3) - faddp st(2),st(0) - jmp LSetSides -Lcase2: - fmul dword ptr[ebx] - fld dword ptr[0+4+edx] - fxch st(2) - fmul dword ptr[ecx] - fxch st(2) - fld st(0) - fmul dword ptr[4+ecx] - fld dword ptr[0+8+edx] - fxch st(2) - fmul dword ptr[4+ebx] - fxch st(2) - fld st(0) - fmul dword ptr[8+ebx] - fxch st(5) - faddp st(3),st(0) - fmul dword ptr[8+ecx] - fxch st(1) - faddp st(3),st(0) - fxch st(3) - faddp st(2),st(0) - jmp LSetSides -Lcase3: - fmul dword ptr[ecx] - fld dword ptr[0+4+edx] - fxch st(2) - fmul dword ptr[ebx] - fxch st(2) - fld st(0) - fmul dword ptr[4+ecx] - fld dword ptr[0+8+edx] - fxch st(2) - fmul dword ptr[4+ebx] - fxch st(2) - fld st(0) - fmul dword ptr[8+ebx] - fxch st(5) - faddp st(3),st(0) - fmul dword ptr[8+ecx] - fxch st(1) - faddp st(3),st(0) - fxch st(3) - faddp st(2),st(0) - jmp LSetSides -Lcase4: - fmul dword ptr[ebx] - fld dword ptr[0+4+edx] - fxch st(2) - fmul dword ptr[ecx] - fxch st(2) - fld st(0) - fmul dword ptr[4+ebx] - fld dword ptr[0+8+edx] - fxch st(2) - fmul dword ptr[4+ecx] - fxch st(2) - fld st(0) - fmul dword ptr[8+ecx] - fxch st(5) - faddp st(3),st(0) - fmul dword ptr[8+ebx] - fxch st(1) - faddp st(3),st(0) - fxch st(3) - faddp st(2),st(0) - jmp LSetSides -Lcase5: - fmul dword ptr[ecx] - fld dword ptr[0+4+edx] - fxch st(2) - fmul dword ptr[ebx] - fxch st(2) - fld st(0) - fmul dword ptr[4+ebx] - fld dword ptr[0+8+edx] - fxch st(2) - fmul dword ptr[4+ecx] - fxch st(2) - fld st(0) - fmul dword ptr[8+ecx] - fxch st(5) - faddp st(3),st(0) - fmul dword ptr[8+ebx] - fxch st(1) - faddp st(3),st(0) - fxch st(3) - faddp st(2),st(0) - jmp LSetSides -Lcase6: - fmul dword ptr[ebx] - fld dword ptr[0+4+edx] - fxch st(2) - fmul dword ptr[ecx] - fxch st(2) - fld st(0) - fmul dword ptr[4+ecx] - fld dword ptr[0+8+edx] - fxch st(2) - fmul dword ptr[4+ebx] - fxch st(2) - fld st(0) - fmul dword ptr[8+ecx] - fxch st(5) - faddp st(3),st(0) - fmul dword ptr[8+ebx] - fxch st(1) - faddp st(3),st(0) - fxch st(3) - faddp st(2),st(0) - jmp LSetSides -Lcase7: - fmul dword ptr[ecx] - fld dword ptr[0+4+edx] - fxch st(2) - fmul dword ptr[ebx] - fxch st(2) - fld st(0) - fmul dword ptr[4+ecx] - fld dword ptr[0+8+edx] - fxch st(2) - fmul dword ptr[4+ebx] - fxch st(2) - fld st(0) - fmul dword ptr[8+ecx] - fxch st(5) - faddp st(3),st(0) - fmul dword ptr[8+ebx] - fxch st(1) - faddp st(3),st(0) - fxch st(3) - faddp st(2),st(0) -LSetSides: - faddp st(2),st(0) - fcomp dword ptr[12+edx] - xor ecx,ecx - fnstsw ax - fcomp dword ptr[12+edx] - and ah,1 - xor ah,1 - add cl,ah - fnstsw ax - and ah,1 - add ah,ah - add cl,ah - pop ebx - mov eax,ecx - ret -Lerror: - int 3 - } -} -#pragma warning( default: 4035 ) -#endif /* ================= diff --git a/src/qcommon/q_platform.h b/src/qcommon/q_platform.h index 4ce1fb56..5da62b38 100644 --- a/src/qcommon/q_platform.h +++ b/src/qcommon/q_platform.h @@ -30,6 +30,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #define id386 0 #define idppc 0 #define idppc_altivec 0 +#define idsparc 0 #else @@ -59,6 +60,12 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #define idppc_altivec 0 #endif +#if defined(__sparc__) && !defined(C_ONLY) +#define idsparc 1 +#else +#define idsparc 0 +#endif + #endif #ifndef __ASM_I386__ // don't include the C bits if included from qasm.h @@ -193,6 +200,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #ifdef __i386__ #define ARCH_STRING "x86" +#elif defined __amd64__ +#define ARCH_STRING "x86_64" #elif defined __axp__ #define ARCH_STRING "alpha" #endif diff --git a/src/qcommon/q_shared.h b/src/qcommon/q_shared.h index 622d71fe..a346bf5e 100644 --- a/src/qcommon/q_shared.h +++ b/src/qcommon/q_shared.h @@ -74,6 +74,16 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #endif #endif +#if (defined _MSC_VER) +#define Q_EXPORT __declspec(dllexport) +#elif (defined __SUNPRO_C) +#define Q_EXPORT __global +#elif ((__GNUC__ >= 3) && (!__EMX__) && (!sun)) +#define Q_EXPORT __attribute__((visibility("default"))) +#else +#define Q_EXPORT +#endif + /********************************************************************** VM Considerations @@ -167,6 +177,10 @@ typedef int clipHandle_t; #define NULL ((void *)0) #endif +#define STRING(s) #s +// expand constants before stringifying them +#define XSTRING(s) STRING(s) + #define MAX_QINT 0x7fffffff #define MIN_QINT (-MAX_QINT-1) diff --git a/src/qcommon/qcommon.h b/src/qcommon/qcommon.h index e4234f54..bcfd60b9 100644 --- a/src/qcommon/qcommon.h +++ b/src/qcommon/qcommon.h @@ -120,6 +120,14 @@ NET ============================================================== */ +#define NET_ENABLEV4 0x01 +#define NET_ENABLEV6 0x02 +// if this flag is set, always attempt ipv6 connections instead of ipv4 if a v6 address is found. +#define NET_PRIOV6 0x04 +// disables ipv6 multicast support if set. +#define NET_DISABLEMCAST 0x08 + + #define PACKET_BACKUP 32 // number of old messages that must be kept on client and // server for delta comrpession and ping estimation #define PACKET_MASK (PACKET_BACKUP-1) @@ -131,7 +139,7 @@ NET #define MAX_RELIABLE_COMMANDS 128 // max string commands buffered for restransmit typedef enum { - NA_BAD, // an address lookup failed + NA_BAD = 0, // an address lookup failed NA_LOOPBACK, NA_BROADCAST, NA_IP, @@ -158,7 +166,7 @@ typedef struct { void NET_Init( void ); void NET_Shutdown( void ); -void NET_Restart( void ); +void NET_Restart_f( void ); void NET_Config( qboolean enableNetworking ); void NET_FlushPacketQueue(void); void NET_SendPacket (netsrc_t sock, int length, const void *data, netadr_t to); @@ -166,6 +174,7 @@ void QDECL NET_OutOfBandPrint( netsrc_t net_socket, netadr_t adr, const char *f void QDECL NET_OutOfBandData( netsrc_t sock, netadr_t adr, byte *format, int len ); qboolean NET_CompareAdr (netadr_t a, netadr_t b); +qboolean NET_CompareBaseAdrMask(netadr_t a, netadr_t b, int netmask); qboolean NET_CompareBaseAdr (netadr_t a, netadr_t b); qboolean NET_IsLocalAddress (netadr_t adr); const char *NET_AdrToString (netadr_t a); @@ -584,6 +593,7 @@ void FS_FreeFileList( char **list ); qboolean FS_FileExists( const char *file ); +qboolean FS_CreatePath (char *OSPath); char *FS_BuildOSPath( const char *base, const char *game, const char *qpath ); int FS_LoadStack( void ); diff --git a/src/qcommon/vm.c b/src/qcommon/vm.c index c1a829a6..713256a7 100644 --- a/src/qcommon/vm.c +++ b/src/qcommon/vm.c @@ -357,6 +357,9 @@ intptr_t QDECL VM_DllSyscall( intptr_t arg, ... ) { #endif } + +#define STACK_SIZE 0x20000 + /* ================= VM_LoadQVM @@ -424,7 +427,8 @@ vmHeader_t *VM_LoadQVM( vm_t *vm, qboolean alloc ) { // round up to next power of 2 so all data operations can // be mask protected - dataLength = header.h->dataLength + header.h->litLength + header.h->bssLength; + dataLength = header.h->dataLength + header.h->litLength + + header.h->bssLength + STACK_SIZE; for ( i = 0 ; dataLength > ( 1 << i ) ; i++ ) { } dataLength = 1 << i; @@ -516,9 +520,6 @@ If image ends in .qvm it will be interpreted, otherwise it will attempt to load as a system dll ================ */ - -#define STACK_SIZE 0x20000 - vm_t *VM_Create( const char *module, intptr_t (*systemCalls)(intptr_t *), vmInterpret_t interpret ) { vm_t *vm; @@ -766,7 +767,7 @@ intptr_t QDECL VM_Call( vm_t *vm, int callnum, ... ) { args[4], args[5], args[6], args[7], args[8], args[9]); } else { -#if id386 // i386 calling convention doesn't need conversion +#if id386 || idsparc // i386/sparc calling convention doesn't need conversion #ifndef NO_VM_COMPILED if ( vm->compiled ) r = VM_CallCompiled( vm, (int*)&callnum ); diff --git a/src/qcommon/vm_sparc.c b/src/qcommon/vm_sparc.c new file mode 100644 index 00000000..39f2202a --- /dev/null +++ b/src/qcommon/vm_sparc.c @@ -0,0 +1,1648 @@ +/* +=========================================================================== +Copyright (C) 2009 David S. Miller <davem@davemloft.net> + +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 +=========================================================================== +*/ + +/* This code is based almost entirely upon the vm_powerpc.c code by + * Przemyslaw Iskra. All I did was make it work on Sparc :-) -DaveM + */ + +#include <sys/types.h> +#include <sys/mman.h> +#include <sys/time.h> +#include <time.h> +#include <stddef.h> + +#include "vm_local.h" +#include "vm_sparc.h" + +/* exit() won't be called but use it because it is marked with noreturn */ +#define DIE( reason ) \ + do { \ + Com_Error(ERR_DROP, "vm_sparc compiler error: " reason "\n"); \ + exit(1); \ + } while(0) + +/* Select Length - first value on 32 bits, second on 64 */ +#ifdef __arch64__ +#define SL(a, b) (b) +#else +#define SL(a, b) (a) +#endif + +#define rTMP G1 +#define rVMDATA G2 +#define rPSTACK G3 +#define rDATABASE G4 +#define rDATAMASK G5 + +struct sparc_opcode { + const char *name; + unsigned int opcode; + unsigned int mask; + unsigned char args[4]; +#define ARG_NONE 0 +#define ARG_RS1 1 +#define ARG_RS2 2 +#define ARG_RD 3 +#define ARG_SIMM13 4 +#define ARG_DISP30 5 +#define ARG_IMM22 6 +#define ARG_DISP22 7 +#define ARG_SWTRAP 8 +}; + +#define ARG_RS1_RS2_RD { ARG_RS1, ARG_RS2, ARG_RD } +#define ARG_RS1_SIMM13_RD { ARG_RS1, ARG_SIMM13, ARG_RD } +#define ARG_RS1_RS2 { ARG_RS1, ARG_RS2 } +#define ARG_RS2_RD { ARG_RS2, ARG_RD } + +#define OP_MASK 0xc0000000 +#define OP2_MASK 0x01c00000 +#define OP3_MASK 0x01f80000 +#define OPF_MASK 0x00003fe0 + +#define IMM 0x00002000 + +#define FMT1(op) ((op) << 30), OP_MASK +#define FMT2(op,op2) ((op) << 30)|((op2)<<22), (OP_MASK | OP2_MASK) +#define FMT3(op,op3) ((op) << 30)|((op3)<<19), (OP_MASK | OP3_MASK | IMM) +#define FMT3I(op,op3) ((op) << 30)|((op3)<<19)|IMM, (OP_MASK | OP3_MASK | IMM) +#define FMT3F(op,op3,opf) ((op) << 30)|((op3)<<19)|((opf)<<5), \ + (OP_MASK | OP3_MASK | OPF_MASK) + +#define BICC(A,COND) FMT2(0,((A<<7)|(COND<<3)|0x2)) +#define BFCC(A,COND) FMT2(0,((A<<7)|(COND<<3)|0x6)) +#define TICC(COND) FMT3I(0,((COND<<6)|0x3a)) + +enum sparc_iname { + CALL, NOP, SETHI, + + BA, BN, BNE, BE, BG, BLE, BGE, BL, BGU, BLEU, BCC, BCS, + BPOS, BNEG, BVC, BVS, + + ADDI, ADD, + ANDI, AND, + ORI, OR, + XORI, XOR, + SUBI, SUB, + ANDNI, ANDN, + ORNI, ORN, + XNORI, XNOR, + + UMULI, UMUL, + SMULI, SMUL, + UDIVI, UDIV, + SDIVI, SDIV, + + SUBCCI, SUBCC, + + SLLI, SLL, + SRLI, SRL, + SRAI, SRA, + + WRI, WR, + + SAVEI, SAVE, + RESTOREI, RESTORE, + + TA, + + JMPLI, JMPL, + + LDXI, LDX, + LDUWI, LDUW, + LDUHI, LDUH, + LDUBI, LDUB, + + STXI, STX, + STWI, STW, + STHI, STH, + STBI, STB, + + LDFI, LDF, + STFI, STF, + + FADD, FSUB, FCMP, FSTOI, FITOS, FNEG, FDIV, FMUL, + FBE, FBNE, FBL, FBGE, FBG, FBLE, +}; + +#define LDLI SL(LDUWI, LDXI) +#define LDL SL(LDUW, LDX) +#define STLI SL(STWI, STXI) +#define STL SL(STW, STX) + +#define SPARC_NOP 0x01000000 + +static const struct sparc_opcode sparc_opcodes[] = { + { "call", FMT1(1), { ARG_DISP30 }, }, + { "nop", SPARC_NOP, 0xffffffff, { ARG_NONE }, }, /* sethi %hi(0), %g0 */ + { "sethi", FMT2(0,4), { ARG_IMM22, ARG_RD }, }, + { "ba", BICC(0,8), { ARG_DISP22 }, }, + { "bn", BICC(0,0), { ARG_DISP22 }, }, + { "bne", BICC(0,9), { ARG_DISP22 }, }, + { "be", BICC(0,1), { ARG_DISP22 }, }, + { "bg", BICC(0,10), { ARG_DISP22 }, }, + { "ble", BICC(0,2), { ARG_DISP22 }, }, + { "bge", BICC(0,11), { ARG_DISP22 }, }, + { "bl", BICC(0,3), { ARG_DISP22 }, }, + { "bgu", BICC(0,12), { ARG_DISP22 }, }, + { "bleu", BICC(0,4), { ARG_DISP22 }, }, + { "bcc", BICC(0,13), { ARG_DISP22 }, }, + { "bcs", BICC(0,5), { ARG_DISP22 }, }, + { "bpos", BICC(0,14), { ARG_DISP22 }, }, + { "bneg", BICC(0,6), { ARG_DISP22 }, }, + { "bvc", BICC(0,15), { ARG_DISP22 }, }, + { "bvs", BICC(0,7), { ARG_DISP22 }, }, + + { "add", FMT3I(2, 0x00), ARG_RS1_SIMM13_RD, }, + { "add", FMT3 (2, 0x00), ARG_RS1_RS2_RD, }, + { "and", FMT3I(2, 0x01), ARG_RS1_SIMM13_RD, }, + { "and", FMT3 (2, 0x01), ARG_RS1_RS2_RD, }, + { "or", FMT3I(2, 0x02), ARG_RS1_SIMM13_RD, }, + { "or", FMT3 (2, 0x02), ARG_RS1_RS2_RD, }, + { "xor", FMT3I(2, 0x03), ARG_RS1_SIMM13_RD, }, + { "xor", FMT3 (2, 0x03), ARG_RS1_RS2_RD, }, + { "sub", FMT3I(2, 0x04), ARG_RS1_SIMM13_RD, }, + { "sub", FMT3 (2, 0x04), ARG_RS1_RS2_RD, }, + { "andn", FMT3I(2, 0x05), ARG_RS1_SIMM13_RD, }, + { "andn", FMT3 (2, 0x05), ARG_RS1_RS2_RD, }, + { "orn", FMT3I(2, 0x06), ARG_RS1_SIMM13_RD, }, + { "orn", FMT3 (2, 0x06), ARG_RS1_RS2_RD, }, + { "xnor", FMT3I(2, 0x07), ARG_RS1_SIMM13_RD, }, + { "xnor", FMT3 (2, 0x07), ARG_RS1_RS2_RD, }, + + { "umul", FMT3I(2, 0x0a), ARG_RS1_SIMM13_RD, }, + { "umul", FMT3 (2, 0x0a), ARG_RS1_RS2_RD, }, + { "smul", FMT3I(2, 0x0b), ARG_RS1_SIMM13_RD, }, + { "smul", FMT3 (2, 0x0b), ARG_RS1_RS2_RD, }, + { "udiv", FMT3I(2, 0x0e), ARG_RS1_SIMM13_RD, }, + { "udiv", FMT3 (2, 0x0e), ARG_RS1_RS2_RD, }, + { "sdiv", FMT3I(2, 0x0f), ARG_RS1_SIMM13_RD, }, + { "sdiv", FMT3 (2, 0x0f), ARG_RS1_RS2_RD, }, + + { "subcc", FMT3I(2, 0x14), ARG_RS1_SIMM13_RD, }, + { "subcc", FMT3 (2, 0x14), ARG_RS1_RS2_RD, }, + + { "sll", FMT3I(2, 0x25), ARG_RS1_SIMM13_RD, }, + { "sll", FMT3 (2, 0x25), ARG_RS1_RS2_RD, }, + { "srl", FMT3I(2, 0x26), ARG_RS1_SIMM13_RD, }, + { "srl", FMT3 (2, 0x26), ARG_RS1_RS2_RD, }, + { "sra", FMT3I(2, 0x27), ARG_RS1_SIMM13_RD, }, + { "sra", FMT3 (2, 0x27), ARG_RS1_RS2_RD, }, + + { "wr", FMT3I(2, 0x30), ARG_RS1_SIMM13_RD, }, + { "wr", FMT3 (2, 0x30), ARG_RS1_SIMM13_RD, }, + + { "save", FMT3I(2,0x3c), ARG_RS1_SIMM13_RD, }, + { "save", FMT3 (2,0x3c), ARG_RS1_RS2_RD, }, + { "restore", FMT3I(2,0x3d), ARG_RS1_SIMM13_RD, }, + { "restore", FMT3 (2,0x3d), ARG_RS1_RS2_RD, }, + { "ta", TICC(8), { ARG_SWTRAP, ARG_NONE }, }, + { "jmpl", FMT3I(2,0x38), ARG_RS1_SIMM13_RD, }, + { "jmpl", FMT3 (2,0x38), ARG_RS1_RS2_RD, }, + + { "ldx", FMT3I(3,0x0b), ARG_RS1_SIMM13_RD, }, + { "ldx", FMT3 (3,0x0b), ARG_RS1_RS2_RD, }, + { "lduw", FMT3I(3,0x00), ARG_RS1_SIMM13_RD, }, + { "lduw", FMT3 (3,0x00), ARG_RS1_RS2_RD, }, + { "lduh", FMT3I(3,0x02), ARG_RS1_SIMM13_RD, }, + { "lduh", FMT3 (3,0x02), ARG_RS1_RS2_RD, }, + { "ldub", FMT3I(3,0x01), ARG_RS1_SIMM13_RD, }, + { "ldub", FMT3 (3,0x01), ARG_RS1_RS2_RD, }, + + { "stx", FMT3I(3,0x0e), ARG_RS1_SIMM13_RD, }, + { "stx", FMT3 (3,0x0e), ARG_RS1_RS2_RD, }, + { "stw", FMT3I(3,0x04), ARG_RS1_SIMM13_RD, }, + { "stw", FMT3 (3,0x04), ARG_RS1_RS2_RD, }, + { "sth", FMT3I(3,0x06), ARG_RS1_SIMM13_RD, }, + { "sth", FMT3 (3,0x06), ARG_RS1_RS2_RD, }, + { "stb", FMT3I(3,0x05), ARG_RS1_SIMM13_RD, }, + { "stb", FMT3 (3,0x05), ARG_RS1_RS2_RD, }, + + { "ldf", FMT3I(3,0x20), ARG_RS1_SIMM13_RD, }, + { "ldf", FMT3 (3,0x20), ARG_RS1_RS2_RD, }, + { "stf", FMT3I(3,0x24), ARG_RS1_SIMM13_RD, }, + { "stf", FMT3 (3,0x24), ARG_RS1_RS2_RD, }, + + { "fadd", FMT3F(2,0x34,0x041), ARG_RS1_RS2_RD, }, + { "fsub", FMT3F(2,0x34,0x045), ARG_RS1_RS2_RD, }, + { "fcmp", FMT3F(2,0x35,0x051), ARG_RS1_RS2, }, + { "fstoi", FMT3F(2,0x34,0x0d1), ARG_RS2_RD, }, + { "fitos", FMT3F(2,0x34,0x0c4), ARG_RS2_RD, }, + + { "fneg", FMT3F(2,0x34,0x005), ARG_RS2_RD, }, + { "fdiv", FMT3F(2,0x34,0x04d), ARG_RS1_RS2_RD, }, + { "fmul", FMT3F(2,0x34,0x049), ARG_RS1_RS2_RD, }, + + { "fbe", BFCC(0,9), { ARG_DISP22 }, }, + { "fbne", BFCC(0,1), { ARG_DISP22 }, }, + { "fbl", BFCC(0,4), { ARG_DISP22 }, }, + { "fbge", BFCC(0,11), { ARG_DISP22 }, }, + { "fbg", BFCC(0,6), { ARG_DISP22 }, }, + { "fble", BFCC(0,13), { ARG_DISP22 }, }, +}; +#define SPARC_NUM_OPCODES (sizeof(sparc_opcodes) / sizeof(sparc_opcodes[0])) + +#define RS1(X) (((X) & 0x1f) << 14) +#define RS2(X) (((X) & 0x1f) << 0) +#define RD(X) (((X) & 0x1f) << 25) +#define SIMM13(X) (((X) & 0x1fff) << 0) +#define IMM22(X) (((X) & 0x3fffff) << 0) +#define DISP30(X) ((((X) >> 2) & 0x3fffffff) << 0) +#define DISP22(X) ((((X) >> 2) & 0x3fffff) << 0) +#define SWTRAP(X) (((X) & 0x7f) << 0) + +#define SIMM13_P(X) ((unsigned int) (X) + 0x1000 < 0x2000) + +static void vimm(unsigned int val, int bits, int shift, int sgned, int arg_index) +{ + unsigned int orig_val = val; + int orig_bits = bits; + + if (sgned) { + int x = (int) val; + if (x < 0) + x = -x; + val = (unsigned int) x; + bits--; + } + if (val & ~((1U << bits) - 1U)) { + Com_Printf("VM ERROR: immediate value 0x%08x out of %d bit range\n", + orig_val, orig_bits); + DIE("sparc VM bug"); + } +} + +static unsigned int sparc_assemble(enum sparc_iname iname, const int argc, const int *argv) +{ + const struct sparc_opcode *op = &sparc_opcodes[iname]; + unsigned int insn = op->opcode; + int i, flt, rd_flt; + + flt = (op->name[0] == 'f'); + rd_flt = flt || (op->name[2] == 'f'); + + for (i = 0; op->args[i] != ARG_NONE; i++) { + int val = argv[i]; + + switch (op->args[i]) { + case ARG_RS1: insn |= RS1(val); break; + case ARG_RS2: insn |= RS2(val); break; + case ARG_RD: insn |= RD(val); break; + case ARG_SIMM13: insn |= SIMM13(val); vimm(val,13,0,1,i); break; + case ARG_DISP30: insn |= DISP30(val); vimm(val,30,0,1,i); break; + case ARG_IMM22: insn |= IMM22(val); vimm(val,22,0,0,i); break; + case ARG_DISP22: insn |= DISP22(val); vimm(val,22,0,1,i); break; + case ARG_SWTRAP: insn |= SWTRAP(val); vimm(val,7,0,0,i); break; + } + } + + return insn; +} + +#define IN(inst, args...) \ +({ const int argv[] = { args }; \ + const int argc = sizeof(argv) / sizeof(argv[0]); \ + sparc_assemble(inst, argc, argv); \ +}) + +#if 0 +static void pgreg(int reg_num, int arg_index, int flt) +{ + if (!flt) { + const char *fmt[] = { "%g", "%o", "%l", "%i" }; + + Com_Printf("%s%s%d", + (arg_index ? ", " : ""), + fmt[reg_num >> 3], reg_num & 7); + } else + Com_Printf("%s%%f%d", (arg_index ? ", " : ""), reg_num); +} + +static void pimm(unsigned int val, int bits, int shift, int sgned, int arg_index) + +{ + val >>= shift; + val &= ((1 << bits) - 1); + if (sgned) { + int sval = val << (32 - bits); + sval >>= (32 - bits); + Com_Printf("%s%d", + (arg_index ? ", " : ""), sval); + } else + Com_Printf("%s0x%08x", + (arg_index ? ", " : ""), val); +} + +static void sparc_disassemble(unsigned int insn) +{ + int op_idx; + + for (op_idx = 0; op_idx < SPARC_NUM_OPCODES; op_idx++) { + const struct sparc_opcode *op = &sparc_opcodes[op_idx]; + int i, flt, rd_flt; + + if ((insn & op->mask) != op->opcode) + continue; + + flt = (op->name[0] == 'f'); + rd_flt = flt || (op->name[2] == 'f'); + + Com_Printf("ASM: %7s\t", op->name); + for (i = 0; op->args[i] != ARG_NONE; i++) { + switch (op->args[i]) { + case ARG_RS1: pgreg((insn >> 14) & 0x1f, i, flt); break; + case ARG_RS2: pgreg((insn >> 0) & 0x1f, i, flt); break; + case ARG_RD: pgreg((insn >> 25) & 0x1f, i, rd_flt); break; + case ARG_SIMM13: pimm(insn, 13, 0, 1, i); break; + case ARG_DISP30: pimm(insn, 30, 0, 0, i); break; + case ARG_IMM22: pimm(insn, 22, 0, 0, i); break; + case ARG_DISP22: pimm(insn, 22, 0, 0, i); break; + case ARG_SWTRAP: pimm(insn, 7, 0, 0, i); break; + } + } + Com_Printf("\n"); + return; + } +} +#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, +}; + +static const char *opnames[256] = { + "OP_UNDEF", "OP_IGNORE", "OP_BREAK", "OP_ENTER", "OP_LEAVE", "OP_CALL", + "OP_PUSH", "OP_POP", "OP_CONST", "OP_LOCAL", "OP_JUMP", + "OP_EQ", "OP_NE", "OP_LTI", "OP_LEI", "OP_GTI", "OP_GEI", + "OP_LTU", "OP_LEU", "OP_GTU", "OP_GEU", "OP_EQF", "OP_NEF", + "OP_LTF", "OP_LEF", "OP_GTF", "OP_GEF", + "OP_LOAD1", "OP_LOAD2", "OP_LOAD4", "OP_STORE1", "OP_STORE2", + "OP_STORE4", "OP_ARG", "OP_BLOCK_COPY", + "OP_SEX8", "OP_SEX16", + "OP_NEGI", "OP_ADD", "OP_SUB", "OP_DIVI", "OP_DIVU", + "OP_MODI", "OP_MODU", "OP_MULI", "OP_MULU", "OP_BAND", + "OP_BOR", "OP_BXOR", "OP_BCOM", "OP_LSH", "OP_RSHI", "OP_RSHU", + "OP_NEGF", "OP_ADDF", "OP_SUBF", "OP_DIVF", "OP_MULF", + "OP_CVIF", "OP_CVFI", +}; + +static void VM_Destroy_Compiled(vm_t *vm) +{ + if (vm->codeBase) { + if (munmap(vm->codeBase, vm->codeLength)) + Com_Printf(S_COLOR_RED "Memory unmap failed, possible memory leak\n"); + } + vm->codeBase = NULL; +} + +typedef struct VM_Data { + unsigned int dataLength; + unsigned int codeLength; + unsigned int *CallThunk; + int (*AsmCall)(int, int); + void (*BlockCopy)(unsigned int, unsigned int, unsigned int); + unsigned int *iPointers; + 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 + +struct src_insn { + unsigned char op; + unsigned int i_count; + + union { + unsigned int i; + signed int si; + signed short ss[2]; + unsigned short us[2]; + unsigned char b; + } arg; + + unsigned char dst_reg_flags; + unsigned char src1_reg_flags; + unsigned char src2_reg_flags; +#define REG_FLAGS_FLOAT 0x1 + + struct src_insn *next; +}; + +struct dst_insn; +struct jump_insn { + enum sparc_iname jump_iname; + int jump_dest_insn; + struct dst_insn *parent; + struct jump_insn *next; +}; + +struct dst_insn { + struct dst_insn *next; + + unsigned int count; + unsigned int i_count; + + struct jump_insn *jump; + unsigned int length; + unsigned int code[0]; +}; + +#define HUNK_SIZE 29 +struct data_hunk { + struct data_hunk *next; + int count; + unsigned int data[HUNK_SIZE]; +}; + +struct func_info { + struct src_insn *first; + struct src_insn *last; + int has_call; + int need_float_tmp; + + struct src_insn *cached_const; + + int stack_space; + int gpr_pos; +#define rFIRST(fp) ((fp)->gpr_pos - 1) +#define rSECOND(fp) ((fp)->gpr_pos - 2) +#define POP_GPR(fp) ((fp)->gpr_pos--) +#define PUSH_GPR(fp) ((fp)->gpr_pos++) + + int fpr_pos; +#define fFIRST(fp) ((fp)->fpr_pos - 1) +#define fSECOND(fp) ((fp)->fpr_pos - 2) +#define POP_FPR(fp) ((fp)->fpr_pos--) +#define PUSH_FPR(fp) ((fp)->fpr_pos++) + +#define INSN_BUF_SIZE 50 + unsigned int insn_buf[INSN_BUF_SIZE]; + int insn_index; + + int saved_icount; + int force_emit; + + struct jump_insn *jump_first; + struct jump_insn *jump_last; + + struct dst_insn *dst_first; + struct dst_insn *dst_last; + int dst_count; + + struct dst_insn **dst_by_i_count; + + struct data_hunk *data_first; + int data_num; +}; + +#define THUNK_ICOUNT -1 + +static unsigned int sparc_push_data(struct func_info * const fp, unsigned int val) +{ + struct data_hunk *last, *dp = fp->data_first; + int off = 0; + + last = NULL; + while (dp) { + int i; + + for (i = 0; i < dp->count; i++) { + if (dp->data[i] == val) { + off += i; + return VM_Data_Offset(data[off]); + } + } + off += dp->count; + last = dp; + dp = dp->next; + } + + dp = last; + if (!dp || dp->count >= HUNK_SIZE) { + struct data_hunk *new = Z_Malloc(sizeof(*new)); + if (!dp) + fp->data_first = new; + else + dp->next = new; + dp = new; + dp->count = 0; + dp->next = NULL; + } + dp->data[dp->count++] = val; + fp->data_num = off + 1; + return VM_Data_Offset(data[off]); +} + +static void dst_insn_insert_tail(struct func_info * const fp, + struct dst_insn *dp) +{ + if (!fp->dst_first) { + fp->dst_first = fp->dst_last = dp; + } else { + fp->dst_last->next = dp; + fp->dst_last = dp; + } +} + +static void jump_insn_insert_tail(struct func_info * const fp, + struct jump_insn *jp) +{ + if (!fp->jump_first) { + fp->jump_first = fp->jump_last = jp; + } else { + fp->jump_last->next = jp; + fp->jump_last = jp; + } +} + +static struct dst_insn *dst_new(struct func_info * const fp, unsigned int length, + struct jump_insn *jp, int insns_size) +{ + struct dst_insn *dp = Z_Malloc(sizeof(struct dst_insn) + insns_size); + + dp->length = length; + dp->jump = jp; + dp->count = fp->dst_count++; + dp->i_count = fp->saved_icount; + dp->next = NULL; + if (fp->saved_icount != THUNK_ICOUNT) + fp->dst_by_i_count[fp->saved_icount] = dp; + + return dp; +} + +static void dst_insn_append(struct func_info * const fp) +{ + int insns_size = (sizeof(unsigned int) * fp->insn_index); + struct dst_insn *dp; + + dp = dst_new(fp, fp->insn_index, NULL, insns_size); + if (insns_size) + memcpy(&dp->code[0], fp->insn_buf, insns_size); + dst_insn_insert_tail(fp, dp); + + fp->insn_index = 0; +} + +static void jump_insn_append(struct func_info * const fp, enum sparc_iname iname, int dest) +{ + struct jump_insn *jp = Z_Malloc(sizeof(*jp)); + struct dst_insn *dp; + + dp = dst_new(fp, 2, jp, 0); + + jp->jump_iname = iname; + jp->jump_dest_insn = dest; + jp->parent = dp; + jp->next = NULL; + + jump_insn_insert_tail(fp, jp); + dst_insn_insert_tail(fp, dp); +} + +static void start_emit(struct func_info * const fp, int i_count) +{ + fp->saved_icount = i_count; + fp->insn_index = 0; + fp->force_emit = 0; +} + +static void __do_emit_one(struct func_info * const fp, unsigned int insn) +{ + fp->insn_buf[fp->insn_index++] = insn; +} + +#define in(inst, args...) __do_emit_one(fp, IN(inst, args)) + +static void end_emit(struct func_info * const fp) +{ + if (fp->insn_index || fp->force_emit) + dst_insn_append(fp); +} + +static void emit_jump(struct func_info * const fp, enum sparc_iname iname, int dest) +{ + end_emit(fp); + jump_insn_append(fp, iname, dest); +} + +static void analyze_function(struct func_info * const fp) +{ + struct src_insn *value_provider[20] = { NULL }; + struct src_insn *sp = fp->first; + int opstack_depth = 0; + + while ((sp = sp->next) != NULL) { + unsigned char opi, op = sp->op; + + opi = vm_opInfo[op]; + if (opi & opArgIF) { + struct src_insn *vp = value_provider[--opstack_depth]; + unsigned char vpopi = vm_opInfo[vp->op]; + + if ((opi & opArgI) && (vpopi & opRetI)) { + /* src1 and dst are integers */ + } else if ((opi & opArgF) && (vpopi & opRetF)) { + /* src1 and dst are floats */ + vp->dst_reg_flags |= REG_FLAGS_FLOAT; + sp->src1_reg_flags = REG_FLAGS_FLOAT; + } else { + /* illegal combination */ + DIE("unrecognized instruction combination"); + } + } + if (opi & opArg2IF) { + struct src_insn *vp = value_provider[--opstack_depth]; + unsigned char vpopi = vm_opInfo[vp->op]; + + if ((opi & opArg2I) && (vpopi & opRetI)) { + /* src2 and dst are integers */ + } else if ( (opi & opArg2F) && (vpopi & opRetF) ) { + /* src2 and dst are floats */ + vp->dst_reg_flags |= REG_FLAGS_FLOAT; + sp->src2_reg_flags = REG_FLAGS_FLOAT; + } else { + /* illegal combination */ + DIE("unrecognized instruction combination"); + } + } + if (opi & opRetIF) { + value_provider[opstack_depth] = sp; + opstack_depth++; + } + } +} + +static int asmcall(int call, int pstack) +{ + vm_t *savedVM = currentVM; + int i, ret; + + currentVM->programStack = pstack - 4; + if (sizeof(intptr_t) == sizeof(int)) { + intptr_t *argPosition = (intptr_t *)((byte *)currentVM->dataBase + pstack + 4); + argPosition[0] = -1 - call; + ret = currentVM->systemCall(argPosition); + } else { + intptr_t args[11]; + + args[0] = -1 - call; + int *argPosition = (int *)((byte *)currentVM->dataBase + pstack + 4); + for( i = 1; i < 11; i++ ) + args[i] = argPosition[i]; + + ret = currentVM->systemCall(args); + } + + currentVM = savedVM; + + return ret; +} + +static void blockcopy(unsigned int dest, unsigned int src, unsigned int count) +{ + unsigned int dataMask = currentVM->dataMask; + + if ((dest & dataMask) != dest || + (src & dataMask) != src || + ((dest+count) & dataMask) != dest + count || + ((src+count) & dataMask) != src + count) { + DIE("OP_BLOCK_COPY out of range!"); + } + + memcpy(currentVM->dataBase+dest, currentVM->dataBase+src, count); +} + +static void do_emit_const(struct func_info * const fp, struct src_insn *sp) +{ + start_emit(fp, sp->i_count); + if (sp->dst_reg_flags & REG_FLAGS_FLOAT) { + in(LDFI, rVMDATA, sparc_push_data(fp, sp->arg.i), fFIRST(fp)); + } else { + if ((sp->arg.i & ~0x3ff) == 0) { + in(ORI, G0, sp->arg.i & 0x3ff, rFIRST(fp)); + } else if ((sp->arg.i & 0x3ff) == 0) { + in(SETHI, sp->arg.i >> 10, rFIRST(fp)); + } else { + in(SETHI, sp->arg.i >> 10, rFIRST(fp)); + in(ORI, rFIRST(fp), sp->arg.i & 0x3ff, rFIRST(fp)); + } + } + end_emit(fp); +} + +#define MAYBE_EMIT_CONST(fp) \ +do { if ((fp)->cached_const) { \ + int saved_i_count = (fp)->saved_icount; \ + do_emit_const(fp, (fp)->cached_const); \ + (fp)->saved_icount = saved_i_count; \ + } \ +} while (0) + +#define EMIT_FALSE_CONST(fp) \ +do { int saved_i_count = (fp)->saved_icount; \ + (fp)->saved_icount = (fp)->cached_const->i_count; \ + dst_insn_append(fp); \ + (fp)->saved_icount = saved_i_count; \ +} while (0) + +static void compile_one_insn(struct func_info * const fp, struct src_insn *sp) +{ + start_emit(fp, sp->i_count); + + switch (sp->op) { + default: + Com_Printf("VM: Unhandled opcode 0x%02x[%s]\n", + sp->op, + opnames[sp->op] ? opnames[sp->op] : "UNKNOWN"); + DIE("Unsupported opcode"); + break; + + case OP_ENTER: { + int stack = SL(64, 128); + + if (fp->need_float_tmp) + stack += 16; + + in(SAVEI, O6, -stack, O6); + if (!SIMM13_P(sp->arg.si)) { + in(SETHI, sp->arg.i >> 10, rTMP); + in(ORI, rTMP, sp->arg.i & 0x3ff, rTMP); + in(SUB, rPSTACK, rTMP, rPSTACK); + } else + in(SUBI, rPSTACK, sp->arg.si, rPSTACK); + break; + } + case OP_LEAVE: + if (fp->cached_const && SIMM13_P(fp->cached_const->arg.si)) { + EMIT_FALSE_CONST(fp); + if (fp->cached_const->src1_reg_flags & REG_FLAGS_FLOAT) + DIE("constant float in OP_LEAVE"); + + if (!SIMM13_P(sp->arg.si)) { + in(SETHI, sp->arg.i >> 10, rTMP); + in(ORI, rTMP, sp->arg.i & 0x3ff, rTMP); + in(ADD, rPSTACK, rTMP, rPSTACK); + } else + in(ADDI, rPSTACK, sp->arg.si, rPSTACK); + in(JMPLI, I7, 8, G0); + in(RESTOREI, G0, fp->cached_const->arg.si, O0); + POP_GPR(fp); + } else { + MAYBE_EMIT_CONST(fp); + if (!SIMM13_P(sp->arg.si)) { + in(SETHI, sp->arg.i >> 10, rTMP); + in(ORI, rTMP, sp->arg.i & 0x3ff, rTMP); + in(ADD, rPSTACK, rTMP, rPSTACK); + } else + in(ADDI, rPSTACK, sp->arg.si, rPSTACK); + if (sp->src1_reg_flags & REG_FLAGS_FLOAT) { + in(STFI, O6, SL(64, 128), fFIRST(fp)); + in(LDUWI, O6, SL(64, 128), O0); + in(JMPLI, I7, 8, G0); + in(RESTORE, O0, G0, O0); + POP_FPR(fp); + } else { + in(JMPLI, I7, 8, G0); + in(RESTORE, rFIRST(fp), G0, O0); + POP_GPR(fp); + } + } + assert(fp->gpr_pos == L0); + assert(fp->fpr_pos == F0); + break; + case OP_JUMP: + if (fp->cached_const) { + EMIT_FALSE_CONST(fp); + emit_jump(fp, BA, fp->cached_const->arg.i); + } else { + MAYBE_EMIT_CONST(fp); + in(LDLI, rVMDATA, VM_Data_Offset(iPointers), rTMP); + in(SLLI, rFIRST(fp), 2, rFIRST(fp)); + in(LDL, rTMP, rFIRST(fp), rTMP); + in(JMPL, rTMP, G0, G0); + in(NOP); + } + POP_GPR(fp); + break; + case OP_CALL: + if (fp->cached_const) { + EMIT_FALSE_CONST(fp); + if (fp->cached_const->arg.si >= 0) { + emit_jump(fp, CALL, fp->cached_const->arg.i); + } else { + in(LDLI, rVMDATA, VM_Data_Offset(CallThunk), rTMP); + in(LDLI, rVMDATA, VM_Data_Offset(AsmCall), O3); + in(ORI, G0, fp->cached_const->arg.si, O0); + in(JMPL, rTMP, G0, O7); + in(OR, G0, rPSTACK, O1); + } + in(OR, G0, O0, rFIRST(fp)); + } else { + MAYBE_EMIT_CONST(fp); + in(SUBCCI, rFIRST(fp), 0, G0); + in(BL, +4*7); + in(NOP); + + /* normal call */ + in(LDLI, rVMDATA, VM_Data_Offset(iPointers), O5); + in(SLLI, rFIRST(fp), 2, rFIRST(fp)); + in(LDL, O5, rFIRST(fp), rTMP); + in(BA, +4*4); + in(NOP); + + /* syscall */ + in(LDLI, rVMDATA, VM_Data_Offset(CallThunk), rTMP); + in(LDLI, rVMDATA, VM_Data_Offset(AsmCall), O3); + + in(OR, G0, rFIRST(fp), O0); + in(JMPL, rTMP, G0, O7); + in(OR, G0, rPSTACK, O1); + + /* return value */ + in(OR, G0, O0, rFIRST(fp)); + } + break; + case OP_BLOCK_COPY: + MAYBE_EMIT_CONST(fp); + in(LDLI, rVMDATA, VM_Data_Offset(CallThunk), rTMP); + in(LDLI, rVMDATA, VM_Data_Offset(BlockCopy), O3); + in(OR, G0, rSECOND(fp), O0); + in(OR, G0, rFIRST(fp), O1); + if ((sp->arg.i & ~0x3ff) == 0) { + in(ORI, G0, sp->arg.i & 0x3ff, O2); + } else if ((sp->arg.i & 0x3ff) == 0) { + in(SETHI, sp->arg.i >> 10, O2); + } else { + in(SETHI, sp->arg.i >> 10, O2); + in(ORI, O2, sp->arg.i & 0x3ff, O2); + } + in(JMPL, rTMP, G0, O7); + in(NOP); + POP_GPR(fp); + POP_GPR(fp); + break; + + case OP_PUSH: + MAYBE_EMIT_CONST(fp); + if (sp->dst_reg_flags & REG_FLAGS_FLOAT) + PUSH_FPR(fp); + else + PUSH_GPR(fp); + fp->force_emit = 1; + break; + case OP_POP: + MAYBE_EMIT_CONST(fp); + if (sp->src1_reg_flags & REG_FLAGS_FLOAT) + POP_FPR(fp); + else + POP_GPR(fp); + fp->force_emit = 1; + break; + case OP_ARG: + MAYBE_EMIT_CONST(fp); + in(ADDI, rPSTACK, sp->arg.b, rTMP); + if (sp->src1_reg_flags & REG_FLAGS_FLOAT) { + in(STF, rDATABASE, rTMP, fFIRST(fp)); + POP_FPR(fp); + } else { + in(STW, rDATABASE, rTMP, rFIRST(fp)); + POP_GPR(fp); + } + break; + case OP_IGNORE: + MAYBE_EMIT_CONST(fp); + in(NOP); + break; + case OP_BREAK: + MAYBE_EMIT_CONST(fp); + in(TA, 0x5); + break; + case OP_LOCAL: + MAYBE_EMIT_CONST(fp); + PUSH_GPR(fp); + if (!SIMM13_P(sp->arg.i)) { + in(SETHI, sp->arg.i >> 10, rTMP); + in(ORI, rTMP, sp->arg.i & 0x3ff, rTMP); + in(ADD, rPSTACK, rTMP, rFIRST(fp)); + } else + in(ADDI, rPSTACK, sp->arg.i, rFIRST(fp)); + break; + case OP_CONST: + MAYBE_EMIT_CONST(fp); + break; + case OP_LOAD4: + MAYBE_EMIT_CONST(fp); + in(AND, rFIRST(fp), rDATAMASK, rFIRST(fp)); + if (sp->dst_reg_flags & REG_FLAGS_FLOAT) { + PUSH_FPR(fp); + in(LDF, rFIRST(fp), rDATABASE, fFIRST(fp)); + POP_GPR(fp); + } else { + in(LDUW, rFIRST(fp), rDATABASE, rFIRST(fp)); + } + break; + case OP_LOAD2: + MAYBE_EMIT_CONST(fp); + in(AND, rFIRST(fp), rDATAMASK, rFIRST(fp)); + in(LDUH, rFIRST(fp), rDATABASE, rFIRST(fp)); + break; + case OP_LOAD1: + MAYBE_EMIT_CONST(fp); + in(AND, rFIRST(fp), rDATAMASK, rFIRST(fp)); + in(LDUB, rFIRST(fp), rDATABASE, rFIRST(fp)); + break; + case OP_STORE4: + MAYBE_EMIT_CONST(fp); + if (sp->src1_reg_flags & REG_FLAGS_FLOAT) { + in(AND, rFIRST(fp), rDATAMASK, rFIRST(fp)); + in(STF, rFIRST(fp), rDATABASE, fFIRST(fp)); + POP_FPR(fp); + } else { + in(AND, rSECOND(fp), rDATAMASK, rSECOND(fp)); + in(STW, rSECOND(fp), rDATABASE, rFIRST(fp)); + POP_GPR(fp); + } + POP_GPR(fp); + break; + case OP_STORE2: + MAYBE_EMIT_CONST(fp); + in(AND, rSECOND(fp), rDATAMASK, rSECOND(fp)); + in(STH, rSECOND(fp), rDATABASE, rFIRST(fp)); + POP_GPR(fp); + POP_GPR(fp); + break; + case OP_STORE1: + MAYBE_EMIT_CONST(fp); + in(AND, rSECOND(fp), rDATAMASK, rSECOND(fp)); + in(STB, rSECOND(fp), rDATABASE, rFIRST(fp)); + POP_GPR(fp); + POP_GPR(fp); + break; + case OP_EQ: + case OP_NE: + case OP_LTI: + case OP_GEI: + case OP_GTI: + case OP_LEI: + case OP_LTU: + case OP_GEU: + case OP_GTU: + case OP_LEU: { + enum sparc_iname iname = BA; + + if (fp->cached_const && SIMM13_P(fp->cached_const->arg.si)) { + EMIT_FALSE_CONST(fp); + in(SUBCCI, rSECOND(fp), fp->cached_const->arg.si, G0); + } else { + MAYBE_EMIT_CONST(fp); + in(SUBCC, rSECOND(fp), rFIRST(fp), G0); + } + switch(sp->op) { + case OP_EQ: iname = BE; break; + case OP_NE: iname = BNE; break; + case OP_LTI: iname = BL; break; + case OP_GEI: iname = BGE; break; + case OP_GTI: iname = BG; break; + case OP_LEI: iname = BLE; break; + case OP_LTU: iname = BCS; break; + case OP_GEU: iname = BCC; break; + case OP_GTU: iname = BGU; break; + case OP_LEU: iname = BLEU; break; + } + emit_jump(fp, iname, sp->arg.i); + POP_GPR(fp); + POP_GPR(fp); + break; + } + + case OP_SEX8: + MAYBE_EMIT_CONST(fp); + in(SLLI, rFIRST(fp), 24, rFIRST(fp)); + in(SRAI, rFIRST(fp), 24, rFIRST(fp)); + break; + case OP_SEX16: + MAYBE_EMIT_CONST(fp); + in(SLLI, rFIRST(fp), 16, rFIRST(fp)); + in(SRAI, rFIRST(fp), 16, rFIRST(fp)); + break; + case OP_NEGI: + MAYBE_EMIT_CONST(fp); + in(SUB, G0, rFIRST(fp), rFIRST(fp)); + break; + case OP_ADD: + if (fp->cached_const && SIMM13_P(fp->cached_const->arg.si)) { + EMIT_FALSE_CONST(fp); + in(ADDI, rSECOND(fp), fp->cached_const->arg.si, rSECOND(fp)); + } else { + MAYBE_EMIT_CONST(fp); + in(ADD, rSECOND(fp), rFIRST(fp), rSECOND(fp)); + } + POP_GPR(fp); + break; + case OP_SUB: + if (fp->cached_const && SIMM13_P(fp->cached_const->arg.si)) { + EMIT_FALSE_CONST(fp); + in(SUBI, rSECOND(fp), fp->cached_const->arg.si, rSECOND(fp)); + } else { + MAYBE_EMIT_CONST(fp); + in(SUB, rSECOND(fp), rFIRST(fp), rSECOND(fp)); + } + POP_GPR(fp); + break; + case OP_DIVI: + MAYBE_EMIT_CONST(fp); + in(SRAI, rSECOND(fp), 31, rTMP); + in(WRI, rTMP, 0, Y_REG); + in(SDIV, rSECOND(fp), rFIRST(fp), rSECOND(fp)); + POP_GPR(fp); + break; + case OP_DIVU: + MAYBE_EMIT_CONST(fp); + in(WRI, G0, 0, Y_REG); + in(UDIV, rSECOND(fp), rFIRST(fp), rSECOND(fp)); + POP_GPR(fp); + break; + case OP_MODI: + MAYBE_EMIT_CONST(fp); + in(SRAI, rSECOND(fp), 31, rTMP); + in(WRI, rTMP, 0, Y_REG); + in(SDIV, rSECOND(fp), rFIRST(fp), rTMP); + in(SMUL, rTMP, rFIRST(fp), rTMP); + in(SUB, rSECOND(fp), rTMP, rSECOND(fp)); + POP_GPR(fp); + break; + case OP_MODU: + MAYBE_EMIT_CONST(fp); + in(WRI, G0, 0, Y_REG); + in(UDIV, rSECOND(fp), rFIRST(fp), rTMP); + in(SMUL, rTMP, rFIRST(fp), rTMP); + in(SUB, rSECOND(fp), rTMP, rSECOND(fp)); + POP_GPR(fp); + break; + case OP_MULI: + MAYBE_EMIT_CONST(fp); + in(SMUL, rSECOND(fp), rFIRST(fp), rSECOND(fp)); + POP_GPR(fp); + break; + case OP_MULU: + MAYBE_EMIT_CONST(fp); + in(UMUL, rSECOND(fp), rFIRST(fp), rSECOND(fp)); + POP_GPR(fp); + break; + case OP_BAND: + MAYBE_EMIT_CONST(fp); + in(AND, rSECOND(fp), rFIRST(fp), rSECOND(fp)); + POP_GPR(fp); + break; + case OP_BOR: + MAYBE_EMIT_CONST(fp); + in(OR, rSECOND(fp), rFIRST(fp), rSECOND(fp)); + POP_GPR(fp); + break; + case OP_BXOR: + MAYBE_EMIT_CONST(fp); + in(XOR, rSECOND(fp), rFIRST(fp), rSECOND(fp)); + POP_GPR(fp); + break; + case OP_BCOM: + MAYBE_EMIT_CONST(fp); + in(XNOR, rFIRST(fp), G0, rFIRST(fp)); + break; + case OP_LSH: + if (fp->cached_const) { + EMIT_FALSE_CONST(fp); + in(SLLI, rSECOND(fp), fp->cached_const->arg.si, rSECOND(fp)); + } else { + MAYBE_EMIT_CONST(fp); + in(SLL, rSECOND(fp), rFIRST(fp), rSECOND(fp)); + } + POP_GPR(fp); + break; + case OP_RSHI: + if (fp->cached_const) { + EMIT_FALSE_CONST(fp); + in(SRAI, rSECOND(fp), fp->cached_const->arg.si, rSECOND(fp)); + } else { + MAYBE_EMIT_CONST(fp); + in(SRA, rSECOND(fp), rFIRST(fp), rSECOND(fp)); + } + POP_GPR(fp); + break; + case OP_RSHU: + if (fp->cached_const) { + EMIT_FALSE_CONST(fp); + in(SRLI, rSECOND(fp), fp->cached_const->arg.si, rSECOND(fp)); + } else { + MAYBE_EMIT_CONST(fp); + in(SRL, rSECOND(fp), rFIRST(fp), rSECOND(fp)); + } + POP_GPR(fp); + break; + + case OP_NEGF: + MAYBE_EMIT_CONST(fp); + in(FNEG, fFIRST(fp), fFIRST(fp)); + break; + case OP_ADDF: + MAYBE_EMIT_CONST(fp); + in(FADD, fSECOND(fp), fFIRST(fp), fSECOND(fp)); + POP_FPR(fp); + break; + case OP_SUBF: + MAYBE_EMIT_CONST(fp); + in(FSUB, fSECOND(fp), fFIRST(fp), fSECOND(fp)); + POP_FPR(fp); + break; + case OP_DIVF: + MAYBE_EMIT_CONST(fp); + in(FDIV, fSECOND(fp), fFIRST(fp), fSECOND(fp)); + POP_FPR(fp); + break; + case OP_MULF: + MAYBE_EMIT_CONST(fp); + in(FMUL, fSECOND(fp), fFIRST(fp), fSECOND(fp)); + POP_FPR(fp); + break; + + case OP_EQF: + case OP_NEF: + case OP_LTF: + case OP_GEF: + case OP_GTF: + case OP_LEF: { + enum sparc_iname iname = FBE; + + MAYBE_EMIT_CONST(fp); + in(FCMP, fSECOND(fp), fFIRST(fp)); + switch(sp->op) { + case OP_EQF: iname = FBE; break; + case OP_NEF: iname = FBNE; break; + case OP_LTF: iname = FBL; break; + case OP_GEF: iname = FBGE; break; + case OP_GTF: iname = FBG; break; + case OP_LEF: iname = FBLE; break; + } + emit_jump(fp, iname, sp->arg.i); + POP_FPR(fp); + POP_FPR(fp); + break; + } + case OP_CVIF: + MAYBE_EMIT_CONST(fp); + PUSH_FPR(fp); + in(STWI, O6, SL(64, 128), rFIRST(fp)); + in(LDFI, O6, SL(64, 128), fFIRST(fp)); + in(FITOS, fFIRST(fp), fFIRST(fp)); + POP_GPR(fp); + break; + case OP_CVFI: + MAYBE_EMIT_CONST(fp); + PUSH_GPR(fp); + in(FSTOI, fFIRST(fp), fFIRST(fp)); + in(STFI, O6, SL(64, 128), fFIRST(fp)); + in(LDUWI, O6, SL(64, 128), rFIRST(fp)); + POP_FPR(fp); + break; + } + if (sp->op != OP_CONST) { + fp->cached_const = NULL; + end_emit(fp); + } else { + fp->cached_const = sp; + if (sp->dst_reg_flags & REG_FLAGS_FLOAT) { + PUSH_FPR(fp); + } else { + PUSH_GPR(fp); + } + } + end_emit(fp); +} + +static void free_source_insns(struct func_info * const fp) +{ + struct src_insn *sp = fp->first->next; + + while (sp) { + struct src_insn *next = sp->next; + Z_Free(sp); + sp = next; + } +} + +static void compile_function(struct func_info * const fp) +{ + struct src_insn *sp; + + analyze_function(fp); + + fp->gpr_pos = L0; + fp->fpr_pos = F0; + fp->insn_index = 0; + + fp->stack_space = SL(64, 128); + fp->cached_const = NULL; + + sp = fp->first; + while ((sp = sp->next) != NULL) + compile_one_insn(fp, sp); + + free_source_insns(fp); +} + +/* We have two thunks for sparc. The first is for the entry into + * the VM, where setup the fixed global registers. The second is + * for calling out to C code from the VM, where we need to preserve + * those fixed globals across the call. + */ +static void emit_vm_thunk(struct func_info * const fp) +{ + /* int vm_thunk(void *vmdata, int programstack, void *database, int datamask) */ + start_emit(fp, THUNK_ICOUNT); + + in(OR, G0, O0, rVMDATA); + in(OR, G0, O1, rPSTACK); + in(OR, G0, O2, rDATABASE); + in(BA, +4*17); + in(OR, G0, O3, rDATAMASK); + + /* int call_thunk(int arg0, int arg1, int arg2, int (*func)(int int int)) */ +#define CALL_THUNK_INSN_OFFSET 5 + in(SAVEI, O6, -SL(64, 128), O6); + + in(OR, G0, rVMDATA, L0); + in(OR, G0, rPSTACK, L1); + in(OR, G0, rDATABASE, L2); + in(OR, G0, rDATAMASK, L3); + + in(OR, G0, I0, O0); + in(OR, G0, I1, O1); + in(JMPL, I3, G0, O7); + in(OR, G0, I2, O2); + + in(OR, G0, L0, rVMDATA); + in(OR, G0, L1, rPSTACK); + in(OR, G0, L2, rDATABASE); + in(OR, G0, L3, rDATAMASK); + + in(JMPLI, I7, 8, G0); + in(RESTORE, O0, G0, O0); + + end_emit(fp); +} + +static void sparc_compute_code(vm_t *vm, struct func_info * const fp) +{ + struct dst_insn *dp = fp->dst_first; + unsigned int *code_now, *code_begin; + unsigned char *data_and_code; + unsigned int code_length; + int code_insns = 0, off; + struct data_hunk *dhp; + struct jump_insn *jp; + vm_data_t *data; + + while (dp) { + code_insns += dp->length; + dp = dp->next; + } + + code_length = (sizeof(vm_data_t) + + (fp->data_num * sizeof(unsigned int)) + + (code_insns * sizeof(unsigned int))); + + data_and_code = mmap(NULL, code_length, PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANONYMOUS, -1, 0); + if (!data_and_code) + DIE("Not enough memory"); + + code_now = code_begin = (unsigned int *) + (data_and_code + VM_Data_Offset(data[fp->data_num])); + + dp = fp->dst_first; + while (dp) { + int i_count = dp->i_count; + + if (i_count != THUNK_ICOUNT) { + if (!fp->dst_by_i_count[i_count]) + fp->dst_by_i_count[i_count] = (void *) code_now; + } + if (!dp->jump) { + memcpy(code_now, &dp->code[0], dp->length * sizeof(unsigned int)); + code_now += dp->length; + } else { + int i; + + dp->jump->parent = (void *) code_now; + + for (i = 0; i < dp->length; i++) + code_now[i] = SPARC_NOP; + code_now += dp->length; + } + + dp = dp->next; + } + + jp = fp->jump_first; + while (jp) { + unsigned int *from = (void *) jp->parent; + unsigned int *to = (void *) fp->dst_by_i_count[jp->jump_dest_insn]; + signed int disp = (to - from); + + *from = IN(jp->jump_iname, disp << 2); + + jp = jp->next; + } + + vm->codeBase = data_and_code; + vm->codeLength = code_length; + + data = (vm_data_t *) data_and_code; + data->CallThunk = code_begin + CALL_THUNK_INSN_OFFSET; + data->AsmCall = asmcall; + data->BlockCopy = blockcopy; + data->iPointers = (unsigned int *) vm->instructionPointers; + data->dataLength = VM_Data_Offset(data[fp->data_num]); + data->codeLength = (code_now - code_begin) * sizeof(unsigned int); + +#if 0 + { + unsigned int *insn = code_begin; + int i; + + Com_Printf("INSN DUMP\n"); + for (i = 0; i < data->codeLength / 4; i+= 8) { + Com_Printf("\t.word\t0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x\n", + insn[i + 0], insn[i + 1], + insn[i + 2], insn[i + 3], + insn[i + 4], insn[i + 5], + insn[i + 6], insn[i + 7]); + } + } +#endif + + dhp = fp->data_first; + off = 0; + while (dhp) { + struct data_hunk *next = dhp->next; + int i; + + for (i = 0; i < dhp->count; i++) + data->data[off + i] = dhp->data[i]; + + off += dhp->count; + + Z_Free(dhp); + + dhp = next; + } + fp->data_first = NULL; + fp->data_num = 0; + + dp = fp->dst_first; + while (dp) { + struct dst_insn *next = dp->next; + if (dp->jump) + Z_Free(dp->jump); + Z_Free(dp); + dp = next; + } + fp->dst_first = fp->dst_last = NULL; +} + +void VM_Compile(vm_t *vm, vmHeader_t *header) +{ + struct func_info fi; + unsigned char *code; + int i_count, pc, i; + + memset(&fi, 0, sizeof(fi)); + + fi.first = Z_Malloc(sizeof(struct src_insn)); + fi.first->next = NULL; + +#ifdef __arch64__ + Z_Free(vm->instructionPointers); + vm->instructionPointers = Z_Malloc(header->instructionCount * + sizeof(void *)); +#endif + + fi.dst_by_i_count = (struct dst_insn **) vm->instructionPointers; + memset(fi.dst_by_i_count, 0, header->instructionCount * sizeof(void *)); + + vm->compiled = qfalse; + + emit_vm_thunk(&fi); + + code = (unsigned char *) header + header->codeOffset; + pc = 0; + + for (i_count = 0; i_count < header->instructionCount; i_count++) { + unsigned char opi, op = code[pc++]; + struct src_insn *sp; + + if (op == OP_CALL || op == OP_BLOCK_COPY) + fi.has_call = 1; + opi = vm_opInfo[op]; + if (op == OP_CVIF || op == OP_CVFI || + (op == OP_LEAVE && (opi & opArgF))) + fi.need_float_tmp = 1; + + if (op == OP_ENTER) { + if (fi.first->next) + compile_function(&fi); + fi.first->next = NULL; + fi.last = fi.first; + fi.has_call = fi.need_float_tmp = 0; + } + + sp = Z_Malloc(sizeof(*sp)); + sp->op = op; + sp->i_count = i_count; + sp->arg.i = 0; + sp->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 ] }, }; + + sp->arg.i = c.i; + pc += 4; + } else if (vm_opInfo[op] & opImm1) { + sp->arg.b = code[pc++]; + } + + fi.last->next = sp; + fi.last = sp; + } + compile_function(&fi); + + Z_Free(fi.first); + + memset(fi.dst_by_i_count, 0, header->instructionCount * sizeof(void *)); + sparc_compute_code(vm, &fi); + + for (i = 0; i < header->instructionCount; i++) { + if (!fi.dst_by_i_count[i]) { + Com_Printf(S_COLOR_RED "Pointer %d not initialized !\n", i); + DIE("sparc JIT bug"); + } + } + + if (mprotect(vm->codeBase, vm->codeLength, PROT_READ|PROT_EXEC)) { + VM_Destroy_Compiled(vm); + DIE("mprotect failed"); + } + + vm->destroy = VM_Destroy_Compiled; + vm->compiled = qtrue; +} + +int VM_CallCompiled(vm_t *vm, int *args) +{ + vm_data_t *vm_dataAndCode = (void *) vm->codeBase; + int programStack = vm->programStack; + int stackOnEntry = programStack; + byte *image = vm->dataBase; + int *argPointer; + int retVal; + + currentVM = vm; + + vm->currentlyInterpreting = qtrue; + + programStack -= 48; + argPointer = (int *)&image[ programStack + 8 ]; + memcpy( argPointer, args, 4 * 9 ); + argPointer[-1] = 0; + argPointer[-2] = -1; + + /* call generated code */ + { + int (*entry)(void *, int, void *, int); + entry = (void *)(vm->codeBase + vm_dataAndCode->dataLength); + retVal = entry(vm->codeBase, programStack, vm->dataBase, vm->dataMask); + } + + vm->programStack = stackOnEntry; + vm->currentlyInterpreting = qfalse; + + return retVal; +} diff --git a/src/qcommon/vm_sparc.h b/src/qcommon/vm_sparc.h new file mode 100644 index 00000000..dbed6278 --- /dev/null +++ b/src/qcommon/vm_sparc.h @@ -0,0 +1,78 @@ +#ifndef VM_SPARC_H +#define VM_SPARC_H + +/* integer regs */ +#define G0 0 +#define G1 1 +#define G2 2 +#define G3 3 +#define G4 4 +#define G5 5 +#define G6 6 +#define G7 7 +#define O0 8 +#define O1 9 +#define O2 10 +#define O3 11 +#define O4 12 +#define O5 13 +#define O6 14 +#define O7 15 +#define L0 16 +#define L1 17 +#define L2 18 +#define L3 19 +#define L4 20 +#define L5 21 +#define L6 22 +#define L7 23 +#define I0 24 +#define I1 25 +#define I2 26 +#define I3 27 +#define I4 28 +#define I5 29 +#define I6 30 +#define I7 31 + +/* float regs */ +#define F0 0 +#define F1 1 +#define F2 2 +#define F3 3 +#define F4 4 +#define F5 5 +#define F6 6 +#define F7 7 +#define F8 8 +#define F9 9 +#define F10 10 +#define F11 11 +#define F12 12 +#define F13 13 +#define F14 14 +#define F15 15 +#define F16 16 +#define F17 17 +#define F18 18 +#define F19 19 +#define F20 20 +#define F21 21 +#define F22 22 +#define F23 23 +#define F24 24 +#define F25 25 +#define F26 26 +#define F27 27 +#define F28 28 +#define F29 29 +#define F30 30 +#define F31 31 + +/* state registers */ +#define Y_REG 0 +#define CCR_REG 2 +#define ASI_REG 3 +#define FPRS_REG 6 + +#endif diff --git a/src/qcommon/vm_x86.c b/src/qcommon/vm_x86.c index 64af7d11..0f558ef7 100644 --- a/src/qcommon/vm_x86.c +++ b/src/qcommon/vm_x86.c @@ -177,9 +177,14 @@ _asm { #else //!_MSC_VER #if defined(__MINGW32__) || defined(MACOS_X) // _ is prepended to compiled symbols -# define CMANG(sym) "_"#sym +#define CMANGVAR(sym) "_"#sym +#define CMANGFUNC(sym) "_"#sym +#elif defined(__ICC) && (__ICC >= 1000) +#define CMANGVAR(sym) #sym".0" +#define CMANGFUNC(sym) #sym #else -# define CMANG(sym) #sym +#define CMANGVAR(sym) #sym +#define CMANGFUNC(sym) #sym #endif static void __attribute__((cdecl, used)) CallAsmCall(int const syscallNum, @@ -200,32 +205,37 @@ __asm__( ".text\n\t" ".p2align 4,,15\n\t" #if defined __ELF__ - ".type " CMANG(AsmCall) ", @function\n" + ".type " CMANGFUNC(AsmCall) ", @function\n" #endif - CMANG(AsmCall) ":\n\t" + CMANGFUNC(AsmCall) ":\n\t" "movl (%edi), %eax\n\t" "subl $4, %edi\n\t" "testl %eax, %eax\n\t" "jl 0f\n\t" "shll $2, %eax\n\t" - "addl " CMANG(instructionPointers) ", %eax\n\t" + "addl " CMANGVAR(instructionPointers) ", %eax\n\t" "call *(%eax)\n\t" "movl (%edi), %eax\n\t" - "andl " CMANG(callMask) ", %eax\n\t" + "andl " CMANGVAR(callMask) ", %eax\n\t" "ret\n" "0:\n\t" // system call "notl %eax\n\t" + "pushl %ebp\n\t" + "movl %esp, %ebp\n\t" + "andl $-16, %esp\n\t" // align the stack so engine can use sse "pushl %ecx\n\t" "pushl %edi\n\t" // opStack "pushl %esi\n\t" // programStack "pushl %eax\n\t" // syscallNum - "call " CMANG(CallAsmCall) "\n\t" + "call " CMANGFUNC(CallAsmCall) "\n\t" "addl $12, %esp\n\t" "popl %ecx\n\t" + "movl %ebp, %esp\n\t" + "popl %ebp\n\t" "addl $4, %edi\n\t" "ret\n\t" #if defined __ELF__ - ".size " CMANG(AsmCall)", .-" CMANG(AsmCall) + ".size " CMANGFUNC(AsmCall)", .-" CMANGFUNC(AsmCall) #endif ); diff --git a/src/qcommon/vm_x86_64.c b/src/qcommon/vm_x86_64.c index 09a9c6d1..b424baf8 100644 --- a/src/qcommon/vm_x86_64.c +++ b/src/qcommon/vm_x86_64.c @@ -247,7 +247,7 @@ void emit(const char* fmt, ...) #else #define JMPIARG \ emit("movq $%lu, %%rax", vm->codeBase+vm->instructionPointers[iarg]); \ - emit("jmpq *%rax"); + emit("jmpq *%%rax"); #endif // integer compare and jump |