2016-10-08 06:35:16 +00:00
/*
* * vmbuilder . cpp
* *
* * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* * Copyright - 2016 Randy Heit
* * All rights reserved .
* *
* * Redistribution and use in source and binary forms , with or without
* * modification , are permitted provided that the following conditions
* * are met :
* *
* * 1. Redistributions of source code must retain the above copyright
* * notice , this list of conditions and the following disclaimer .
* * 2. Redistributions in binary form must reproduce the above copyright
* * notice , this list of conditions and the following disclaimer in the
* * documentation and / or other materials provided with the distribution .
* * 3. The name of the author may not be used to endorse or promote products
* * derived from this software without specific prior written permission .
* *
* * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ` ` AS IS ' ' AND ANY EXPRESS OR
* * IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE IMPLIED WARRANTIES
* * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED .
* * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT , INDIRECT ,
* * INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING , BUT
* * NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE ,
* * DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
* * THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT
* * ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF
* * THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
* * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* *
*/
2016-03-01 15:47:10 +00:00
# include "vmbuilder.h"
2017-02-08 11:24:08 +00:00
# include "codegen.h"
2016-10-12 22:53:59 +00:00
# include "info.h"
# include "m_argv.h"
# include "thingdef.h"
2016-11-20 22:00:05 +00:00
# include "doomerrors.h"
struct VMRemap
{
2017-03-08 17:44:37 +00:00
uint8_t altOp , kReg , kType ;
2016-11-20 22:00:05 +00:00
} ;
# define xx(op, name, mode, alt, kreg, ktype) {OP_##alt, kreg, ktype }
VMRemap opRemap [ NUM_OPS ] = {
# include "vmops.h"
} ;
# undef xx
2016-03-01 15:47:10 +00:00
//==========================================================================
//
// VMFunctionBuilder - Constructor
//
//==========================================================================
2016-10-31 16:02:47 +00:00
VMFunctionBuilder : : VMFunctionBuilder ( int numimplicits )
2016-03-01 15:47:10 +00:00
{
MaxParam = 0 ;
ActiveParam = 0 ;
2016-10-31 16:02:47 +00:00
NumImplicits = numimplicits ;
2016-03-01 15:47:10 +00:00
}
//==========================================================================
//
// VMFunctionBuilder - Destructor
//
//==========================================================================
VMFunctionBuilder : : ~ VMFunctionBuilder ( )
{
}
//==========================================================================
//
2016-12-02 16:36:29 +00:00
// VMFunctionBuilder :: BeginStatement
2016-03-01 15:47:10 +00:00
//
2016-12-02 16:36:29 +00:00
// Records the start of a new statement.
2016-03-01 15:47:10 +00:00
//
//==========================================================================
2016-12-02 16:36:29 +00:00
void VMFunctionBuilder : : BeginStatement ( FxExpression * stmt )
{
// pop empty statement records.
while ( LineNumbers . Size ( ) > 0 & & LineNumbers . Last ( ) . InstructionIndex = = Code . Size ( ) ) LineNumbers . Pop ( ) ;
// only add a new entry if the line number differs.
if ( LineNumbers . Size ( ) = = 0 | | stmt - > ScriptPosition . ScriptLine ! = LineNumbers . Last ( ) . LineNumber )
{
2016-12-03 11:23:13 +00:00
FStatementInfo si = { ( uint16_t ) Code . Size ( ) , ( uint16_t ) stmt - > ScriptPosition . ScriptLine } ;
2016-12-02 16:36:29 +00:00
LineNumbers . Push ( si ) ;
}
StatementStack . Push ( stmt ) ;
}
void VMFunctionBuilder : : EndStatement ( )
{
// pop empty statement records.
while ( LineNumbers . Size ( ) > 0 & & LineNumbers . Last ( ) . InstructionIndex = = Code . Size ( ) ) LineNumbers . Pop ( ) ;
StatementStack . Pop ( ) ;
// Re-enter the previous statement.
if ( StatementStack . Size ( ) > 0 )
{
2016-12-03 11:23:13 +00:00
FStatementInfo si = { ( uint16_t ) Code . Size ( ) , ( uint16_t ) StatementStack . Last ( ) - > ScriptPosition . ScriptLine } ;
2016-12-02 16:36:29 +00:00
LineNumbers . Push ( si ) ;
}
}
2016-10-12 22:53:59 +00:00
void VMFunctionBuilder : : MakeFunction ( VMScriptFunction * func )
2016-03-01 15:47:10 +00:00
{
2016-12-03 11:23:13 +00:00
func - > Alloc ( Code . Size ( ) , IntConstantList . Size ( ) , FloatConstantList . Size ( ) , StringConstantList . Size ( ) , AddressConstantList . Size ( ) , LineNumbers . Size ( ) ) ;
2016-03-01 15:47:10 +00:00
// Copy code block.
memcpy ( func - > Code , & Code [ 0 ] , Code . Size ( ) * sizeof ( VMOP ) ) ;
2016-12-03 11:23:13 +00:00
memcpy ( func - > LineInfo , & LineNumbers [ 0 ] , LineNumbers . Size ( ) * sizeof ( LineNumbers [ 0 ] ) ) ;
2016-03-01 15:47:10 +00:00
// Create constant tables.
2016-11-20 17:00:37 +00:00
if ( IntConstantList . Size ( ) > 0 )
2016-03-01 15:47:10 +00:00
{
FillIntConstants ( func - > KonstD ) ;
}
2016-11-20 17:00:37 +00:00
if ( FloatConstantList . Size ( ) > 0 )
2016-03-01 15:47:10 +00:00
{
FillFloatConstants ( func - > KonstF ) ;
}
2016-11-20 17:00:37 +00:00
if ( AddressConstantList . Size ( ) > 0 )
2016-03-01 15:47:10 +00:00
{
FillAddressConstants ( func - > KonstA , func - > KonstATags ( ) ) ;
}
2016-11-20 17:00:37 +00:00
if ( StringConstantList . Size ( ) > 0 )
2016-03-01 15:47:10 +00:00
{
FillStringConstants ( func - > KonstS ) ;
}
// Assign required register space.
func - > NumRegD = Registers [ REGT_INT ] . MostUsed ;
func - > NumRegF = Registers [ REGT_FLOAT ] . MostUsed ;
func - > NumRegA = Registers [ REGT_POINTER ] . MostUsed ;
func - > NumRegS = Registers [ REGT_STRING ] . MostUsed ;
func - > MaxParam = MaxParam ;
// Technically, there's no reason why we can't end the function with
// entries on the parameter stack, but it means the caller probably
// did something wrong.
assert ( ActiveParam = = 0 ) ;
}
//==========================================================================
//
// VMFunctionBuilder :: FillIntConstants
//
//==========================================================================
void VMFunctionBuilder : : FillIntConstants ( int * konst )
{
2016-11-20 17:00:37 +00:00
memcpy ( konst , & IntConstantList [ 0 ] , sizeof ( int ) * IntConstantList . Size ( ) ) ;
2016-03-01 15:47:10 +00:00
}
//==========================================================================
//
// VMFunctionBuilder :: FillFloatConstants
//
//==========================================================================
void VMFunctionBuilder : : FillFloatConstants ( double * konst )
{
2016-11-20 17:00:37 +00:00
memcpy ( konst , & FloatConstantList [ 0 ] , sizeof ( double ) * FloatConstantList . Size ( ) ) ;
2016-03-01 15:47:10 +00:00
}
//==========================================================================
//
// VMFunctionBuilder :: FillAddressConstants
//
//==========================================================================
void VMFunctionBuilder : : FillAddressConstants ( FVoidObj * konst , VM_ATAG * tags )
{
2016-11-20 17:00:37 +00:00
memcpy ( konst , & AddressConstantList [ 0 ] , sizeof ( void * ) * AddressConstantList . Size ( ) ) ;
memcpy ( tags , & AtagConstantList [ 0 ] , sizeof ( VM_ATAG ) * AtagConstantList . Size ( ) ) ;
2016-03-01 15:47:10 +00:00
}
//==========================================================================
//
// VMFunctionBuilder :: FillStringConstants
//
//==========================================================================
void VMFunctionBuilder : : FillStringConstants ( FString * konst )
{
2016-11-20 17:00:37 +00:00
for ( auto & s : StringConstantList )
2016-03-01 15:47:10 +00:00
{
2016-11-20 17:00:37 +00:00
* konst + + = s ;
2016-03-01 15:47:10 +00:00
}
}
//==========================================================================
//
// VMFunctionBuilder :: GetConstantInt
//
2016-11-20 17:00:37 +00:00
// Returns a constant register initialized with the given value.
2016-03-01 15:47:10 +00:00
//
//==========================================================================
2016-11-20 17:00:37 +00:00
unsigned VMFunctionBuilder : : GetConstantInt ( int val )
2016-03-01 15:47:10 +00:00
{
2016-11-20 17:00:37 +00:00
unsigned int * locp = IntConstantMap . CheckKey ( val ) ;
2016-03-01 15:47:10 +00:00
if ( locp ! = NULL )
{
return * locp ;
}
else
{
2016-11-20 17:00:37 +00:00
unsigned loc = IntConstantList . Push ( val ) ;
IntConstantMap . Insert ( val , loc ) ;
2016-03-01 15:47:10 +00:00
return loc ;
}
}
//==========================================================================
//
// VMFunctionBuilder :: GetConstantFloat
//
2016-11-20 17:00:37 +00:00
// Returns a constant register initialized with the given value.
2016-03-01 15:47:10 +00:00
//
//==========================================================================
2016-11-20 17:00:37 +00:00
unsigned VMFunctionBuilder : : GetConstantFloat ( double val )
2016-03-01 15:47:10 +00:00
{
2016-11-20 17:00:37 +00:00
unsigned * locp = FloatConstantMap . CheckKey ( val ) ;
2016-03-01 15:47:10 +00:00
if ( locp ! = NULL )
{
return * locp ;
}
else
{
2016-11-20 17:00:37 +00:00
unsigned loc = FloatConstantList . Push ( val ) ;
FloatConstantMap . Insert ( val , loc ) ;
2016-03-01 15:47:10 +00:00
return loc ;
}
}
//==========================================================================
//
// VMFunctionBuilder :: GetConstantString
//
2016-11-20 17:00:37 +00:00
// Returns a constant register initialized with the given value.
2016-03-01 15:47:10 +00:00
//
//==========================================================================
2016-11-20 17:00:37 +00:00
unsigned VMFunctionBuilder : : GetConstantString ( FString val )
2016-03-01 15:47:10 +00:00
{
2016-11-20 17:00:37 +00:00
unsigned * locp = StringConstantMap . CheckKey ( val ) ;
2016-03-01 15:47:10 +00:00
if ( locp ! = NULL )
{
return * locp ;
}
else
{
2016-11-20 17:00:37 +00:00
int loc = StringConstantList . Push ( val ) ;
StringConstantMap . Insert ( val , loc ) ;
2016-03-01 15:47:10 +00:00
return loc ;
}
}
//==========================================================================
//
// VMFunctionBuilder :: GetConstantAddress
//
// Returns a constant register initialized with the given value, or -1 if
// there were no more constants free.
//
//==========================================================================
2016-11-20 17:00:37 +00:00
unsigned VMFunctionBuilder : : GetConstantAddress ( void * ptr , VM_ATAG tag )
2016-03-01 15:47:10 +00:00
{
if ( ptr = = NULL )
{ // Make all NULL pointers generic. (Or should we allow typed NULLs?)
tag = ATAG_GENERIC ;
}
2016-11-20 17:00:37 +00:00
AddrKonst * locp = AddressConstantMap . CheckKey ( ptr ) ;
2016-03-01 15:47:10 +00:00
if ( locp ! = NULL )
{
2017-02-04 18:36:39 +00:00
// There should only be one tag associated with a memory location. Exceptions are made for null pointers that got allocated through constant arrays.
assert ( ptr = = nullptr | | locp - > Tag = = tag ) ;
2016-03-01 15:47:10 +00:00
return locp - > KonstNum ;
}
else
{
2016-11-20 17:00:37 +00:00
unsigned locc = AddressConstantList . Push ( ptr ) ;
AtagConstantList . Push ( tag ) ;
AddrKonst loc = { locc , tag } ;
AddressConstantMap . Insert ( ptr , loc ) ;
2016-03-01 15:47:10 +00:00
return loc . KonstNum ;
}
}
2016-11-20 17:00:37 +00:00
//==========================================================================
//
// VMFunctionBuilder :: AllocConstants*
//
// Returns a range of constant register initialized with the given values.
//
//==========================================================================
unsigned VMFunctionBuilder : : AllocConstantsInt ( unsigned count , int * values )
{
unsigned addr = IntConstantList . Reserve ( count ) ;
memcpy ( & IntConstantList [ addr ] , values , count * sizeof ( int ) ) ;
for ( unsigned i = 0 ; i < count ; i + + )
{
IntConstantMap . Insert ( values [ i ] , addr + i ) ;
}
return addr ;
}
unsigned VMFunctionBuilder : : AllocConstantsFloat ( unsigned count , double * values )
{
unsigned addr = FloatConstantList . Reserve ( count ) ;
memcpy ( & FloatConstantList [ addr ] , values , count * sizeof ( double ) ) ;
for ( unsigned i = 0 ; i < count ; i + + )
{
FloatConstantMap . Insert ( values [ i ] , addr + i ) ;
}
return addr ;
}
unsigned VMFunctionBuilder : : AllocConstantsAddress ( unsigned count , void * * ptrs , VM_ATAG tag )
{
unsigned addr = AddressConstantList . Reserve ( count ) ;
AtagConstantList . Reserve ( count ) ;
memcpy ( & AddressConstantList [ addr ] , ptrs , count * sizeof ( void * ) ) ;
for ( unsigned i = 0 ; i < count ; i + + )
{
AtagConstantList [ addr + i ] = tag ;
AddrKonst loc = { addr + i , tag } ;
AddressConstantMap . Insert ( ptrs [ i ] , loc ) ;
}
return addr ;
}
unsigned VMFunctionBuilder : : AllocConstantsString ( unsigned count , FString * ptrs )
{
unsigned addr = StringConstantList . Reserve ( count ) ;
for ( unsigned i = 0 ; i < count ; i + + )
{
StringConstantList [ addr + i ] = ptrs [ i ] ;
StringConstantMap . Insert ( ptrs [ i ] , addr + i ) ;
}
return addr ;
}
2016-03-01 15:47:10 +00:00
//==========================================================================
//
// VMFunctionBuilder :: ParamChange
//
// Adds delta to ActiveParam and keeps track of MaxParam.
//
//==========================================================================
void VMFunctionBuilder : : ParamChange ( int delta )
{
assert ( delta > 0 | | - delta < = ActiveParam ) ;
ActiveParam + = delta ;
if ( ActiveParam > MaxParam )
{
MaxParam = ActiveParam ;
}
}
//==========================================================================
//
// VMFunctionBuilder :: RegAvailability - Constructor
//
//==========================================================================
VMFunctionBuilder : : RegAvailability : : RegAvailability ( )
{
memset ( Used , 0 , sizeof ( Used ) ) ;
MostUsed = 0 ;
}
//==========================================================================
//
// VMFunctionBuilder :: RegAvailability :: Get
//
// Gets one or more unused registers. If getting multiple registers, they
// will all be consecutive. Returns -1 if there were not enough consecutive
// registers to satisfy the request.
//
// Preference is given to low-numbered registers in an attempt to keep
// the maximum register count low so as to preserve VM stack space when this
// function is executed.
//
//==========================================================================
int VMFunctionBuilder : : RegAvailability : : Get ( int count )
{
VM_UWORD mask ;
int i , firstbit ;
// Getting fewer than one register makes no sense, and
// the algorithm used here can only obtain ranges of up to 32 bits.
if ( count < 1 | | count > 32 )
{
return - 1 ;
}
2016-11-07 00:10:56 +00:00
2016-03-01 15:47:10 +00:00
mask = count = = 32 ? ~ 0u : ( 1 < < count ) - 1 ;
2016-11-07 00:10:56 +00:00
for ( i = 0 ; i < 256 / 32 ; + + i )
2016-03-01 15:47:10 +00:00
{
// Find the first word with free registers
VM_UWORD bits = Used [ i ] ;
if ( bits ! = ~ 0u )
{
// Are there enough consecutive bits to satisfy the request?
// Search by 16, then 8, then 1 bit at a time for the first
// free register.
if ( ( bits & 0xFFFF ) = = 0xFFFF )
{
firstbit = ( ( bits & 0xFF0000 ) = = 0xFF0000 ) ? 24 : 16 ;
}
else
{
firstbit = ( ( bits & 0xFF ) = = 0xFF ) ? 8 : 0 ;
}
for ( ; firstbit < 32 ; + + firstbit )
{
if ( ( ( bits > > firstbit ) & mask ) = = 0 )
{
if ( firstbit + count < = 32 )
{ // Needed bits all fit in one word, so we got it.
2016-11-07 00:10:56 +00:00
if ( i * 32 + firstbit + count > MostUsed )
2016-03-01 15:47:10 +00:00
{
2016-11-07 00:10:56 +00:00
MostUsed = i * 32 + firstbit + count ;
2016-03-01 15:47:10 +00:00
}
Used [ i ] | = mask < < firstbit ;
return i * 32 + firstbit ;
}
// Needed bits span two words, so check the next word.
else if ( i < 256 / 32 - 1 )
{ // There is a next word.
if ( ( ( Used [ i + 1 ] ) & ( mask > > ( 32 - firstbit ) ) ) = = 0 )
{ // The next word has the needed open space, too.
2016-11-07 00:10:56 +00:00
if ( i * 32 + firstbit + count > MostUsed )
2016-03-01 15:47:10 +00:00
{
2016-11-07 00:10:56 +00:00
MostUsed = i * 32 + firstbit + count ;
2016-03-01 15:47:10 +00:00
}
Used [ i ] | = mask < < firstbit ;
Used [ i + 1 ] | = mask > > ( 32 - firstbit ) ;
return i * 32 + firstbit ;
}
else
{ // Skip to the next word, because we know we won't find
// what we need if we stay inside this one. All bits
// from firstbit to the end of the word are 0. If the
// next word does not start with the x amount of 0's, we
// need to satisfy the request, then it certainly won't
// have the x+1 0's we would need if we started at
// firstbit+1 in this one.
firstbit = 32 ;
}
}
else
{ // Out of words.
break ;
}
}
}
}
}
// No room!
return - 1 ;
}
//==========================================================================
//
// VMFunctionBuilder :: RegAvailibity :: Return
//
// Marks a range of registers as free again.
//
//==========================================================================
void VMFunctionBuilder : : RegAvailability : : Return ( int reg , int count )
{
assert ( count > = 1 & & count < = 32 ) ;
assert ( reg > = 0 & & reg + count < = 256 ) ;
VM_UWORD mask , partialmask ;
int firstword , firstbit ;
mask = count = = 32 ? ~ 0u : ( 1 < < count ) - 1 ;
firstword = reg / 32 ;
firstbit = reg & 31 ;
if ( firstbit + count < = 32 )
{ // Range is all in one word.
mask < < = firstbit ;
// If we are trying to return registers that are already free,
// it probably means that the caller messed up somewhere.
assert ( ( Used [ firstword ] & mask ) = = mask ) ;
Used [ firstword ] & = ~ mask ;
}
else
{ // Range is in two words.
partialmask = mask < < firstbit ;
assert ( ( Used [ firstword ] & partialmask ) = = partialmask ) ;
Used [ firstword ] & = ~ partialmask ;
partialmask = mask > > ( 32 - firstbit ) ;
assert ( ( Used [ firstword + 1 ] & partialmask ) = = partialmask ) ;
Used [ firstword + 1 ] & = ~ partialmask ;
}
}
//==========================================================================
//
// VMFunctionBuilder :: RegAvailability :: Reuse
//
// Marks an unused register as in-use. Returns false if the register is
// already in use or true if it was successfully reused.
//
//==========================================================================
bool VMFunctionBuilder : : RegAvailability : : Reuse ( int reg )
{
assert ( reg > = 0 & & reg < = 255 ) ;
assert ( reg < MostUsed & & " Attempt to reuse a register that was never used " ) ;
VM_UWORD mask = 1 < < ( reg & 31 ) ;
int word = reg / 32 ;
if ( Used [ word ] & mask )
{ // It's already in use!
return false ;
}
Used [ word ] | = mask ;
return true ;
}
2016-07-25 04:38:02 +00:00
//==========================================================================
//
// VMFunctionBuilder :: GetAddress
//
//==========================================================================
size_t VMFunctionBuilder : : GetAddress ( )
{
return Code . Size ( ) ;
}
2016-03-01 15:47:10 +00:00
//==========================================================================
//
// VMFunctionBuilder :: Emit
//
// Just dumbly output an instruction. Returns instruction position, not
// byte position. (Because all instructions are exactly four bytes long.)
//
//==========================================================================
size_t VMFunctionBuilder : : Emit ( int opcode , int opa , int opb , int opc )
{
2017-03-08 17:44:37 +00:00
static uint8_t opcodes [ ] = { OP_LK , OP_LKF , OP_LKS , OP_LKP } ;
2016-11-20 22:00:05 +00:00
2016-03-01 15:47:10 +00:00
assert ( opcode > = 0 & & opcode < NUM_OPS ) ;
2016-11-20 22:00:05 +00:00
assert ( opa > = 0 ) ;
assert ( opb > = 0 ) ;
assert ( opc > = 0 ) ;
// The following were just asserts, meaning this would silently create broken code if there was an overflow
// if this happened in a release build. Not good.
// These are critical errors that need to be reported to the user.
// In addition, the limit of 256 constants can easily be exceeded with arrays so this had to be extended to
// 65535 by adding some checks here that map byte-limited instructions to alternatives that can handle larger indices.
// (See vmops.h for the remapping info.)
// Note: OP_CMPS also needs treatment, but I do not expect constant overflow to become an issue with strings, so for now there is no handling.
if ( opa > 255 )
{
if ( opRemap [ opcode ] . kReg ! = 1 | | opa > 32767 )
{
I_Error ( " Register limit exceeded " ) ;
}
int regtype = opRemap [ opcode ] . kType ;
ExpEmit emit ( this , regtype ) ;
Emit ( opcodes [ regtype ] , emit . RegNum , opa ) ;
opcode = opRemap [ opcode ] . altOp ;
opa = emit . RegNum ;
emit . Free ( this ) ;
}
if ( opb > 255 )
{
if ( opRemap [ opcode ] . kReg ! = 2 | | opb > 32767 )
{
I_Error ( " Register limit exceeded " ) ;
}
int regtype = opRemap [ opcode ] . kType ;
ExpEmit emit ( this , regtype ) ;
Emit ( opcodes [ regtype ] , emit . RegNum , opb ) ;
opcode = opRemap [ opcode ] . altOp ;
opb = emit . RegNum ;
emit . Free ( this ) ;
}
if ( opc > 255 )
{
if ( opcode = = OP_PARAM & & ( opb & REGT_KONST ) & & opc < = 32767 )
{
int regtype = opb & REGT_TYPE ;
opb = regtype ;
ExpEmit emit ( this , regtype ) ;
Emit ( opcodes [ regtype ] , emit . RegNum , opc ) ;
opc = emit . RegNum ;
emit . Free ( this ) ;
}
else
{
if ( opRemap [ opcode ] . kReg ! = 4 | | opc > 32767 )
{
I_Error ( " Register limit exceeded " ) ;
}
int regtype = opRemap [ opcode ] . kType ;
ExpEmit emit ( this , regtype ) ;
Emit ( opcodes [ regtype ] , emit . RegNum , opc ) ;
opcode = opRemap [ opcode ] . altOp ;
opc = emit . RegNum ;
emit . Free ( this ) ;
}
}
2016-03-01 15:47:10 +00:00
if ( opcode = = OP_PARAM )
{
2016-10-29 11:10:27 +00:00
int chg ;
if ( opb & REGT_MULTIREG2 ) chg = 2 ;
else if ( opb & REGT_MULTIREG3 ) chg = 3 ;
else chg = 1 ;
ParamChange ( chg ) ;
2016-03-01 15:47:10 +00:00
}
else if ( opcode = = OP_CALL | | opcode = = OP_CALL_K | | opcode = = OP_TAIL | | opcode = = OP_TAIL_K )
{
ParamChange ( - opb ) ;
}
VMOP op ;
op . op = opcode ;
op . a = opa ;
op . b = opb ;
op . c = opc ;
return Code . Push ( op ) ;
}
size_t VMFunctionBuilder : : Emit ( int opcode , int opa , VM_SHALF opbc )
{
assert ( opcode > = 0 & & opcode < NUM_OPS ) ;
assert ( opa > = 0 & & opa < = 255 ) ;
//assert(opbc >= -32768 && opbc <= 32767); always true due to parameter's width
VMOP op ;
op . op = opcode ;
op . a = opa ;
op . i16 = opbc ;
return Code . Push ( op ) ;
}
size_t VMFunctionBuilder : : Emit ( int opcode , int opabc )
{
assert ( opcode > = 0 & & opcode < NUM_OPS ) ;
assert ( opabc > = - ( 1 < < 23 ) & & opabc < = ( 1 < < 24 ) - 1 ) ;
if ( opcode = = OP_PARAMI )
{
ParamChange ( 1 ) ;
}
VMOP op ;
op . op = opcode ;
op . i24 = opabc ;
return Code . Push ( op ) ;
}
//==========================================================================
//
// VMFunctionBuilder :: EmitParamInt
//
// Passes a constant integer parameter, using either PARAMI and an immediate
// value or PARAM and a constant register, as appropriate.
//
//==========================================================================
size_t VMFunctionBuilder : : EmitParamInt ( int value )
{
// Immediates for PARAMI must fit in 24 bits.
if ( ( ( value < < 8 ) > > 8 ) = = value )
{
return Emit ( OP_PARAMI , value ) ;
}
else
{
return Emit ( OP_PARAM , 0 , REGT_INT | REGT_KONST , GetConstantInt ( value ) ) ;
}
}
//==========================================================================
//
// VMFunctionBuilder :: EmitLoadInt
//
// Loads an integer constant into a register, using either an immediate
// value or a constant register, as appropriate.
//
//==========================================================================
size_t VMFunctionBuilder : : EmitLoadInt ( int regnum , int value )
{
assert ( regnum > = 0 & & regnum < Registers [ REGT_INT ] . MostUsed ) ;
if ( value > = - 32768 & & value < = 32767 )
{
return Emit ( OP_LI , regnum , value ) ;
}
else
{
return Emit ( OP_LK , regnum , GetConstantInt ( value ) ) ;
}
}
//==========================================================================
//
// VMFunctionBuilder :: EmitRetInt
//
// Returns an integer, using either an immediate value or a constant
// register, as appropriate.
//
//==========================================================================
size_t VMFunctionBuilder : : EmitRetInt ( int retnum , bool final , int value )
{
assert ( retnum > = 0 & & retnum < = 127 ) ;
if ( value > = - 32768 & & value < = 32767 )
{
return Emit ( OP_RETI , retnum | ( final < < 7 ) , value ) ;
}
else
{
return Emit ( OP_RET , retnum | ( final < < 7 ) , REGT_INT | REGT_KONST , GetConstantInt ( value ) ) ;
}
}
//==========================================================================
//
// VMFunctionBuilder :: Backpatch
//
// Store a JMP instruction at <loc> that points at <target>.
//
//==========================================================================
void VMFunctionBuilder : : Backpatch ( size_t loc , size_t target )
{
assert ( loc < Code . Size ( ) ) ;
int offset = int ( target - loc - 1 ) ;
assert ( ( ( offset < < 8 ) > > 8 ) = = offset ) ;
Code [ loc ] . op = OP_JMP ;
Code [ loc ] . i24 = offset ;
}
2016-12-01 23:51:29 +00:00
void VMFunctionBuilder : : BackpatchList ( TArray < size_t > & locs , size_t target )
{
for ( auto loc : locs )
Backpatch ( loc , target ) ;
}
2016-03-01 15:47:10 +00:00
//==========================================================================
//
// VMFunctionBuilder :: BackpatchToHere
//
// Store a JMP instruction at <loc> that points to the current code gen
// location.
//
//==========================================================================
void VMFunctionBuilder : : BackpatchToHere ( size_t loc )
{
Backpatch ( loc , Code . Size ( ) ) ;
}
2016-10-12 22:53:59 +00:00
2016-12-01 23:51:29 +00:00
void VMFunctionBuilder : : BackpatchListToHere ( TArray < size_t > & locs )
{
for ( auto loc : locs )
Backpatch ( loc , Code . Size ( ) ) ;
}
2016-10-12 22:53:59 +00:00
//==========================================================================
//
// FFunctionBuildList
//
// This list contains all functions yet to build.
// All adding functions return a VMFunction - either a complete one
// for native functions or an empty VMScriptFunction for scripted ones
// This VMScriptFunction object later gets filled in with the actual
// info, but we get the pointer right after registering the function
// with the builder.
//
//==========================================================================
FFunctionBuildList FunctionBuildList ;
2017-03-05 16:58:55 +00:00
VMFunction * FFunctionBuildList : : AddFunction ( PNamespace * gnspc , const VersionInfo & ver , PFunction * functype , FxExpression * code , const FString & name , bool fromdecorate , int stateindex , int statecount , int lumpnum )
2016-10-12 22:53:59 +00:00
{
2017-03-05 20:44:10 +00:00
auto func = code - > GetDirectFunction ( ver ) ;
2016-10-12 22:53:59 +00:00
if ( func ! = nullptr )
{
delete code ;
return func ;
}
2016-10-13 18:45:52 +00:00
//Printf("Adding %s\n", name.GetChars());
2016-10-12 22:53:59 +00:00
Item it ;
2017-01-23 18:09:36 +00:00
assert ( gnspc ! = nullptr ) ;
it . CurGlobals = gnspc ;
2016-10-15 12:36:08 +00:00
it . Func = functype ;
2016-10-12 22:53:59 +00:00
it . Code = code ;
2016-10-20 14:55:12 +00:00
it . PrintableName = name ;
2016-10-12 22:53:59 +00:00
it . Function = new VMScriptFunction ;
2016-10-26 09:30:30 +00:00
it . Function - > Name = functype - > SymbolName ;
2016-11-27 17:52:24 +00:00
it . Function - > PrintableName = name ;
2016-10-22 14:35:48 +00:00
it . Function - > ImplicitArgs = functype - > GetImplicitArgs ( ) ;
2016-10-12 22:53:59 +00:00
it . Proto = nullptr ;
2016-10-15 19:35:31 +00:00
it . FromDecorate = fromdecorate ;
2016-11-06 12:14:46 +00:00
it . StateIndex = stateindex ;
it . StateCount = statecount ;
2016-11-08 10:12:56 +00:00
it . Lump = lumpnum ;
2017-03-05 16:58:55 +00:00
it . Version = ver ;
2016-11-21 11:38:39 +00:00
assert ( it . Func - > Variants . Size ( ) = = 1 ) ;
it . Func - > Variants [ 0 ] . Implementation = it . Function ;
2016-10-30 15:21:44 +00:00
// set prototype for named functions.
if ( it . Func - > SymbolName ! = NAME_None )
{
it . Function - > Proto = it . Func - > Variants [ 0 ] . Proto ;
}
2016-10-12 22:53:59 +00:00
mItems . Push ( it ) ;
return it . Function ;
}
void FFunctionBuildList : : Build ( )
{
int errorcount = 0 ;
int codesize = 0 ;
2017-03-02 10:58:30 +00:00
int datasize = 0 ;
2016-10-12 22:53:59 +00:00
FILE * dump = nullptr ;
if ( Args - > CheckParm ( " -dumpdisasm " ) ) dump = fopen ( " disasm.txt " , " w " ) ;
for ( auto & item : mItems )
{
assert ( item . Code ! = NULL ) ;
// We don't know the return type in advance for anonymous functions.
2017-03-05 19:40:07 +00:00
FCompileContext ctx ( item . CurGlobals , item . Func , item . Func - > SymbolName = = NAME_None ? nullptr : item . Func - > Variants [ 0 ] . Proto , item . FromDecorate , item . StateIndex , item . StateCount , item . Lump , item . Version ) ;
2016-10-20 14:55:12 +00:00
// Allocate registers for the function's arguments and create local variable nodes before starting to resolve it.
2016-11-02 15:05:57 +00:00
VMFunctionBuilder buildit ( item . Func - > GetImplicitArgs ( ) ) ;
2017-01-03 15:00:25 +00:00
for ( unsigned i = 0 ; i < item . Func - > Variants [ 0 ] . Proto - > ArgumentTypes . Size ( ) ; i + + )
2016-10-20 14:55:12 +00:00
{
auto type = item . Func - > Variants [ 0 ] . Proto - > ArgumentTypes [ i ] ;
auto name = item . Func - > Variants [ 0 ] . ArgNames [ i ] ;
2016-10-20 23:12:54 +00:00
auto flags = item . Func - > Variants [ 0 ] . ArgFlags [ i ] ;
// this won't get resolved and won't get emitted. It is only needed so that the code generator can retrieve the necessary info about this argument to do its work.
2017-01-03 15:00:25 +00:00
auto local = new FxLocalVariableDeclaration ( type , name , nullptr , flags , FScriptPosition ( ) ) ;
if ( ! ( flags & VARF_Out ) ) local - > RegNum = buildit . Registers [ type - > GetRegType ( ) ] . Get ( type - > GetRegCount ( ) ) ;
else local - > RegNum = buildit . Registers [ REGT_POINTER ] . Get ( 1 ) ;
2016-10-20 14:55:12 +00:00
ctx . FunctionArgs . Push ( local ) ;
}
2016-11-05 08:50:53 +00:00
FScriptPosition : : StrictErrors = ! item . FromDecorate ;
2016-10-12 22:53:59 +00:00
item . Code = item . Code - > Resolve ( ctx ) ;
2016-11-17 15:44:41 +00:00
// If we need extra space, load the frame pointer into a register so that we do not have to call the wasteful LFP instruction more than once.
if ( item . Function - > ExtraSpace > 0 )
{
buildit . FramePointer = ExpEmit ( & buildit , REGT_POINTER ) ;
buildit . FramePointer . Fixed = true ;
buildit . Emit ( OP_LFP , buildit . FramePointer . RegNum ) ;
}
2016-10-12 22:53:59 +00:00
// Make sure resolving it didn't obliterate it.
if ( item . Code ! = nullptr )
{
2016-11-05 17:56:04 +00:00
if ( ! item . Code - > CheckReturn ( ) )
{
auto newcmpd = new FxCompoundStatement ( item . Code - > ScriptPosition ) ;
newcmpd - > Add ( item . Code ) ;
newcmpd - > Add ( new FxReturnStatement ( nullptr , item . Code - > ScriptPosition ) ) ;
item . Code = newcmpd - > Resolve ( ctx ) ;
}
2016-11-05 20:02:26 +00:00
item . Proto = ctx . ReturnProto ;
if ( item . Proto = = nullptr )
{
item . Code - > ScriptPosition . Message ( MSG_ERROR , " Function %s without prototype " , item . PrintableName . GetChars ( ) ) ;
continue ;
}
2016-10-30 15:21:44 +00:00
// Generate prototype for anonymous functions.
2016-10-12 22:53:59 +00:00
VMScriptFunction * sfunc = item . Function ;
2016-10-15 12:36:08 +00:00
// create a new prototype from the now known return type and the argument list of the function's template prototype.
2016-10-30 15:21:44 +00:00
if ( sfunc - > Proto = = nullptr )
{
sfunc - > Proto = NewPrototype ( item . Proto - > ReturnTypes , item . Func - > Variants [ 0 ] . Proto - > ArgumentTypes ) ;
}
2016-10-12 22:53:59 +00:00
// Emit code
2016-11-20 22:00:05 +00:00
try
2016-10-30 08:05:42 +00:00
{
2016-12-02 16:36:29 +00:00
sfunc - > SourceFileName = item . Code - > ScriptPosition . FileName ; // remember the file name for printing error messages if something goes wrong in the VM.
buildit . BeginStatement ( item . Code ) ;
2016-11-20 22:00:05 +00:00
item . Code - > Emit ( & buildit ) ;
2016-12-02 16:36:29 +00:00
buildit . EndStatement ( ) ;
2016-11-20 22:00:05 +00:00
buildit . MakeFunction ( sfunc ) ;
sfunc - > NumArgs = 0 ;
// NumArgs for the VMFunction must be the amount of stack elements, which can differ from the amount of logical function arguments if vectors are in the list.
// For the VM a vector is 2 or 3 args, depending on size.
for ( auto s : item . Func - > Variants [ 0 ] . Proto - > ArgumentTypes )
{
sfunc - > NumArgs + = s - > GetRegCount ( ) ;
}
2016-10-12 22:53:59 +00:00
2016-11-20 22:00:05 +00:00
if ( dump ! = nullptr )
{
DumpFunction ( dump , sfunc , item . PrintableName . GetChars ( ) , ( int ) item . PrintableName . Len ( ) ) ;
codesize + = sfunc - > CodeSize ;
2017-03-02 10:58:30 +00:00
datasize + = sfunc - > LineInfoCount * sizeof ( FStatementInfo ) + sfunc - > ExtraSpace + sfunc - > NumKonstD * sizeof ( int ) +
sfunc - > NumKonstA * sizeof ( void * ) + sfunc - > NumKonstF * sizeof ( double ) + sfunc - > NumKonstS * sizeof ( FString ) ;
2016-11-20 22:00:05 +00:00
}
sfunc - > Unsafe = ctx . Unsafe ;
}
catch ( CRecoverableError & err )
2016-10-12 22:53:59 +00:00
{
2016-11-20 22:00:05 +00:00
// catch errors from the code generator and pring something meaningful.
2016-11-21 18:09:58 +00:00
item . Code - > ScriptPosition . Message ( MSG_ERROR , " %s in %s " , err . GetMessage ( ) , item . PrintableName . GetChars ( ) ) ;
2016-10-12 22:53:59 +00:00
}
}
delete item . Code ;
2016-10-27 22:32:52 +00:00
if ( dump ! = nullptr )
{
fflush ( dump ) ;
}
2016-10-12 22:53:59 +00:00
}
if ( dump ! = nullptr )
{
2017-03-02 10:58:30 +00:00
fprintf ( dump , " \n ************************************************************************* \n %i code bytes \n %i data bytes " , codesize * 4 , datasize ) ;
2016-10-12 22:53:59 +00:00
fclose ( dump ) ;
}
2016-11-05 08:50:53 +00:00
FScriptPosition : : StrictErrors = false ;
2016-11-07 10:30:41 +00:00
mItems . Clear ( ) ;
2017-03-02 10:58:30 +00:00
mItems . ShrinkToFit ( ) ;
2016-11-10 14:13:31 +00:00
FxAlloc . FreeAllBlocks ( ) ;
2016-10-12 22:53:59 +00:00
}