Control - Implemented all basic opcodes
+ memory access + flow control + FPU ops
This commit is contained in:
parent
8b7964632a
commit
512c981402
2 changed files with 381 additions and 13 deletions
51
control.hh
51
control.hh
|
@ -112,6 +112,10 @@ union T_OpValue
|
||||||
int32_t i;
|
int32_t i;
|
||||||
uint32_t u;
|
uint32_t u;
|
||||||
float f;
|
float f;
|
||||||
|
|
||||||
|
constexpr T_OpValue( ) : i{ 0 } {}
|
||||||
|
constexpr T_OpValue( uint32_t u ) : u{ u } {}
|
||||||
|
constexpr T_OpValue( float f ) : f{ f } {}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct T_OpProgram
|
struct T_OpProgram
|
||||||
|
@ -136,18 +140,49 @@ struct T_OpProgram
|
||||||
};
|
};
|
||||||
using P_OpProgram = T_OwnPtr< T_OpProgram >;
|
using P_OpProgram = T_OwnPtr< T_OpProgram >;
|
||||||
|
|
||||||
|
class T_Compiler : public A_PrivateImplementation
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
T_Compiler( ) noexcept;
|
||||||
|
|
||||||
|
P_OpProgram compile(
|
||||||
|
opast::T_ParserOutput const& input ) noexcept;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
struct T_OpContext
|
struct T_OpContext
|
||||||
{
|
{
|
||||||
T_OpProgram& program; // The program
|
T_OpProgram& program; // The program
|
||||||
|
|
||||||
uint32_t instrPtr; // Instruction pointer
|
uint32_t instrPtr; // Instruction pointer
|
||||||
bool aborted; // Did the program fail?
|
bool aborted{ false }; // Did the program fail?
|
||||||
|
|
||||||
T_Array< T_OpValue > values; // VM data
|
T_Array< T_OpValue > values; // VM data
|
||||||
T_Array< T_OpValue > stack; // Main VM stack
|
T_Array< T_OpValue > stack; // Main VM stack
|
||||||
|
|
||||||
|
T_OpValue wreg; // Work register
|
||||||
double x87stack[ 8 ]; // x87 FPU emulation stack
|
double x87stack[ 8 ]; // x87 FPU emulation stack
|
||||||
int x87sp; // x87 FPU emulation stack pointer
|
uint32_t x87sp; // x87 FPU emulation stack pointer
|
||||||
|
|
||||||
|
enum E_RunTarget {
|
||||||
|
R_INIT ,
|
||||||
|
R_RENDER
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit T_OpContext( T_OpProgram& program ) noexcept;
|
||||||
|
void run( E_RunTarget target ,
|
||||||
|
float time ,
|
||||||
|
float width ,
|
||||||
|
float height );
|
||||||
|
|
||||||
|
private:
|
||||||
|
void ensureStack( T_Op const& op ,
|
||||||
|
uint32_t min );
|
||||||
|
void ensureFpuStack( T_Op const& op ,
|
||||||
|
uint32_t minStacked ,
|
||||||
|
uint32_t minFree );
|
||||||
|
void checkAddress( T_Op const& op ,
|
||||||
|
uint32_t address );
|
||||||
};
|
};
|
||||||
|
|
||||||
class X_OpFailure : public std::exception
|
class X_OpFailure : public std::exception
|
||||||
|
@ -157,7 +192,8 @@ class X_OpFailure : public std::exception
|
||||||
DEF_COPY( X_OpFailure );
|
DEF_COPY( X_OpFailure );
|
||||||
DEF_MOVE( X_OpFailure );
|
DEF_MOVE( X_OpFailure );
|
||||||
|
|
||||||
X_OpFailure( T_Op const& op ,
|
X_OpFailure(
|
||||||
|
T_Op const& op ,
|
||||||
T_String error ) noexcept;
|
T_String error ) noexcept;
|
||||||
|
|
||||||
T_Op const& op( ) const noexcept
|
T_Op const& op( ) const noexcept
|
||||||
|
@ -173,15 +209,6 @@ class X_OpFailure : public std::exception
|
||||||
T_String fullMessage_;
|
T_String fullMessage_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class T_Compiler : public A_PrivateImplementation
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
T_Compiler( ) noexcept;
|
|
||||||
|
|
||||||
P_OpProgram compile(
|
|
||||||
opast::T_ParserOutput const& input ) noexcept;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace ops
|
} // namespace ops
|
||||||
|
|
||||||
|
|
||||||
|
|
343
ops.cc
343
ops.cc
|
@ -91,7 +91,7 @@ static T_KeyValueTable< E_OpType , T_OpInfo > OpInfoTable_{ ([]() {
|
||||||
infos.add( E_OpType::OP_RES_STACK , T_OpInfo{ "res-stack" , 1 } );
|
infos.add( E_OpType::OP_RES_STACK , T_OpInfo{ "res-stack" , 1 } );
|
||||||
infos.add( E_OpType::OP_PUSH , T_OpInfo{ "push" , 0 , OpStackMain{ 1 } } );
|
infos.add( E_OpType::OP_PUSH , T_OpInfo{ "push" , 0 , OpStackMain{ 1 } } );
|
||||||
infos.add( E_OpType::OP_POP , T_OpInfo{ "pop" , 1 } );
|
infos.add( E_OpType::OP_POP , T_OpInfo{ "pop" , 1 } );
|
||||||
infos.add( E_OpType::OP_POP , T_OpInfo{ "dup" , 1 , OpStackMain{ 1 } } );
|
infos.add( E_OpType::OP_DUP , T_OpInfo{ "dup" , 1 , OpStackMain{ 1 } } );
|
||||||
//
|
//
|
||||||
infos.add( E_OpType::OP_LOAD , T_OpInfo{ "load" , 1 } );
|
infos.add( E_OpType::OP_LOAD , T_OpInfo{ "load" , 1 } );
|
||||||
infos.add( E_OpType::OP_SLOAD , T_OpInfo{ "load-stack" , 1 } );
|
infos.add( E_OpType::OP_SLOAD , T_OpInfo{ "load-stack" , 1 } );
|
||||||
|
@ -191,3 +191,344 @@ T_StringBuilder& ops::operator<<(
|
||||||
}
|
}
|
||||||
return sb;
|
return sb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*= EMULATOR ===================================================================*/
|
||||||
|
|
||||||
|
X_OpFailure::X_OpFailure(
|
||||||
|
T_Op const& op ,
|
||||||
|
T_String error ) noexcept
|
||||||
|
: op_( &op ) , error_( std::move( error ) )
|
||||||
|
{
|
||||||
|
T_StringBuilder sb;
|
||||||
|
sb << "operation (" << op << ") failed; source: " << op.location
|
||||||
|
<< '\0';
|
||||||
|
fullMessage_ = std::move( sb );
|
||||||
|
}
|
||||||
|
|
||||||
|
char const* X_OpFailure::what( ) const noexcept
|
||||||
|
{
|
||||||
|
return fullMessage_.data( );
|
||||||
|
}
|
||||||
|
|
||||||
|
/*------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
T_OpContext::T_OpContext(
|
||||||
|
T_OpProgram& program ) noexcept
|
||||||
|
: program( program )
|
||||||
|
{
|
||||||
|
stack.ensureCapacity( stack.growth( ) );
|
||||||
|
|
||||||
|
const auto nc{ program.constants.size( ) };
|
||||||
|
const auto ts{ 3 + nc + program.nVariables
|
||||||
|
+ program.nPrograms + program.nPipelines
|
||||||
|
+ program.nSamplers + program.nTextures };
|
||||||
|
values.resize( ts );
|
||||||
|
|
||||||
|
for ( auto i = 0u ; i < nc ; i ++ ) {
|
||||||
|
values[ i + 3 ] = program.constants[ nc ];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void T_OpContext::run(
|
||||||
|
const E_RunTarget target ,
|
||||||
|
const float time ,
|
||||||
|
const float width ,
|
||||||
|
const float height )
|
||||||
|
{
|
||||||
|
assert( !aborted );
|
||||||
|
x87sp = 0;
|
||||||
|
instrPtr = program.ops.firstOf( target == R_INIT ? program.init : program.frame );
|
||||||
|
values[ 0 ] = time;
|
||||||
|
values[ 1 ] = width;
|
||||||
|
values[ 2 ] = height;
|
||||||
|
stack.clear( );
|
||||||
|
stack.add( 0xffffffff );
|
||||||
|
|
||||||
|
while ( 0 /* FIXME */ ) {
|
||||||
|
// if ( instrPtr >= program.ops.
|
||||||
|
auto const& instr{ program.ops[ instrPtr ] };
|
||||||
|
|
||||||
|
switch ( instr.op ) {
|
||||||
|
|
||||||
|
case OP_END:
|
||||||
|
throw X_OpFailure{ instr , "invalid instruction" };
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
case OP_CALL:
|
||||||
|
if ( program.ops.size( ) <= instr.args[ 0 ] ) {
|
||||||
|
throw X_OpFailure{ instr , "invalid function index" };
|
||||||
|
}
|
||||||
|
stack.add( instrPtr + 1 );
|
||||||
|
instrPtr = program.ops.firstOf( instr.args[ 0 ] );
|
||||||
|
continue;
|
||||||
|
|
||||||
|
case OP_RET:
|
||||||
|
ensureStack( instr , 1 + instr.args[ 0 ] );
|
||||||
|
instrPtr = stack.last( ).u;
|
||||||
|
stack.resize( stack.size( ) - instr.args[ 0 ] - 1 );
|
||||||
|
continue;
|
||||||
|
|
||||||
|
case OP_SKIP:
|
||||||
|
// FIXME: make sure we don't go past the end
|
||||||
|
instrPtr += instr.args[ 0 ];
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OP_COND_SKIP:
|
||||||
|
ensureStack( instr , 1 );
|
||||||
|
if ( stack.last( ).u != instr.args[ 1 ] ) {
|
||||||
|
// FIXME: make sure we don't go past the end
|
||||||
|
instrPtr += instr.args[ 0 ];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
case OP_RES_STACK:
|
||||||
|
stack.resize( stack.size( ) + instr.args[ 0 ] );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OP_PUSH:
|
||||||
|
stack.add( wreg );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OP_POP:
|
||||||
|
ensureStack( instr , instr.args[ 0 ] + 1 );
|
||||||
|
stack.resize( stack.size( ) - instr.args[ 0 ] - 1 );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OP_DUP:
|
||||||
|
ensureStack( instr , instr.args[ 0 ] + 1 );
|
||||||
|
stack.add( stack[ stack.size( ) - instr.args[ 0 ] - 1 ] );
|
||||||
|
break;
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
case OP_LOAD:
|
||||||
|
checkAddress( instr , instr.args[ 0 ] );
|
||||||
|
wreg = values[ instr.args[ 0 ] ];
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OP_SLOAD:
|
||||||
|
ensureStack( instr , instr.args[ 0 ] + 1 );
|
||||||
|
wreg = stack[ stack.size( ) - instr.args[ 0 ] - 1 ];
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OP_CONST:
|
||||||
|
wreg = instr.args[ 0 ];
|
||||||
|
break;
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
case OP_FP_LOAD:
|
||||||
|
ensureFpuStack( instr , 0 , 1 );
|
||||||
|
checkAddress( instr , instr.args[ 0 ] );
|
||||||
|
x87stack[ x87sp ++ ] = values[ instr.args[ 0 ] ].f;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OP_FP_STORE:
|
||||||
|
ensureFpuStack( instr , 1 , 0 );
|
||||||
|
checkAddress( instr , instr.args[ 0 ] );
|
||||||
|
values[ instr.args[ 0 ] ].f = x87stack[ -- x87sp ];
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OP_FP_SLOAD:
|
||||||
|
ensureFpuStack( instr , 0 , 1 );
|
||||||
|
ensureStack( instr , instr.args[ 0 ] + 1 );
|
||||||
|
x87stack[ x87sp ++ ] = stack[ stack.size( ) - instr.args[ 0 ] - 1 ].f;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OP_FP_SSTORE:
|
||||||
|
ensureFpuStack( instr , 1 , 0 );
|
||||||
|
ensureStack( instr , instr.args[ 0 ] + 1 );
|
||||||
|
stack[ stack.size( ) - instr.args[ 0 ] - 1 ].f = x87stack[ -- x87sp ];
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OP_FP_SSTORE_INT:
|
||||||
|
ensureFpuStack( instr , 1 , 0 );
|
||||||
|
ensureStack( instr , instr.args[ 0 ] + 1 );
|
||||||
|
stack[ stack.size( ) - instr.args[ 0 ] - 1 ].i = int32_t( x87stack[ -- x87sp ] );
|
||||||
|
break;
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
case OP_FP_CMP:
|
||||||
|
{
|
||||||
|
ensureFpuStack( instr , 2 , 0 );
|
||||||
|
const auto v2( x87stack[ x87sp - 1 ] ) ,
|
||||||
|
v1( x87stack[ x87sp - 2 ] );
|
||||||
|
x87sp --;
|
||||||
|
|
||||||
|
const bool rv{ ([&]( ) -> bool {
|
||||||
|
switch ( instr.args[ 0 ] ) {
|
||||||
|
case 0: return v1 == v2;
|
||||||
|
case 1: return v1 != v2;
|
||||||
|
case 2: return v1 > v2;
|
||||||
|
case 3: return v1 >= v2;
|
||||||
|
case 4: return v1 < v2;
|
||||||
|
case 5: return v1 <= v2;
|
||||||
|
}
|
||||||
|
throw X_OpFailure( instr , "invalid operation" );
|
||||||
|
})() };
|
||||||
|
x87stack[ x87sp - 1 ] = rv ? 1 : 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OP_FP_ADD:
|
||||||
|
{
|
||||||
|
ensureFpuStack( instr , 2 , 0 );
|
||||||
|
x87stack[ x87sp - 2 ] += x87stack[ x87sp - 1 ];
|
||||||
|
x87sp --;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OP_FP_SUB:
|
||||||
|
{
|
||||||
|
ensureFpuStack( instr , 2 , 0 );
|
||||||
|
x87stack[ x87sp - 2 ] -= x87stack[ x87sp - 1 ];
|
||||||
|
x87sp --;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OP_FP_MUL:
|
||||||
|
{
|
||||||
|
ensureFpuStack( instr , 2 , 0 );
|
||||||
|
x87stack[ x87sp - 2 ] *= x87stack[ x87sp - 1 ];
|
||||||
|
x87sp --;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OP_FP_DIV:
|
||||||
|
{
|
||||||
|
ensureFpuStack( instr , 2 , 0 );
|
||||||
|
if ( x87stack[ x87sp - 1 ] == 0 ) {
|
||||||
|
throw X_OpFailure{ instr , "arithmetic error" };
|
||||||
|
}
|
||||||
|
x87stack[ x87sp - 2 ] *= x87stack[ x87sp - 1 ];
|
||||||
|
x87sp --;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OP_FP_POW:
|
||||||
|
{
|
||||||
|
ensureFpuStack( instr , 2 , 0 );
|
||||||
|
x87stack[ x87sp - 2 ] = pow( x87stack[ x87sp - 2 ] ,
|
||||||
|
x87stack[ x87sp - 1 ] );
|
||||||
|
x87sp --;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
case OP_FP_NEG:
|
||||||
|
{
|
||||||
|
ensureFpuStack( instr , 1 , 1 );
|
||||||
|
x87stack[ x87sp - 1 ] = -x87stack[ x87sp - 1 ];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OP_FP_INV:
|
||||||
|
{
|
||||||
|
ensureFpuStack( instr , 1 , 1 );
|
||||||
|
if ( x87stack[ x87sp - 1 ] == 0 ) {
|
||||||
|
throw X_OpFailure{ instr , "arithmetic error" };
|
||||||
|
}
|
||||||
|
x87stack[ x87sp - 1 ] = 1.0 / x87stack[ x87sp - 1 ];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OP_FP_NOT:
|
||||||
|
{
|
||||||
|
ensureFpuStack( instr , 1 , 0 );
|
||||||
|
x87stack[ x87sp - 1 ] = x87stack[ x87sp - 1 ] == 0 ? 1 : 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OP_FP_SIN:
|
||||||
|
{
|
||||||
|
ensureFpuStack( instr , 1 , 0 );
|
||||||
|
x87stack[ x87sp - 1 ] = sin( x87stack[ x87sp - 1 ] );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OP_FP_COS:
|
||||||
|
{
|
||||||
|
ensureFpuStack( instr , 1 , 0 );
|
||||||
|
x87stack[ x87sp - 1 ] = cos( x87stack[ x87sp - 1 ] );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OP_FP_TAN:
|
||||||
|
{
|
||||||
|
ensureFpuStack( instr , 1 , 1 );
|
||||||
|
const auto c( cos( x87stack[ x87sp - 1 ] ) );
|
||||||
|
if ( c == 0 ) {
|
||||||
|
throw X_OpFailure{ instr , "arithmetic error" };
|
||||||
|
}
|
||||||
|
x87stack[ x87sp - 1 ] = sin( x87stack[ x87sp - 1 ] ) / c;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OP_FP_SQRT:
|
||||||
|
{
|
||||||
|
ensureFpuStack( instr , 1 , 0 );
|
||||||
|
const auto v( x87stack[ x87sp - 1 ] );
|
||||||
|
if ( v < 0 ) {
|
||||||
|
throw X_OpFailure{ instr , "arithmetic error" };
|
||||||
|
}
|
||||||
|
x87stack[ x87sp - 1 ] = sqrt( v );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OP_FP_EXP:
|
||||||
|
{
|
||||||
|
ensureFpuStack( instr , 1 , 0 ); // FIXME: need FPU stack space
|
||||||
|
x87stack[ x87sp - 1 ] = exp( x87stack[ x87sp - 1 ] );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OP_FP_LN:
|
||||||
|
{
|
||||||
|
ensureFpuStack( instr , 1 , 0 ); // FIXME: need FPU stack space
|
||||||
|
x87stack[ x87sp - 1 ] = log( x87stack[ x87sp - 1 ] );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
instrPtr ++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void T_OpContext::ensureStack(
|
||||||
|
T_Op const& op ,
|
||||||
|
const uint32_t min )
|
||||||
|
{
|
||||||
|
if ( stack.size( ) < min ) {
|
||||||
|
throw X_OpFailure{ op , "stack underrun" };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void T_OpContext::ensureFpuStack(
|
||||||
|
T_Op const& op ,
|
||||||
|
const uint32_t minStacked ,
|
||||||
|
const uint32_t minFree )
|
||||||
|
{
|
||||||
|
if ( x87sp < minStacked ) {
|
||||||
|
throw X_OpFailure{ op , "FPU stack underrun" };
|
||||||
|
}
|
||||||
|
if ( 8 - x87sp < minFree ) {
|
||||||
|
throw X_OpFailure{ op , "FPU stack overflow" };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void T_OpContext::checkAddress(
|
||||||
|
T_Op const& op ,
|
||||||
|
const uint32_t address )
|
||||||
|
{
|
||||||
|
if ( address >= values.size( ) ) {
|
||||||
|
throw X_OpFailure( op , "invalid access" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue