-
Adam Knapp authored
Change-Id: Iacba53d8499439eba82045c30a1e0f2e0edf16fe Signed-off-by:
Adam Knapp <adam.knapp@sigmatechnology.se>
Adam Knapp authoredChange-Id: Iacba53d8499439eba82045c30a1e0f2e0edf16fe Signed-off-by:
Adam Knapp <adam.knapp@sigmatechnology.se>
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
ILT.cc 16.39 KiB
/******************************************************************************
* Copyright (c) 2000-2021 Ericsson Telecom AB
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html
*
* Contributors:
* Balasko, Jeno
* Feher, Csaba
* Forstner, Matyas
* Raduly, Csaba
* Szabo, Janos Zoltan – initial implementation
*
******************************************************************************/
#include "ILT.hh"
namespace Ttcn {
// =================================
// ===== ILT
// =================================
ILT::~ILT()
{
for(size_t i=0; i<branches.size(); i++)
delete branches[i];
branches.clear();
}
ILT_branch* ILT::get_as_branch()
{
FATAL_ERROR("ILT::get_as_branch()");
return 0;
}
void ILT::add_branch(ILT_branch *p_branch)
{
if(!p_branch) FATAL_ERROR("ILT::add_branch()");
branches.add(p_branch);
ILT_root *my_root = get_my_root();
p_branch->set_my_root(my_root);
p_branch->set_branch_i(my_root->get_new_branch_num());
}
// =================================
// ===== ILT_root
// =================================
ILT_root::ILT_root(Statement *p_il, char*& p_def_glob_vars, char*& p_src_glob_vars)
: il(p_il), tmpnum(0), c_l(0), c_b(0), out_def_glob_vars(p_def_glob_vars)
, out_src_glob_vars(p_src_glob_vars)
{
if(!p_il || p_il->get_statementtype()!=Statement::S_INTERLEAVE)
FATAL_ERROR("ILT_root::ILT_root()");
mytmpid=p_il->get_my_sb()->get_scope_mod_gen()->get_temporary_id();
out_def=memptystr();
out_statevars=memptystr();
out_code=memptystr();
out_branches=memptystr();
}
ILT_root::~ILT_root()
{
for(size_t i=0; i<state_var_vals.size(); i++)
delete state_var_vals[i];
state_var_vals.clear();
Free(out_def);
Free(out_statevars);
Free(out_code);
Free(out_branches);
}
ILT_root *ILT_root::clone() const
{
FATAL_ERROR("ILT_root::clone()");
}
ILT_root *ILT_root::get_my_root()
{
return this;
}
bool ILT_root::is_toplevel()
{
return true;
}
char*& ILT_root::get_out_def()
{
return out_def;
}
char*& ILT_root::get_out_code()
{
return out_code;
}
char*& ILT_root::get_out_branches()
{
return out_branches;
}
char*& ILT_root::get_out_def_glob_vars()
{
return out_def_glob_vars;
}
char*& ILT_root::get_out_src_glob_vars()
{
return out_src_glob_vars;
}
const string& ILT_root::get_my_tmpid()
{
return mytmpid;
}
size_t ILT_root::get_new_tmpnum()
{
return tmpnum++;
}
size_t ILT_root::get_new_state_var(bool toplevel)
{
state_var_vals.add(new size_t(1));
size_t newvar=state_var_vals.size()-1;
out_statevars=mputprintf(out_statevars, "%s_state[%lu]=%s;\n",
mytmpid.c_str(), static_cast<unsigned long>( newvar ), toplevel?"2":"0");
return newvar;
}
size_t ILT_root::get_new_state_var_val(size_t p_state_var)
{
if(p_state_var>=state_var_vals.size())
FATAL_ERROR("ILT_root::get_new_state_var_val()");
return ++*state_var_vals[p_state_var];
}
size_t ILT_root::get_new_label_num()
{
return c_l++;
}
char* ILT_root::generate_code(char *str)
{
// everything starts here: the top-level interleave statement
il->ilt_generate_code(this);
// generating code for the branches
for(size_t i=0; i<branches.size(); i++)
branches[i]->ilt_generate_code(this);
str=mputstr(str, "{\n"); // (1)
// the state vars: definition
str=mputprintf(str, "size_t %s_state[%lu];\n",
mytmpid.c_str(), static_cast<unsigned long>( state_var_vals.size()));
// the state vars: initialization
str=mputstr(str, out_statevars);
// the alt status flags: definition
str=mputprintf(str, "alt_status %s_alt_flag[%lu];\n",
mytmpid.c_str(), static_cast<unsigned long>(c_b+1) );
// the definitions
str=mputstr(str, out_def);
// the core of the interleave
str=mputprintf(str, "%s:\n"
"for(size_t tmp_i=0; tmp_i<%lu; tmp_i++)"
" %s_alt_flag[tmp_i]=ALT_UNCHECKED;\n"
"%s_alt_flag[%lu]=ALT_MAYBE;\n"
"TTCN_Snapshot::take_new(FALSE);\n"
"for( ; ; ) {\n" // (2)
"if(", mytmpid.c_str(),
static_cast<unsigned long>( c_b ), mytmpid.c_str(),
mytmpid.c_str(), static_cast<unsigned long>( c_b ));
// checking for final condition
for(size_t i=0; i<branches.size(); i++) {
ILT_branch *b=branches[i];
str=mputprintf(str, "%s%s_state[%lu]==1",
i?" && ":"", mytmpid.c_str(), static_cast<unsigned long>( b->get_my_state_var() ));
}
str=mputstr(str, ") break;\n");
// the altsteps :)
str=mputstr(str, out_code);
// checking defaults
str=mputprintf(str, "if(%s_alt_flag[%lu]==ALT_MAYBE) {\n"
"%s_alt_flag[%lu]=TTCN_Default::try_altsteps();\n"
"if(%s_alt_flag[%lu]==ALT_YES || %s_alt_flag[%lu]==ALT_BREAK)"
" break;\n"
"else if(%s_alt_flag[%lu]==ALT_REPEAT) goto %s;\n"
"}\n",
mytmpid.c_str(), static_cast<unsigned long>( c_b ),
mytmpid.c_str(), static_cast<unsigned long>( c_b ),
mytmpid.c_str(), static_cast<unsigned long>( c_b ),
mytmpid.c_str(), static_cast<unsigned long>( c_b ),
mytmpid.c_str(), static_cast<unsigned long>( c_b ),
mytmpid.c_str());
str=il->update_location_object(str);
// checking deadlock
str=mputprintf(str, "for(size_t tmp_i=0; tmp_i<%lu; tmp_i++)"
" if(%s_alt_flag[tmp_i]!=ALT_NO) goto %s_newsnapshot;\n",
static_cast<unsigned long>(c_b+1), mytmpid.c_str(), mytmpid.c_str());
str = mputstr(str, "TTCN_error(\"None of the branches can be chosen in the "
"interleave statement in file ");
str = Code::translate_string(str, il->get_filename());
size_t first_line = il->get_first_line(), last_line = il->get_last_line();
if (first_line < last_line) str = mputprintf(str,
" between lines %lu and %lu", first_line, last_line);
else str = mputprintf(str, ", line %lu", first_line);
str = mputprintf(str, ".\");\n"
"%s_newsnapshot:\n"
"TTCN_Snapshot::take_new(TRUE);\n"
"continue;\n", mytmpid.c_str());
// the code of branches
str=mputstr(str, out_branches);
str=mputstr(str, "}\n" // (2)
"}\n"); // (1)
return str;
}
// =================================
// ===== ILT_branch
// =================================
ILT_branch::ILT_branch(branchtype_t p_bt, AltGuard *p_ag,
string p_state_cond, size_t p_state_var,
size_t p_state_var_val, size_t p_goto_label_num)
: branchtype(p_bt), root(0), branch_i(static_cast<size_t>(-1)),
state_cond(p_state_cond), state_var(p_state_var),
state_var_val(p_state_var_val), goto_label_num(p_goto_label_num)
{
switch(p_bt) {
case BT_ALT:
case BT_IL:
if(!p_ag) FATAL_ERROR("ILT_branch::ILT_branch()");
ag=p_ag;
// the checker should check this but one never knows :)
if(ag->get_type()!=AltGuard::AG_OP)
FATAL_ERROR("ILT_branch::ILT_branch()");
break;
default:
FATAL_ERROR("ILT_branch::ILT_branch()");
} // switch
}
ILT_branch::ILT_branch(branchtype_t p_bt, Statement *p_stmt,
string p_state_cond, size_t p_state_var,
size_t p_state_var_val, size_t p_goto_label_num)
: branchtype(p_bt), root(0), branch_i(static_cast<size_t>(-1)),
state_cond(p_state_cond), state_var(p_state_var),
state_var_val(p_state_var_val), goto_label_num(p_goto_label_num)
{
switch(p_bt) {
case BT_RECV:
if(!p_stmt) FATAL_ERROR("ILT_branch::ILT_branch()");
stmt=p_stmt;
break;
default:
FATAL_ERROR("ILT_branch::ILT_branch()");
} // switch
}
ILT_branch::~ILT_branch()
{
// nothing to do
}
ILT_branch *ILT_branch::clone() const
{
FATAL_ERROR("ILT_branch::clone()");
}
void ILT_branch::set_my_root(ILT_root *p_root)
{
if(!p_root) FATAL_ERROR("ILT_branch::set_my_root()");
root=p_root;
}
ILT_root* ILT_branch::get_my_root()
{
if (!root) FATAL_ERROR("ILT_branch::get_my_root()");
return root;
}
ILT_branch *ILT_branch::get_as_branch()
{
return this;
}
bool ILT_branch::is_toplevel()
{
return false;
}
char*& ILT_branch::get_out_def()
{
return root->get_out_def();
}
char*& ILT_branch::get_out_code()
{
return root->get_out_code();
}
char*& ILT_branch::get_out_branches()
{
return root->get_out_branches();
}
char*& ILT_branch::get_out_def_glob_vars()
{
return root->get_out_def_glob_vars();
}
char*& ILT_branch::get_out_src_glob_vars()
{
return root->get_out_src_glob_vars();
}
const string& ILT_branch::get_my_tmpid()
{
return root->get_my_tmpid();
}
size_t ILT_branch::get_new_tmpnum()
{
return root->get_new_tmpnum();
}
size_t ILT_branch::get_new_state_var(bool toplevel)
{
return root->get_new_state_var(toplevel);
}
size_t ILT_branch::get_new_state_var_val(size_t p_state_var)
{
return root->get_new_state_var_val(p_state_var);
}
size_t ILT_branch::get_new_label_num()
{
return root->get_new_label_num();
}
void ILT_branch::ilt_generate_code(ILT *ilt)
{
const string& mytmpid=get_my_tmpid();
char*& out_code=get_out_code();
Statement *recv_stmt
=branchtype==BT_RECV?stmt:ag->get_guard_stmt();
out_code=recv_stmt->update_location_object(out_code);
// ALT_UNCHECKED -> ALT_MAYBE or ALT_NO (state vars & guard expr)
{
out_code=mputprintf(out_code,
"if(%s_alt_flag[%lu]==ALT_UNCHECKED) {\n" // (1)
"if(",
mytmpid.c_str(), static_cast<unsigned long>( branch_i ));
if(!state_cond.empty())
out_code=mputprintf(out_code, "%s && ", state_cond.c_str());
out_code=mputprintf(out_code, "%s_state[%lu]==%lu) {\n", // (2)
mytmpid.c_str(), static_cast<unsigned long>( state_var ),
static_cast<unsigned long>( state_var_val ));
// only alt branch can have guard expression
Value *guard_expr=branchtype==BT_ALT?ag->get_guard_expr():0;
if(guard_expr) {
out_code=guard_expr->update_location_object(out_code);
expression_struct expr;
Code::init_expr(&expr);
guard_expr->generate_code_expr(&expr);
out_code=mputstr(out_code, expr.preamble);
out_code=mputprintf(out_code,
"%s_alt_flag[%lu]=(%s)?ALT_MAYBE:ALT_NO;\n",
mytmpid.c_str(), static_cast<unsigned long>( branch_i ),
expr.expr);
out_code=mputstr(out_code, expr.postamble);
Code::free_expr(&expr);
} // if guard_expr
else out_code=mputprintf(out_code,
"%s_alt_flag[%lu]=ALT_MAYBE;\n",
mytmpid.c_str(), static_cast<unsigned long>( branch_i ));
out_code=mputprintf(out_code,
"}\n" // (2)
"else %s_alt_flag[%lu]=ALT_NO;\n"
"}\n", // (1)
mytmpid.c_str(), static_cast<unsigned long>( branch_i ));
}
// ALT_MAYBE -> ALT_YES or ALT_REPEAT or ALT_NO (guard stmt)
{
out_code=mputprintf(out_code,
"if(%s_alt_flag[%lu]==ALT_MAYBE) {\n", // (1)
mytmpid.c_str(), static_cast<unsigned long>( branch_i ));
expression_struct expr;
Code::init_expr(&expr);
recv_stmt->generate_code_expr(&expr);
// indicates whether the guard operation might return ALT_REPEAT
bool can_repeat=recv_stmt->can_repeat();
if(expr.preamble || expr.postamble) {
out_code=mputstr(out_code, "{\n");
out_code=mputstr(out_code, expr.preamble);
}
out_code=mputprintf(out_code, "%s_alt_flag[%lu]=%s;\n",
mytmpid.c_str(), static_cast<unsigned long>( branch_i), expr.expr);
if(expr.preamble || expr.postamble) {
out_code=mputstr(out_code, expr.postamble);
out_code=mputstr(out_code, "}\n");
}
Code::free_expr(&expr);
if(can_repeat)
out_code=mputprintf(out_code,
"if(%s_alt_flag[%lu]==ALT_REPEAT) goto %s;\n",
mytmpid.c_str(), static_cast<unsigned long>( branch_i),
mytmpid.c_str());
}
if(branchtype==BT_RECV)
out_code=mputprintf(out_code,
"if(%s_alt_flag[%lu]==ALT_YES) goto %s_l%lu;\n",
mytmpid.c_str(), static_cast<unsigned long>( branch_i),
mytmpid.c_str(), static_cast<unsigned long>( goto_label_num));
else {
out_code=mputprintf(out_code,
"if(%s_alt_flag[%lu]==ALT_YES) ",
mytmpid.c_str(), static_cast<unsigned long>( branch_i));
// statement block code generation;
StatementBlock *block=ag->get_block();
bool has_recv=block->has_receiving_stmt();
char*& out_stmt=has_recv?get_out_branches():out_code;
if(has_recv) {
out_code=mputprintf(out_code,
"goto %s_branch%lu;\n",
mytmpid.c_str(), static_cast<unsigned long>( branch_i));
out_stmt=mputprintf(out_stmt, "%s_branch%lu:\n",
mytmpid.c_str(), static_cast<unsigned long>( branch_i));
block->ilt_generate_code(this);
}
else {
out_stmt=mputstr(out_stmt, "{\n"); // (2)
out_stmt=block->generate_code(out_stmt, get_out_def_glob_vars(),
get_out_src_glob_vars());
}
if(branchtype==BT_IL) {
out_stmt=mputprintf(out_stmt, "%s_state[%lu]=1;\n",
mytmpid.c_str(), static_cast<unsigned long>( state_var));
if(!ilt->is_toplevel()) {
vector<ILT_branch>& ilt_branches = ilt->get_as_branch()->branches;
bool use_loop=ilt_branches.size() > 8;
size_t bmin=0, bmax=0;
if (use_loop) {
size_t st_var=ilt_branches[0]->get_my_state_var();
bmin=st_var; bmax=st_var;
for (size_t i=1; i<ilt_branches.size(); i++) {
st_var=ilt_branches[i]->get_my_state_var();
if (st_var<bmin) bmin=st_var;
if (st_var>bmax) bmax=st_var;
}
use_loop=(bmax-bmin+1) == ilt_branches.size();
}
if (use_loop) {
out_stmt=mputprintf(out_stmt,
"for(size_t tmp_i=%lu; tmp_i<%lu; tmp_i++)"
" if (%s_state[tmp_i]!=1) goto %s;\n",
static_cast<unsigned long>( bmin), static_cast<unsigned long>( bmax+1 ),
mytmpid.c_str(), mytmpid.c_str());
} else if (ilt_branches.size() > 1) {
// Check if any branches of non top level interleave need to be exe.
for(size_t i=0, j=0; i<ilt_branches.size(); i++) {
ILT_branch *b=ilt_branches[i];
if (b != this)
out_stmt=mputprintf(out_stmt, "%s%s_state[%lu]!=1",
j++?" || ":"if (", mytmpid.c_str(),
static_cast<unsigned long>( b->get_my_state_var() ));
}
out_stmt=mputprintf(out_stmt, ") goto %s;\n", mytmpid.c_str());
}
// non top level interleave: Handle finish case
out_stmt=mputprintf(out_stmt, "goto %s_l%lu;\n",
mytmpid.c_str(), static_cast<unsigned long>( goto_label_num ));
} else // top level: finish condition is checked at the beginning
out_stmt=mputprintf(out_stmt, "goto %s;\n", mytmpid.c_str());
} else // not interleave: finish after one branch execution
out_stmt=mputprintf(out_stmt, "goto %s_l%lu;\n",
mytmpid.c_str(), static_cast<unsigned long>( goto_label_num ));
if(!has_recv)
out_stmt=mputstr(out_stmt, "}\n"); // (2)
}
out_code=mputstr(out_code, "}\n"); // (1)
// the nested branches
for(size_t i=0; i<branches.size(); i++)
branches[i]->ilt_generate_code(this);
}
} // namespace Ttcn