#include #include #include #include "clocks.h" #include "eval.h" #include #include #include using namespace UTAP; using namespace UTAP::Constants; using namespace std; namespace divine { namespace timed { std::pair< int, int > Evaluator::evalRange( int procId, const UTAP::type_t &type ) { auto range = type.getRange(); return make_pair( eval( procId, range.first ).get_int(), eval( procId, range.second ).get_int() ); } void Evaluator::computeChannelPriorities( UTAP::TimedAutomataSystem &sys ) { ChannelPriorities.resize( ChannelTable.size(), sys.getTauPriority() ); for ( auto& ch : sys.getChanPriorities() ) { if ( ch.chanElement.getType().isArray() ) { const Value ch_val = eval( -1, ch.chanElement ); unsigned int begin = ch_val.array().index; unsigned int end = begin + ch_val.array().len; for ( unsigned int i = begin; i < end; ++i ) { ChannelPriorities[ i ] = ch.chanPriority; } } else { ChannelPriorities[ evalChan( -1, ch.chanElement ) ] = ch.chanPriority; } } } void Evaluator::emitError( int code ) { ASSERT_NEQ( code, 0 ); throw EvalError( code ); } UTAP::type_t Evaluator::getBasicType( const UTAP::type_t &type ) { UTAP::type_t type_tmp = type; while ( type_tmp.isArray() || type_tmp.isRecord() || type_tmp.is( REF ) ) { type_tmp = type_tmp[0]; } return type_tmp; } int Evaluator::getRecordElementIndex( int procId, const type_t record_type, unsigned field_index ) { int index = 0; ASSERT( record_type.isRecord() ); ASSERT_LEQ( field_index, record_type.getRecordSize() - 1 ); for ( unsigned i = 0; i < field_index; ++i ) index += getElementCount( procId, record_type.getSub( i ) ); return index; } int Evaluator::getElementCount( int procId, const type_t t ) { if ( t.getKind() == REF ) return getElementCount( procId, t[0] ); else if ( t.getKind() == ARRAY ) { int cell_element_count = eval( procId, t.getArraySize().getRange().second ).get_int() + 1; return getElementCount( procId, t.getSub() ) * cell_element_count; } else if ( t.isRecord() ) { int size = 0; for ( unsigned i = 0; i < t.getRecordSize(); ++i ) size += getElementCount( procId, t.getSub( i ) ); return size; } else return 1; } const Evaluator::VarData &Evaluator::getVarData( int procId, const UTAP::symbol_t & s ) const { auto var = vars.find( make_pair( s, procId ) ); // local if ( var == vars.end() ) var = vars.find( make_pair( s, -1 ) ); // global ASSERT( var != vars.end() ); return var->second; } const Evaluator::VarData &Evaluator::getVarData( int procId, UTAP::expression_t expr ) { while ( expr.getKind() == ARRAY || expr.getKind() == DOT ) expr = expr[ 0 ]; if ( expr.getKind() == DOT ) { ASSERT_EQ( procId, -1 ); procId = resolveId( -1, expr[0] ); auto proc = static_cast< const instance_t* >( expr[0].getSymbol().getData() ); // get instance symbol_t symbol = proc->templ->frame[ expr.getIndex() ]; // get referenced symbol auto var = vars.find( make_pair( symbol, procId ) ); ASSERT( var != vars.end() ); return var->second; } ASSERT_EQ( expr.getKind(), IDENTIFIER ); return getVarData( procId, expr.getSymbol() ); } const Evaluator::FuncData &Evaluator::getFuncData( int procId, const UTAP::symbol_t & s ) const { auto fun = funs.find( make_pair( s, procId ) ); // local if ( fun == funs.end() ) fun = funs.find( make_pair( s, -1 ) ); // global ASSERT( fun != funs.end() ); return fun->second; } void Evaluator::parsePropClockGuard( const UTAP::expression_t &expr ) { if ( expr.getSize() != 2 ) return; const UTAP::expression_t &a = expr[ 0 ]; const UTAP::expression_t &b = expr[ 1 ]; if ( expr.getKind() == UTAP::Constants::AND ) { parsePropClockGuard( a ); parsePropClockGuard( b ); return; } bool open; switch ( expr.getKind() ) { case UTAP::Constants::GT: case UTAP::Constants::LT: open = false; break; case UTAP::Constants::GE: case UTAP::Constants::LE: open = true; break; default: ASSERT_UNREACHABLE_F( "unexpected expr kind %d", expr.getKind() ); } if ( a.getType().isClock() || b.getType().isClock() ) { //enableLU( false ); //enableLB( false ); //enableDiag( false ); int clock_id, value; if ( a.getType().isClock() ) { clock_id = resolveId( -1, a ); value = eval( -1, b ).get_int(); } else { clock_id = resolveId( -1, b ); value = eval( -1, a ).get_int(); } bool lessthan = expr.getKind() == UTAP::Constants::LT || UTAP::Constants::LE; if ( lessthan == a.getType().isClock() ) ura.add_guard( clock_id, value, open ); } else if ( a.getType().isDiff() || b.getType().isDiff() ) { ASSERT_UNIMPLEMENTED(); // diagonal constraints in the property are not supported yet } } unsigned Evaluator::getResetedMask( int procId, const UTAP::expression_t effect ) { std::vector< bool > resets; collectResets( procId, effect, resets ); unsigned reseted_mask = 0; int mask = 1; for ( unsigned i = 0; i < resets.size(); ++i, mask <<= 1 ) { if ( resets[ i ] ) reseted_mask |= mask; } return reseted_mask; } void Evaluator::parseArrayValue( const expression_t &exp, vector< int32_t > &output, int procId ) { if ( exp.getType().isArray() || exp.getType().isRecord() ) { if ( exp.getKind() == LIST ) { int arraySize; if ( exp.getType().isArray() ) arraySize = eval( procId, exp.getType().getArraySize().getRange().second ).get_int() + 1; else arraySize = exp.getType().getRecordSize(); for ( int i = 0; i < arraySize; i++ ) parseArrayValue( exp[i], output, procId ); } else { Value init_val = eval( procId, exp ); int32_t *array = init_val.array().base_ptr + init_val.array().index; for ( int i = 0; i < init_val.array().len; ++i ) output.push_back( array[ i ] ); } } else { output.push_back( eval( procId, exp ).get_int() ); } } void Evaluator::processFunctionDecl( const function_t &f, int procId ) { funs[ make_pair( f.uid, procId ) ] = FuncData( f ); } void Evaluator::processSingleDecl( const symbol_t &s, const expression_t &initializer, int procId, bool local ) { if ( s.getType().is( META ) ) local = true; const type_t& type = s.getType(); const type_t& basicType = getBasicType( type ); PrefixType variablePrefix = local ? PrefixType::LOCAL : PrefixType::NONE; if ( type.is( REF ) && !basicType.is( CONSTANT ) ) { symbol_t ref_symbol = initializer.getSymbol(); VarData new_vardata( getVarData( procId, ref_symbol ) ); if ( ref_symbol.getType().isArray() && initializer.getKind() != IDENTIFIER ) { Value init_val = eval( procId, initializer ); new_vardata.elementsCount = init_val.array().len; const type_t &init_basic_type = getBasicType( initializer.getType() ); if ( init_basic_type.isClock() || init_basic_type.isChannel() || init_basic_type.isProcess() ) new_vardata.offset = init_val.array().index; else new_vardata.offset += init_val.array().index; } // else nothing vars[ make_pair( s, procId ) ] = new_vardata; } else if ( basicType.isChannel() ) { if ( type.isArray() ) { int size = getElementCount( procId, type ); vars[ make_pair( s, procId) ] = VarData ( Type::CHANNEL, variablePrefix, ChannelTable.size(), size ); for ( int i = 0; i < size; i++ ) ChannelTable.push_back( &s ); } else { vars[ make_pair(s, procId) ] = VarData( Type::CHANNEL, variablePrefix, ChannelTable.size() ); ChannelTable.push_back( &s ); } } else if ( basicType.isClock() ) { if ( s.getName() == "t(0)" ) // we dont need to save global timer return; std::string basename = ( procId < 0 ? "" : getProcessName( procId ) + "." ) + s.getName(); if ( type.isArray() ) { int size = getElementCount( procId, type ); vars[ make_pair( s, procId) ] = VarData ( Type::CLOCK, variablePrefix, ClockTable.size(), size ); for ( int i = 0; i < size; i++ ) { std::stringstream ss( basename ); ss << "[" << i << "]"; clocks.setName( ClockTable.size(), ss.str() ); ClockTable.push_back( &s ); } } else { vars[ make_pair(s, procId) ] = VarData( Type::CLOCK, variablePrefix, ClockTable.size() ); clocks.setName( ClockTable.size(), basename ); ClockTable.push_back( &s ); } } else { pair< int, int > ranges; if ( basicType.is( UTAP::Constants::BOOL ) ) ranges = make_pair( 0, 1 ); else if ( basicType.is( CONSTANT ) ) ranges = make_pair( numeric_limits< int >::min(), numeric_limits< int >::max() ); else ranges = evalRange( procId, basicType); int default_value; if ( ( ranges.first < 0 && ranges.second < 0 ) || ( ranges.first > 0 && ranges.second > 0 ) ) default_value = ranges.first; else default_value = 0; if ( type.isArray() || type.isRecord() ) { /* * ARRAY value */ PrefixType prefix = basicType.is( CONSTANT ) ? PrefixType::CONSTANT : variablePrefix; int size = getElementCount( procId, type ); if ( prefix == PrefixType::NONE ) { vars.insert( make_pair( make_pair( s, procId ), VarData( Type::ARRAY, prefix, initValues.size(), size, ranges ) ) ); if ( initializer.empty() ) initValues.insert( initValues.end(), size, default_value ); else parseArrayValue( initializer, initValues, procId ); } else { decltype( vars )::iterator it = vars.find( make_pair( s, procId ) ); if ( it == vars.end() ) { vars.insert( it, make_pair ( make_pair ( s, procId ), VarData( Type::ARRAY, prefix, metaValues.size(), size, ranges ) ) ); if ( initializer.empty() ) metaValues.insert( metaValues.end(), size, default_value ); else parseArrayValue( initializer, metaValues, procId ); } else { vector< int32_t > vals; if ( initializer.empty() ) vals.resize( size, default_value ); else parseArrayValue( initializer, vals, procId ); for ( int i = 0; i < size; i++ ) { if ( !inRange( procId, s, vals[ i ] ) ) { emitError( EvalError::OUT_OF_RANGE ); } metaValues[ i + it->second.offset ] = vals[ i ]; } } } } else { /* * SCALAR value */ ASSERT( basicType == type ); PrefixType prefix = type.is( CONSTANT ) ? PrefixType::CONSTANT : variablePrefix; int32_t value = initializer.empty() ? default_value : eval( procId, initializer ).get_int(); if ( prefix == PrefixType::NONE ) { vars.insert( make_pair( make_pair( s, procId ), VarData( Type::SCALAR, prefix, initValues.size(), ranges ) ) ); if ( !inRange( procId, s, value ) ) emitError( EvalError::OUT_OF_RANGE ); initValues.push_back( value ); } else { auto it = vars.find( make_pair( s, procId ) ); if ( it == vars.end() ) { vars.insert( it, make_pair( make_pair( s, procId), VarData( Type::SCALAR, prefix, metaValues.size(), ranges ) ) ); if ( !inRange( procId, s, value ) ) emitError( EvalError::OUT_OF_RANGE ); metaValues.push_back( value ); } else { if ( !inRange( procId, s, value ) ) emitError( EvalError::OUT_OF_RANGE ); metaValues[ it->second.offset ] = value; } } } } } Evaluator::Value Evaluator::unop( int procId, const Constants::kind_t op, const expression_t &a ) { int result; switch ( op ) { case UNARY_MINUS: return -eval( procId, a ).get_int(); case NOT: return !eval( procId, a ).get_int(); case PREINCREMENT: result = eval( procId, a ).get_int(); assign( a, result + 1, procId ); return result + 1; case POSTINCREMENT: result = eval( procId, a ).get_int(); assign( a, result + 1, procId ); return result; case PREDECREMENT: result = eval( procId, a ).get_int(); assign( a, result - 1, procId ); return result - 1; case POSTDECREMENT: result = eval( procId, a ).get_int(); assign( a, result - 1, procId ); return result; default: ASSERT_UNREACHABLE_F( "unexpected unary operator %d", op ); return 0; } } Evaluator::Value Evaluator::binop( int procId, const Constants::kind_t &op, const expression_t &a, const expression_t &b ) { if ( a.getType().isClock() || b.getType().isClock() ) { int clock_id, value; if ( a.getType().isClock() ) { clock_id = resolveId( procId, a ); value = eval( procId, b ).get_int(); switch ( op ) { case LT: return clocks.constrainBelow( clock_id, value, true ); case LE: return clocks.constrainBelow( clock_id, value, false ); case GT: return clocks.constrainAbove( clock_id, value, true ); case GE: return clocks.constrainAbove( clock_id, value, false ); case EQ: return clocks.constrainAbove( clock_id, value, false ) && clocks.constrainBelow( clock_id, value, false ); default: ASSERT_UNREACHABLE_F( "unexpected binary clock operation %d", op ); } } else { clock_id = resolveId( procId, b ); value = eval( procId, a ).get_int(); switch ( op ) { case GT: return clocks.constrainBelow( clock_id, value, true ); case GE: return clocks.constrainBelow( clock_id, value, false ); case LT: return clocks.constrainAbove( clock_id, value, true ); case LE: return clocks.constrainAbove( clock_id, value, false ); case EQ: return clocks.constrainAbove( clock_id, value, false ) && clocks.constrainBelow( clock_id, value, false ); default: ASSERT_UNREACHABLE_F( "unexpected binary clock operation %d", op ); } } } else if ( a.getType().isDiff() || b.getType().isDiff() ) { int clock_idL, clock_idR, value; if ( a.getType().isDiff() ) { clock_idL = resolveId( procId, a[0] ); clock_idR = resolveId( procId, a[1] ); value = eval( procId, b ).get_int(); switch ( op ) { case LT: return clocks.constrainClocks( clock_idL, clock_idR, value, true ); case LE: return clocks.constrainClocks( clock_idL, clock_idR, value, false ); case GT: return clocks.constrainClocks( clock_idR, clock_idL, -value, true ); case GE: return clocks.constrainClocks( clock_idR, clock_idL, -value, false ); case EQ: return clocks.constrainClocks( clock_idL, clock_idR, value, false ) && clocks.constrainClocks( clock_idR, clock_idL, -value, false ); default: ASSERT_UNREACHABLE_F( "unexpected binary clock operation %d", op ); } } else { clock_idL = resolveId( procId, b[0] ); clock_idR = resolveId( procId, b[1] ); value = eval( procId, a ).get_int(); switch ( op ) { case GT: return clocks.constrainClocks( clock_idL, clock_idR, value, true ); case GE: return clocks.constrainClocks( clock_idL, clock_idR, value, false ); case LT: return clocks.constrainClocks( clock_idR, clock_idL, -value, true ); case LE: return clocks.constrainClocks( clock_idR, clock_idL, -value, false ); case EQ: return clocks.constrainClocks( clock_idL, clock_idR, value, false ) && clocks.constrainClocks( clock_idR, clock_idL, -value, false ); default: ASSERT_UNREACHABLE_F( "unexpected binary clock operation %d", op ); } } } int32_t b_num; pair< const VarData*, int > p; switch ( op ) { case ASSPLUS: case PLUS: return eval( procId, a ).get_int() + eval( procId, b ).get_int(); case ASSMINUS: case MINUS: return eval( procId, a ).get_int() - eval( procId, b ).get_int(); case ASSMULT: case MULT: return eval( procId, a ).get_int() * eval( procId, b ).get_int(); case ASSDIV: case DIV: b_num = eval( procId, b ).get_int(); if ( b_num == 0 ) { emitError( EvalError::DIVIDE_BY_ZERO ); } return eval( procId, a ).get_int() / b_num; case ASSMOD: case MOD: b_num = eval( procId, b ).get_int(); if ( b_num == 0 ) { emitError( EvalError::DIVIDE_BY_ZERO ); } return eval( procId, a ).get_int() % b_num; case ASSAND: case BIT_AND: return eval( procId, a ).get_int() & eval( procId, b ).get_int(); case ASSOR: case BIT_OR: return eval( procId, a ).get_int() | eval( procId, b ).get_int(); case ASSXOR: case BIT_XOR: return eval( procId, a ).get_int() ^ eval( procId, b ).get_int(); case ASSLSHIFT: case BIT_LSHIFT: b_num = eval( procId, b ).get_int(); if ( (b_num & 31) != b_num ) emitError( EvalError::SHIFT ); return eval( procId, a ).get_int() << b_num; case ASSRSHIFT: case BIT_RSHIFT: b_num = eval( procId, b ).get_int(); if ( (b_num & 31) != b_num ) emitError( EvalError::SHIFT ); return eval( procId, a ).get_int() >> b_num; case AND: return eval( procId, a).get_int() && eval( procId, b).get_int(); case OR: return eval( procId, a).get_int() || eval( procId, b).get_int(); case LT: return eval( procId, a).get_int() < eval( procId, b).get_int(); case LE: return eval( procId, a).get_int() <= eval( procId, b).get_int(); case GT: return eval( procId, a).get_int() > eval( procId, b).get_int(); case GE: return eval( procId, a).get_int() >= eval( procId, b).get_int(); case EQ: return eval( procId, a).get_int() == eval( procId, b).get_int(); case NEQ: return eval( procId, a).get_int() != eval( procId, b).get_int(); case MAX: return max( eval( procId, a ).get_int(), eval( procId, b ).get_int() ); case MIN: return min( eval( procId, a ).get_int(), eval( procId, b ).get_int() ); case ARRAY: { const expression_t expr = expression_t::createBinary( ARRAY, a, b ); Value a_val = eval( procId, a ); Value index = eval( procId, b ); type_t a_elem_type = a.getType(); while( a_elem_type.getKind() == REF ) a_elem_type = a_elem_type[ 0 ]; a_elem_type = a_elem_type[ 0 ]; int factor = getElementCount( procId, a_elem_type ); return Value( a_val.array().base_ptr, a_val.array().index + index.get_int() * factor, factor ); } case FORALL: { bool ret = true; auto range = evalRange( procId, a.getType() ); for ( int val = range.first; val <= range.second; ++val ) { expression_t b_subs = b.subst( a.getSymbol(), UTAP::expression_t::createConstant( val ) ); ret = ret && eval( procId, b_subs ).get_int(); } return ret; } case EXISTS: { bool ret = false; auto range = evalRange( procId, a.getType() ); for ( int val = range.first; val <= range.second; ++val ) { expression_t b_subs = b.subst( a.getSymbol(), UTAP::expression_t::createConstant( val ) ); ret = ret || eval( procId, b_subs ).get_int(); } return ret; } default: ASSERT_UNREACHABLE_F( "unexpected clock operation %d", op ); } return -1; } Evaluator::Value Evaluator::ternop( int procId, const Constants::kind_t op, const expression_t &a, const expression_t &b, const expression_t &c ) { switch ( op ) { case INLINEIF: return eval( procId, a ).get_int() ? eval( procId, b ) : eval( procId, c ); default: ASSERT_UNREACHABLE_F( "unexpected ternary clock operation %d", op ); } } void Evaluator::assign( const expression_t& lval, int32_t val, int pId ) { Value l_evaluated = eval( pId, lval ); const VarData& var = getVarData( pId, lval ); if ( lval.getType().is( CLOCK ) ) // assign to clock clocks.set( l_evaluated.array().index, val ); else { // assign to variable if ( !var.inRange( val ) ) { emitError( EvalError::OUT_OF_RANGE ); } l_evaluated.array().base_ptr[ l_evaluated.array().index ] = val; } } void Evaluator::assign( const expression_t& lexp, const expression_t& rexp, int pId ) { if ( rexp.getType().isArray() || rexp.getType().isRecord() ) { // assign array to array Value dest = eval( pId, lexp ); int32_t *pDest = dest.array().base_ptr + dest.array().index; const VarData& var = getVarData( pId, lexp ); Value src = eval( pId, rexp ); int32_t *pSrc = src.array().base_ptr + src.array().index; int32_t *pEnd = pSrc + src.array().len; ASSERT_LEQ( pEnd - pSrc, var.elementsCount ); while ( pSrc != pEnd ) { if ( !var.inRange( *pSrc ) ) { emitError( EvalError::OUT_OF_RANGE ); } *pDest++ = *pSrc++; } } else { // assign to variable assign( lexp, eval( pId, rexp ), pId ); } } int32_t *Evaluator::getValue( const VarData &var ) { int32_t *data; if ( var.prefix == PrefixType::NONE ) { // get variable data = Evaluator::data; } else { // get constant or local variable ASSERT_LEQ( 1u, metaValues.size() ); data = &metaValues[0]; } ASSERT( data ); return data + var.offset; } int32_t *Evaluator::getValue( int procId, const symbol_t& s ) { const VarData& var = getVarData( procId, s ); return getValue( var ); } bool Evaluator::inRange( int procId, const symbol_t& s, int32_t value ) const { return getVarData( procId, s ).inRange( value ); } bool Evaluator::isChanUrgent( int ch ) const { return ChannelTable[ch]->getType().is( URGENT ); } bool Evaluator::isChanBcast( int ch ) const { return ChannelTable[ch]->getType().is( BROADCAST ); } void Evaluator::processDeclGlobals( UTAP::TimedAutomataSystem &sys ) { ASSERT( ProcessTable.empty() ); for ( const function_t &f : sys.getGlobals().functions ) // global functions processFunctionDecl( f, -1 ); for ( const variable_t &v : sys.getGlobals().variables ) { // global variables processSingleDecl( v.uid, v.expr, -1 ); } computeChannelPriorities( sys ); } void Evaluator::processDecl( const vector< instance_t > &procs ) { for ( size_t pId = 0; pId < procs.size(); ++pId ) { const instance_t &p = procs[pId]; stringstream ss; ss << p.uid.getName(); // save the symbol so it can be used in property auto type = p.uid.getType(); if ( type.isProcessSet() ) { // partial instances are represended as arrays of processes int size = 1; for ( size_t i = 0; i < type.size(); ++i ) { auto r = evalRange( -1, type[ i ] ); size *= r.second - r.first + 1; // build name for partial instance ss << ( i ? ',' : '[' ); ss << eval( -1, p.mapping.at( p.parameters[i] ) ).get_int(); } ss << ']'; // insert array of processes or do nothing if we have already seen this uid vars.insert( make_pair( make_pair( p.uid, -1 ), VarData( Type::PROCESS, PrefixType::NONE, pId, size ) ) ); } else { vars[ make_pair( p.uid, -1 ) ] = VarData ( Type::PROCESS, PrefixType::NONE, pId ); } ProcessTable.emplace_back( p, ss.str() ); // template parametres for ( const pair< symbol_t, expression_t > &m : p.mapping ) processSingleDecl( m.first, m.second, ProcessTable.size() - 1 ); // fuctions for ( const function_t &f : p.templ->functions ) processFunctionDecl( f, ProcessTable.size() - 1 ); // local variables ASSERT( p.templ ); for ( const variable_t &v : p.templ->variables ) processSingleDecl( v.uid, v.expr, ProcessTable.size() - 1 ); } finalize(); } void Evaluator::finalize() { // compute clock limits clocks.resize( ClockTable.size() ); ura.set_dimension( ClockTable.size() ); fixedClockLimits.resize( ClockTable.size(), make_pair( -dbm_INFINITY, -dbm_INFINITY ) ); generalClockLimits.resize( ClockTable.size(), make_pair( -dbm_INFINITY, -dbm_INFINITY ) ); computeLocClockLimits(); } // after collectResets, out[c] is true iff the clock _c_ is reseted on the edge _e_ void Evaluator::collectResets( int pId, const expression_t &e, vector< bool > & out ) { out.resize( ClockTable.size(), false ); expression_t tmp = e; while ( tmp.getKind() == COMMA ) { if ( tmp[0].getKind() == ASSIGN && tmp[0][0].getType().isClock() ) { out[ resolveId( pId, tmp[0][0] ) ] = true; } tmp = tmp[1]; } if ( tmp.getKind() == ASSIGN && tmp[0].getType().isClock() ) out[ resolveId( pId, tmp[0] ) ] = true; } void Evaluator::computeLocClockLimits() { locClockLimits.clear(); locClockLimits.resize( ProcessTable.size() ); int i = 0; for ( const instance_t &p : ProcessTable ) { for ( state_t &s : p.templ->states ) { setClockLimits( i, s.invariant, generalClockLimits ); limitsVector limits; limits.resize( ClockTable.size(), make_pair( -dbm_INFINITY, -dbm_INFINITY ) ); setClockLimits( i, s.invariant, limits ); locClockLimits[ i ][ s.locNr ] = limits; } for ( edge_t &e : p.templ->edges ) { setClockLimits( i, e.guard, generalClockLimits ); symbol_t src = e.src->uid; limitsVector &limits = locClockLimits[ i ][ e.src->locNr ]; ASSERT_EQ( limits.size(), ClockTable.size() ); setClockLimits( i, e.guard, limits ); } ++i; } bool change = true; while ( change ) { // bounds propagation change = false; i = 0; for ( const instance_t &p : ProcessTable ) { for ( edge_t &e : p.templ->edges ) { vector< bool > reseted; collectResets( i, e.assign, reseted ); for ( size_t clk = 0; clk < ClockTable.size(); clk++ ) { if ( reseted[ clk ] ) continue; auto& srcLim = locClockLimits[ i ][ e.src->locNr ][ clk ]; auto& dstLim = locClockLimits[ i ][ e.dst->locNr ][ clk ]; if ( srcLim.first < dstLim.first ) { srcLim.first = dstLim.first; change = true; } if ( srcLim.second < dstLim.second ) { srcLim.second = dstLim.second; change = true; } } } ++i; } } } void Evaluator::computeLocalBounds() { vector< pair< int32_t, int32_t > > bounds = fixedClockLimits; ASSERT_EQ( fixedClockLimits.size(), ClockTable.size() ); if ( usesLB() ) { for ( size_t proc = 0; proc < ProcessTable.size(); ++proc ) { for ( size_t clk = 0; clk < ClockTable.size(); ++clk ) { auto &b = locClockLimits[ proc ][ locations[ proc ] ][ clk ]; bounds[ clk ].first = max( b.first, bounds[ clk ].first ); bounds[ clk ].second = max( b.second, bounds[ clk ].second ); } } } else { for ( size_t clk = 0; clk < ClockTable.size(); ++clk ) { bounds[ clk ].first = max( bounds[ clk ].first, generalClockLimits[ clk ].first ); bounds[ clk ].second = max( bounds[ clk ].second, generalClockLimits[ clk ].second ); } } if ( extrapLU ) { for ( size_t clk = 0; clk < ClockTable.size(); ++clk ) { clocks.setUpperBound( clk, bounds[clk].first ); clocks.setLowerBound( clk, bounds[clk].second ); } } else { for ( size_t clk = 0; clk < ClockTable.size(); ++clk ) { int32_t maximum = max( bounds[ clk ].first, bounds[ clk ].second ); clocks.setUpperBound( clk, maximum ); clocks.setLowerBound( clk, maximum ); } } } void Evaluator::evalCmd( int procId, const expression_t& expr ) { if ( expr.getKind() == COMMA ) { evalCmd( procId, expr[0] ); evalCmd( procId, expr[1] ); return; } if ( expr.getSize() < 2 ) { eval( procId, expr ); return; } symbol_t dest_sym = expr[0].getSymbol(); if ( dest_sym.getType().isClock() ) { int value = eval( procId, expr[ 1 ] ).get_int(); if ( value < 0 ) { emitError( EvalError::NEG_CLOCK ); } clocks.set( resolveId( procId, expr[0]), value ); return; } switch ( expr.getKind() ) { case( ASSIGN ): assign( expr[0], expr[1], procId ); break; case( ASSPLUS ): case( ASSMINUS ): case( ASSDIV ): case( ASSMOD ): case( ASSMULT ): case( ASSAND ): case( ASSOR ): case( ASSXOR ): case( ASSLSHIFT ): case( ASSRSHIFT ): assign( expr[0], binop( procId, expr.getKind(), expr[ 0 ], expr[ 1 ] ), procId ); break; default: eval( procId, expr ); break; } } void Evaluator::pushStatements( int procId, vector< StatementInfo > &stack, StatementInfo block ) { const BlockStatement *blk_stmt = dynamic_cast< const BlockStatement* >( block.stmt ); if ( blk_stmt ) { for ( auto &var : blk_stmt->variables ) { processSingleDecl( var.uid, var.expr, procId, true ); } // NOTE: BlockStatement does not provide a size() method int blockSize = blk_stmt->end() - blk_stmt->begin(); stack.resize( stack.size() + blockSize ); reverse_copy( blk_stmt->begin(), blk_stmt->end(), stack.end() - blockSize ); } else { // special case when block consists of only one statement // // in this case the are no variables in the block stack.push_back( block ); } } Evaluator::Value Evaluator::evalFunCall( int procId, const UTAP::expression_t &exp ) { const symbol_t &sym_f = exp[0].getSymbol(); const FuncData &fdata = getFuncData( procId, sym_f ); ASSERT( fdata.fun ); int arity = fdata.fun->uid.getType().size() - 1; for ( int i = 0; i < arity; ++i ) { // parameters are stored in the frame and apparently nowhere else processSingleDecl( fdata.fun->body->getFrame()[i], exp[ i + 1 ], procId, true ); } for ( auto &var : fdata.fun->variables ) processSingleDecl( var.uid, var.expr, procId, true ); // stack of statements to be evaluated vector< StatementInfo > statementStack; statementStack.push_back( StatementInfo( fdata.fun->body, 0 ) ); while ( !statementStack.empty() ) { StatementInfo stmt_info = statementStack.back(); const Statement *stmt = stmt_info.stmt; statementStack.pop_back(); ASSERT( stmt ); if ( dynamic_cast< const EmptyStatement *>( stmt ) ) { } else if ( dynamic_cast< const ExprStatement* >( stmt ) ) { evalCmd( procId, dynamic_cast< const ExprStatement* >( stmt )->expr ); } else if ( dynamic_cast< const ForStatement* >( stmt ) ) { const ForStatement *for_stmt = dynamic_cast< const ForStatement* >( stmt ); if ( stmt_info.iterCount == 0 ) evalCmd( procId, for_stmt->init ); else evalCmd( procId, for_stmt->step ); if ( eval( procId, for_stmt->cond ).get_int() ) { ++stmt_info.iterCount; pushStatements( procId, statementStack, stmt_info ); pushStatements( procId, statementStack, StatementInfo( for_stmt->stat ) ); } } else if ( dynamic_cast< const IterationStatement* >( stmt ) ) { const IterationStatement *iter_stmt = dynamic_cast< const IterationStatement* >( stmt ); auto r = evalRange( procId, iter_stmt->symbol.getType() ); int range_len = r.second - r.first + 1; if ( stmt_info.iterCount < range_len ) { const VarData &var = getVarData( procId, iter_stmt->symbol ); if ( !inRange( procId, iter_stmt->symbol, stmt_info.iterCount + r.first ) ) { emitError( EvalError::OUT_OF_RANGE ); } metaValues[ var.offset ] = stmt_info.iterCount + r.first; ++stmt_info.iterCount; pushStatements( procId, statementStack, stmt_info ); pushStatements( procId, statementStack, StatementInfo( iter_stmt->stat ) ); } } else if ( dynamic_cast< const WhileStatement* >( stmt ) ) { const WhileStatement *while_stmt = dynamic_cast< const WhileStatement* >( stmt ); if ( eval( procId, while_stmt->cond).get_int() ) { ASSERT( while_stmt->stat ); ++stmt_info.iterCount; pushStatements( procId, statementStack, stmt_info ); pushStatements( procId, statementStack, StatementInfo( while_stmt->stat ) ); } } else if ( dynamic_cast< const DoWhileStatement* >( stmt ) ) { const DoWhileStatement *dowhile_stmt = dynamic_cast< const DoWhileStatement* >( stmt ); ++stmt_info.iterCount; ASSERT( dowhile_stmt->stat ); if ( stmt_info.iterCount == 0 || eval( procId, dowhile_stmt->cond ).get_int() ) { pushStatements( procId, statementStack, stmt_info ); pushStatements( procId, statementStack, StatementInfo( dowhile_stmt->stat ) ); } } else if ( dynamic_cast< const BlockStatement* >( stmt ) ) { const BlockStatement *blk = dynamic_cast< const BlockStatement* >( stmt ); pushStatements( procId, statementStack, StatementInfo( blk ) ); } else if ( dynamic_cast< const SwitchStatement* >( stmt ) ) { // UTAP does not know switch, case, default keywords ASSERT_UNREACHABLE( "Switch statement is not supported" ); } else if ( dynamic_cast< const CaseStatement* >( stmt ) ) { ASSERT_UNREACHABLE( "Case statement is not supported" ); } else if ( dynamic_cast< const DefaultStatement* >( stmt ) ) { ASSERT_UNREACHABLE( "Default statement is not supported" ); } else if ( dynamic_cast< const IfStatement* >( stmt ) ) { const IfStatement *eval_stmt = dynamic_cast< const IfStatement* >( stmt ); bool cond_value = eval( procId, eval_stmt->cond ).get_int(); if ( cond_value ) { ASSERT( eval_stmt->trueCase ); pushStatements( procId, statementStack, StatementInfo( eval_stmt->trueCase ) ); } else if ( eval_stmt->falseCase ) { ASSERT( eval_stmt->falseCase ); pushStatements( procId, statementStack, StatementInfo( eval_stmt->falseCase ) ); } } else if ( dynamic_cast< const BreakStatement* >( stmt ) ) { // NOTE: UTAP library declares BreakStatement and ContinueStatement, but UPPAAL does not // know break nor continue keyword... // delete current block + one more statement (while/for "header") ASSERT_UNREACHABLE( "Break statement is not supported" ); } else if ( dynamic_cast< const ContinueStatement* >( stmt ) ) { ASSERT_UNREACHABLE( "Continue statement is not supported" ); } else if ( dynamic_cast< const ReturnStatement* >( stmt ) ) { return eval( procId, dynamic_cast< const ReturnStatement* >( stmt )->value ); } else { ASSERT_UNREACHABLE( "Unknown statement type" ); } } return 0; } Evaluator::Value Evaluator::eval( int procId, const expression_t& expr ) { if ( expr.empty() ) return 1; if ( expr.getKind() == RECORD || expr.getKind() == LIST ) return 1; if ( expr.getKind() == FUNCALL ) return evalFunCall( procId, expr ); switch ( expr.getSize() ) { case 3: return ternop( procId, expr.getKind(), expr[0], expr[1], expr[2] ); case 2: return binop( procId, expr.getKind(), expr[0], expr[1] ); case 1: if ( expr.getKind() == DOT ) { if ( expr[0].getType().isRecord() ) { int index = getRecordElementIndex( procId, expr[0].getType(), expr.getIndex() ); Value s_val = eval( procId, expr[0] ); int elem_count = getElementCount( procId, expr.getType() ); return Value( s_val.array().base_ptr, s_val.array().index + index, elem_count ); } else { int pId = resolveId( -1, expr[0] ); auto proc = static_cast< const instance_t* >( expr[0].getSymbol().getData() ); // get instance symbol_t symb = proc->templ->frame[ expr.getIndex() ]; // get referenced symbol if ( symb.getType().isLocation() ) { auto location = static_cast< const state_t* >( symb.getData() ); // get location return locations[ pId ] == location->locNr; } else { return eval( pId, expression_t::createIdentifier( symb ) ); } } } return unop( procId, expr.getKind(), expr[0] ) ; case 0: switch ( expr.getKind() ) { case IDENTIFIER: { const type_t &basicType = getBasicType( expr.getType() ); if ( basicType.isClock() || basicType.isChannel() || basicType.isProcess() ) { int offset = getVarData( procId, expr.getSymbol() ).offset; return Value( nullptr, offset, 1 ); } else { int32_t *base_ptr = getValue( procId, expr.getSymbol() ); return Value( base_ptr, 0, getElementCount( procId, expr.getType() ) ); } } case CONSTANT: return expr.getValue(); default: ASSERT_UNREACHABLE_F( "unknown nullop %d", expr.getKind() ); } } ASSERT_UNREACHABLE_F( "unexpected expr size %d", expr.getSize() ); } void Evaluator::setData( char *d, Locations l ) { data = reinterpret_cast< int32_t* >( d ); clocks.setData( d + getReqSize() - clocks.getReqSize() - sizeof( ura_id ) ); ura_id_ptr = reinterpret_cast< int32_t* >( d + getReqSize() - sizeof( ura_id ) ); locations = l; } void Evaluator::extrapolate() { if ( usesLB() ) computeLocalBounds(); if ( usesLU() && usesDiag() ) clocks.extrapolate( ); else if ( !usesLU() && !usesDiag() ) clocks.extrapolateMaxBounds(); else if ( !usesLU() && usesDiag() ) clocks.extrapolateDiagonalMaxBounds(); else { ASSERT_UNREACHABLE( "unexpected extrapolation setup" ); } } /* * resolveId() */ int Evaluator::resolveId( int procId, const expression_t& expr ) { ASSERT( expr.getType().isChannel() || expr.getType().isClock() || expr.getType().isProcess() ); return eval( procId, expr ).array().index; } std::ostream& operator<<( std::ostream& o, Evaluator& e ) { for ( auto var : e.vars ) { if ( var.second.prefix == Evaluator::PrefixType::NONE ) { // VARIABLE if ( var.second.type == Evaluator::Type::SCALAR) { o << e.getProcessName( var.first.second ) << "."; o << var.first.first.getName() << " = " << *e.getValue( var.first.second, var.first.first ) << endl; } else if ( var.second.type == Evaluator::Type::ARRAY ) { for ( int i = 0; i < var.second.elementsCount; i++ ) { o << e.getProcessName( var.first.second ) << "."; o << var.first.first.getName() << "[" << i << "] = " << e.getValue( var.first.second, var.first.first )[ i ] << endl; } } } } o << e.clocks; o << "URA state: " << e.getUraZone() << std::endl; return o; } static bool isConstant( const expression_t &expr ) { set< symbol_t > reads; expr.collectPossibleReads( reads ); for ( const symbol_t &r : reads ) if ( !r.getType().isConstant() ) return false; return true; } void Evaluator::setClockLimits( int procId, const UTAP::expression_t &expr ) { setClockLimits( procId, expr, fixedClockLimits ); } void Evaluator::setClockLimits( int procId, const UTAP::expression_t &expr, vector< pair< int32_t, int32_t > > &out ) { if ( expr.empty() ) return; switch ( expr.getSize() ) { case 3: setClockLimits( procId, expr[0], out ); setClockLimits( procId, expr[1], out ); setClockLimits( procId, expr[2], out ); case 2: if ( ( expr[0].getKind() == ARRAY && expr[0].getType().isClock() ) || ( expr[1].getKind() == ARRAY && expr[1].getType().isClock() )) { int clock_index, limit_index; if ( expr[0].getType().isClock() ) { clock_index = 0; limit_index = 1; } else { clock_index = 1; limit_index = 0; } const VarData &var = getVarData( procId, expr[clock_index].getSymbol() ); int32_t limit = getRange( procId, expr[ limit_index ] ).second; for ( int clockIndex = var.offset; clockIndex < var.offset + var.elementsCount; ++clockIndex ) { ASSERT_LEQ( clockIndex, intptr_t( ClockTable.size() ) - 1 ); ASSERT_EQ( ClockTable.size(), out.size() ); if ( out[ clockIndex ].first < limit ) out[ clockIndex ].first = limit; if ( out[ clockIndex ].second < limit ) out[ clockIndex ].second = limit; } } else if ( ( expr[0].getType().isDiff() || expr[1].getType().isDiff() ) ) { addCut( expr, procId, clockDiffrenceExprs ); int32_t limit; int clockIndexl, clockIndexr; if ( expr[0].getType().isDiff() ) { limit = abs( getRange( procId, expr[ 1 ] ).second ); clockIndexl = resolveId( procId, expr[0][0] ); clockIndexr = resolveId( procId, expr[0][1] ); } else { limit = abs( getRange( procId, expr[ 0 ] ).second ); clockIndexl = resolveId( procId, expr[1][0] ); clockIndexr = resolveId( procId, expr[1][1] ); } if ( fixedClockLimits[ clockIndexl ].first < limit ) fixedClockLimits[ clockIndexl ].first = limit; if ( fixedClockLimits[ clockIndexr ].first < limit ) fixedClockLimits[ clockIndexr ].first = limit; if ( fixedClockLimits[ clockIndexl ].second < limit ) fixedClockLimits[ clockIndexl ].second = limit; if ( fixedClockLimits[ clockIndexr ].second < limit ) fixedClockLimits[ clockIndexr ].second = limit; } else if ( expr[0].getType().isClock() || expr[1].getType().isClock() ) { bool upper = expr.getKind() == LE || expr.getKind() == LT; int clockIndex; int32_t limit; if ( expr[1].getType().isClock() ) { clockIndex = resolveId( procId, expr[1] ); limit = abs( getRange( procId, expr[ 0 ] ).second ); upper = !upper; } else { clockIndex = resolveId( procId, expr[0] ); limit = abs( getRange( procId, expr[ 1 ] ).second ); } if ( expr.getKind() == EQ ) { if ( out[ clockIndex ].first < limit ) out[ clockIndex ].first = limit; if ( out[ clockIndex ].second < limit ) out[ clockIndex ].second = limit; } else if ( upper ) { if ( out[ clockIndex ].first < limit ) out[ clockIndex ].first = limit; } else { if ( out[ clockIndex ].second < limit ) out[ clockIndex ].second = limit; } } else { setClockLimits( procId, expr[0], out ); setClockLimits( procId, expr[1], out ); } break; case 1: setClockLimits( procId, expr[0], out ); break; case 0: break; } } pair < int32_t, int32_t > Evaluator::getRange( int procId, const expression_t &expr ) { if ( isConstant( expr ) ) return make_pair( eval( procId, expr ).get_int(), eval( procId, expr ).get_int() ); else if ( expr.getKind() == IDENTIFIER ) return evalRange( procId, expr.getType() ); else if ( expr.getKind() == PLUS ) { auto rl = getRange( procId, expr[0] ) , rr = getRange( procId, expr[1] ); return make_pair( rl.first + rr.first, rl.second + rr.second ); } else if ( expr.getKind() == MINUS ) { auto rl = getRange( procId, expr[0] ) , rr = getRange( procId, expr[1] ); return make_pair( rl.first - rr.second, rl.second - rr.first ); } else if ( expr.getKind() == MAX ) { auto rl = getRange( procId, expr[0] ) , rr = getRange( procId, expr[1] ); return make_pair( max( rl.first, rr.first ), max( rl.second, rr.second) ); } else if ( expr.getKind() == MIN ) { auto rl = getRange( procId, expr[0] ) , rr = getRange( procId, expr[1] ); return make_pair( min( rl.first, rr.first ), min( rl.second, rr.second) ); } else if ( expr.getKind() == INLINEIF ) { auto rtrue = getRange( procId, expr[1] ); auto rfalse = getRange( procId, expr[2] ); return make_pair( min( rtrue.first, rfalse.first), max( rtrue.second, rfalse.second) ); } else { return ( make_pair( numeric_limits< int >::min(), numeric_limits< int >::max() ) ); } } } }