From 512c981402570b0c736858c1f75fd6a7bf050b83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emmanuel=20Beno=C3=AEt?= Date: Tue, 14 Nov 2017 12:49:59 +0100 Subject: [PATCH] Control - Implemented all basic opcodes + memory access + flow control + FPU ops --- control.hh | 51 ++++++-- ops.cc | 343 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 381 insertions(+), 13 deletions(-) diff --git a/control.hh b/control.hh index a78e204..b147437 100644 --- a/control.hh +++ b/control.hh @@ -112,6 +112,10 @@ union T_OpValue int32_t i; uint32_t u; 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 @@ -136,18 +140,49 @@ struct 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 { T_OpProgram& program; // The program 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 > stack; // Main VM stack + T_OpValue wreg; // Work register 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 @@ -157,7 +192,8 @@ class X_OpFailure : public std::exception DEF_COPY( X_OpFailure ); DEF_MOVE( X_OpFailure ); - X_OpFailure( T_Op const& op , + X_OpFailure( + T_Op const& op , T_String error ) noexcept; T_Op const& op( ) const noexcept @@ -173,15 +209,6 @@ class X_OpFailure : public std::exception T_String fullMessage_; }; -class T_Compiler : public A_PrivateImplementation -{ - public: - T_Compiler( ) noexcept; - - P_OpProgram compile( - opast::T_ParserOutput const& input ) noexcept; -}; - } // namespace ops diff --git a/ops.cc b/ops.cc index 6329939..4ca01bb 100644 --- a/ops.cc +++ b/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_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{ "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_SLOAD , T_OpInfo{ "load-stack" , 1 } ); @@ -191,3 +191,344 @@ T_StringBuilder& ops::operator<<( } 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" ); + } +}