#include #include #include #include #include #include #ifndef DIVINE_TIMED_EVAL_H #define DIVINE_TIMED_EVAL_H namespace divine { namespace timed { class Evaluator { private: enum class Type { CLOCK, SCALAR, CHANNEL, ARRAY, PROCESS }; enum class PrefixType { CONSTANT, LOCAL, NONE }; struct VarData { Type type; PrefixType prefix; // offset gives the position in value vector of the variable, // or unique id of clock or channel int32_t offset; int elementsCount; std::pair< int, int > ranges; VarData( ) {} VarData( Type t, PrefixType p, int32_t off, const std::pair< int, int > r = std::make_pair( std::numeric_limits< int >::min(), std::numeric_limits< int >::max() ) ) : type( t ), prefix( p ), offset( off ), ranges( r ) {} VarData( Type t, PrefixType p, int off, int elC, const std::pair< int, int > r = std::make_pair( std::numeric_limits< int >::min(), std::numeric_limits< int >::max() ) ) : type( t ), prefix( p ), offset( off ), elementsCount( elC ), ranges( r ) {} bool inRange( int32_t value ) const { return value >= ranges.first && value <= ranges.second; } }; struct FuncData { // NOTE: _fun_ has to be a pointer as function_t does not copy instances properly const UTAP::function_t *fun; FuncData( ) {} FuncData( const UTAP::function_t &f ) : fun ( &f ) {} }; struct Value { private: enum Kind { Int, Array }; const Kind kind; // used for values not residing in the "memory" const int32_t num; const struct Array { // base_ptr is nullptr for clocks or channels and it points to // the first element of underlying array/record // otherwise int32_t *base_ptr; // index is equal to the offset for channel or clocks expressions // and to the offset *inside* the top-level array of integers // (i.e. for a[1][1], where type of _a_ is int[2][3], // index==4) int index; // the "type" of this value; number of elements in the // value, e.g. for simple integer len==1, for int[2][3] len==6 int len; } _array; public: const struct Array& array() { ASSERT( kind == Array ); return _array; } const struct Array& array() const { ASSERT( kind == Array ); return _array; } Value( int32_t val ) : kind( Int ), num( val ), _array( { nullptr, 0, 0 } ) {} Value( int32_t *ptr, int i, int l ) : kind( Array ), num( 0 ), _array( { ptr, i, l } ) {} int32_t get_int() const { ASSERT( kind == Int || ( kind == Array && array().len == 1 ) ); if ( kind == Int ) return num; else return array().base_ptr[ array().index ]; } }; struct NamedInstance : public UTAP::instance_t { std::string name; NamedInstance( const UTAP::instance_t& inst, const std::string& n ) : UTAP::instance_t( inst ), name( n ) {} }; int getRecordElementIndex( int procId, const UTAP::type_t record_type, unsigned field_index ); int getElementCount( int procId, const UTAP::type_t t ); const VarData &getVarData( int procId, const UTAP::symbol_t & s ) const; const VarData &getVarData( int procId, UTAP::expression_t expr ); const FuncData &getFuncData( int procId, const UTAP::symbol_t & s ) const; Clocks clocks; typedef uint32_t ura_id; typedef std::pair< UTAP::symbol_t, int > VariableIdentifier; typedef std::map< VariableIdentifier, VarData > SymTable; typedef std::map< VariableIdentifier, FuncData > FuncTable; SymTable vars; FuncTable funs; int32_t *data = NULL; int32_t *ura_id_ptr = NULL; bool extrapLU = true; bool extrapLB = true; bool extrapDiag = true; std::vector< int32_t > initValues; std::vector< int32_t > metaValues; // constants + local variables std::vector< NamedInstance > ProcessTable; std::vector< const UTAP::symbol_t* > ClockTable; std::vector< const UTAP::symbol_t* > ChannelTable; std::vector< int > ChannelPriorities; Locations locations; void computeLocalBounds(); void assign( const UTAP::expression_t& lexp, const Value &val, int pId ) { assign( lexp, val.get_int(), pId ); } void assign( const UTAP::expression_t& lexp, int32_t val, int pId ); void assign( const UTAP::expression_t& lexp, const UTAP::expression_t& rexp, int pId ); static UTAP::type_t getBasicType( const UTAP::type_t &type ); void parseArrayValue( const UTAP::expression_t &exp, std::vector< int32_t > &output, int procId ) ; void processFunctionDecl( const UTAP::function_t &f, int procId ); void processSingleDecl( const UTAP::symbol_t &s, const UTAP::expression_t &initializer, int procId, bool local = false ); Value unop( int procId, const UTAP::Constants::kind_t op, const UTAP::expression_t &a ); Value binop( int procId, const UTAP::Constants::kind_t &op, const UTAP::expression_t &a, const UTAP::expression_t &b ); Value ternop( int procId, const UTAP::Constants::kind_t op, const UTAP::expression_t &a, const UTAP::expression_t &b, const UTAP::expression_t &c ); int32_t *getValue( const VarData &var ); int32_t *getValue( int procId, const UTAP::symbol_t& s ); bool inRange( int procId, const UTAP::symbol_t& s, int32_t value ) const; std::pair < int32_t, int32_t > getRange( int procId, const UTAP::expression_t &expr ); void collectResets( int pId, const UTAP::expression_t &e, std::vector< bool > & out ); typedef std::vector< std::pair< int32_t, int32_t > > limitsVector; // location based clock bounds std::vector< std::map< int32_t, limitsVector> > locClockLimits; // clock bounds from diagonals and property std::vector< std::pair< int32_t, int32_t > > fixedClockLimits; // other bounds std::vector< std::pair< int32_t, int32_t > > generalClockLimits; void computeLocClockLimits(); void computeChannelPriorities( UTAP::TimedAutomataSystem &sys ); void finalize(); struct StatementInfo { const UTAP::Statement *stmt; int iterCount; StatementInfo( ) { } StatementInfo( const UTAP::Statement* s, int ic = 0 ) : stmt( s ), iterCount( ic ) {} }; void pushStatements( int, std::vector< StatementInfo > &, StatementInfo ); void emitError( int code ) __attribute__((noreturn)); void setClockLimits( int procId, const UTAP::expression_t &exp, std::vector< std::pair< int32_t, int32_t > > & ); Value evalFunCall( int procId, const UTAP::expression_t &exp ); int resolveId( int procId, const UTAP::expression_t& expr ); public: Evaluator( ) {} Ura::ura ura; int &getUraStateId() { ASSERT( ura_id_ptr ); return *ura_id_ptr; } bool hasClocks() const { return !ClockTable.empty(); } int getUraStateId() const { ASSERT( ura_id_ptr ); return *ura_id_ptr; } void finalizeUra() { ura.finalize(); } const dbm::dbm_t getUraZone() const { ura_id uid = getUraStateId(); ASSERT( uid < ura.uppaal_dbm_rep.size() ); return ura.uppaal_dbm_rep[ uid ]; } // adds all clock comparisons to URA void parsePropClockGuard( const UTAP::expression_t &expr ); // returns mask of clocks that was reseted in _effect_ unsigned getResetedMask( int procId, const UTAP::expression_t effect ); // contains all inequalities involving clocks differences; filled by processDecl and setClockLimits; not internally used std::vector< Cut > clockDiffrenceExprs; // finalize zones void extrapolate(); // set data and locations to work on, resets error flag void setData( char *d, Locations ); // read global system declarations void processDeclGlobals( UTAP::TimedAutomataSystem &sys ); // read individual process instances; processDeclGlobals has to be called first void processDecl( const std::vector< UTAP::instance_t > &procs ); // evaluate expression and return its value Value eval( int procId, const UTAP::expression_t& expr ); // run command void evalCmd( int procId, const UTAP::expression_t& expr ); // read inequalities from an expression and update global clock limits if necessary; used for properties void setClockLimits( int procId, const UTAP::expression_t &exp ); // evaluate boolean expression bool evalBool( int procId, const UTAP::expression_t& expr ) { ASSERT( (computeLocalBounds(), true) ); // computeLocalBounds() has to be called so ASSERTs in Clocks::constrain* work return eval( procId, expr ).get_int(); } // set variables and clocks to their initial values void initial() { memcpy( data, &initValues[0], initValues.size() * sizeof(int32_t) ); clocks.initial(); ura_id_ptr = reinterpret_cast< int32_t * >( reinterpret_cast< char * >( data ) + getReqSize() - sizeof( int32_t ) ); getUraStateId() = 0; } friend std::ostream& operator<<( std::ostream& o, Evaluator& e ); // perform clock "up" operation (release upper constraints) void up() { ASSERT( !clocks.isEmpty() ); clocks.up(); } Federation getFederation() const { return clocks.createFed(); } Federation zoneIntersection( Federation fed ) const { return clocks.intersection( fed ); } dbm::dbm_t dbmIntersection( dbm::dbm_t d ) const { return clocks.intersection( d ); } void assignZone( const raw_t* src ) { clocks.assignZone( src ); } void assignZone( const dbm::dbm_t &src ) { clocks.assignZone( src ); } // return number of bytes required to store variable values and clock zone unsigned int getReqSize() const { return initValues.size() * sizeof(int32_t) + clocks.getReqSize() + sizeof(ura_id); } // returns range of the given type std::pair< int, int > evalRange( int procId, const UTAP::type_t &type ); int evalChan( int procId, const UTAP::expression_t& expr ) { // evaluate expression and return unique channel identifier that can be used // to call isChanUrgent, isChanBcast and getChanPriority ASSERT( expr.getType().is( UTAP::Constants::CHANNEL ) ); return resolveId( procId, expr ); } bool isChanUrgent( int ch ) const; bool isChanBcast( int ch ) const; int getChanPriority( int ch ) const { return ChannelPriorities[ ch ]; }; std::string getProcessName( int p ) const { if ( p == -1 ) return ""; else return ProcessTable[p].name; } void addSymbol( const UTAP::symbol_t &s, int procId = -1, const UTAP::expression_t &init = UTAP::expression_t() ) { processSingleDecl( s, init, procId ); finalize(); } void enableLU( bool enable ) { extrapLU = enable; } bool usesLU() const { return extrapLU; } void enableLB( bool enable ) { extrapLB = enable; } bool usesLB() const { return extrapLB; } void enableDiag( bool enable ) { extrapDiag = enable; } bool usesDiag() const { return extrapDiag; } // returns DBM offset and size of each row std::pair< unsigned int, unsigned int > splitPoints() const { unsigned int row = ClockTable.size() + 1; ASSERT( clocks.getReqSize() == row * row * 4 ); return std::make_pair( getReqSize() - clocks.getReqSize(), row * 4 ); } }; } } #endif