Commit | Line | Data |
---|---|---|
d44e3c4f | 1 | /****************************************************************************** |
2 | * Copyright (c) 2000-2016 Ericsson Telecom AB | |
3 | * All rights reserved. This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License v1.0 | |
5 | * which accompanies this distribution, and is available at | |
6 | * http://www.eclipse.org/legal/epl-v10.html | |
7 | * | |
8 | * Contributors: | |
9 | * Balasko, Jeno | |
10 | * Feher, Csaba | |
11 | * Forstner, Matyas | |
12 | * Raduly, Csaba | |
13 | * Szabo, Janos Zoltan – initial implementation | |
14 | * | |
15 | ******************************************************************************/ | |
970ed795 EL |
16 | #include "ILT.hh" |
17 | ||
18 | namespace Ttcn { | |
19 | ||
20 | // ================================= | |
21 | // ===== ILT | |
22 | // ================================= | |
23 | ||
24 | ILT::~ILT() | |
25 | { | |
26 | for(size_t i=0; i<branches.size(); i++) | |
27 | delete branches[i]; | |
28 | branches.clear(); | |
29 | } | |
30 | ||
31 | ILT_branch* ILT::get_as_branch() | |
32 | { | |
33 | FATAL_ERROR("ILT::get_as_branch()"); | |
34 | return 0; | |
35 | } | |
36 | ||
37 | void ILT::add_branch(ILT_branch *p_branch) | |
38 | { | |
39 | if(!p_branch) FATAL_ERROR("ILT::add_branch()"); | |
40 | branches.add(p_branch); | |
41 | ILT_root *my_root = get_my_root(); | |
42 | p_branch->set_my_root(my_root); | |
43 | p_branch->set_branch_i(my_root->get_new_branch_num()); | |
44 | } | |
45 | ||
46 | ||
47 | // ================================= | |
48 | // ===== ILT_root | |
49 | // ================================= | |
50 | ||
51 | ILT_root::ILT_root(Statement *p_il) | |
52 | : il(p_il), tmpnum(0), c_l(0), c_b(0) | |
53 | { | |
54 | if(!p_il || p_il->get_statementtype()!=Statement::S_INTERLEAVE) | |
55 | FATAL_ERROR("ILT_root::ILT_root()"); | |
56 | mytmpid=p_il->get_my_sb()->get_scope_mod_gen()->get_temporary_id(); | |
57 | out_def=memptystr(); | |
58 | out_statevars=memptystr(); | |
59 | out_code=memptystr(); | |
60 | out_branches=memptystr(); | |
61 | } | |
62 | ||
63 | ILT_root::~ILT_root() | |
64 | { | |
65 | for(size_t i=0; i<state_var_vals.size(); i++) | |
66 | delete state_var_vals[i]; | |
67 | state_var_vals.clear(); | |
68 | Free(out_def); | |
69 | Free(out_statevars); | |
70 | Free(out_code); | |
71 | Free(out_branches); | |
72 | } | |
73 | ||
74 | ILT_root *ILT_root::clone() const | |
75 | { | |
76 | FATAL_ERROR("ILT_root::clone()"); | |
77 | } | |
78 | ||
79 | ILT_root *ILT_root::get_my_root() | |
80 | { | |
81 | return this; | |
82 | } | |
83 | ||
84 | bool ILT_root::is_toplevel() | |
85 | { | |
86 | return true; | |
87 | } | |
88 | ||
89 | char*& ILT_root::get_out_def() | |
90 | { | |
91 | return out_def; | |
92 | } | |
93 | ||
94 | char*& ILT_root::get_out_code() | |
95 | { | |
96 | return out_code; | |
97 | } | |
98 | ||
99 | char*& ILT_root::get_out_branches() | |
100 | { | |
101 | return out_branches; | |
102 | } | |
103 | ||
104 | const string& ILT_root::get_my_tmpid() | |
105 | { | |
106 | return mytmpid; | |
107 | } | |
108 | ||
109 | size_t ILT_root::get_new_tmpnum() | |
110 | { | |
111 | return tmpnum++; | |
112 | } | |
113 | ||
114 | size_t ILT_root::get_new_state_var(bool toplevel) | |
115 | { | |
116 | state_var_vals.add(new size_t(1)); | |
117 | size_t newvar=state_var_vals.size()-1; | |
118 | out_statevars=mputprintf(out_statevars, "%s_state[%lu]=%s;\n", | |
119 | mytmpid.c_str(), (unsigned long) newvar, toplevel?"2":"0"); | |
120 | return newvar; | |
121 | } | |
122 | ||
123 | size_t ILT_root::get_new_state_var_val(size_t p_state_var) | |
124 | { | |
125 | if(p_state_var>=state_var_vals.size()) | |
126 | FATAL_ERROR("ILT_root::get_new_state_var_val()"); | |
127 | return ++*state_var_vals[p_state_var]; | |
128 | } | |
129 | ||
130 | size_t ILT_root::get_new_label_num() | |
131 | { | |
132 | return c_l++; | |
133 | } | |
134 | ||
135 | char* ILT_root::generate_code(char *str) | |
136 | { | |
137 | // everything starts here: the top-level interleave statement | |
138 | il->ilt_generate_code(this); | |
139 | // generating code for the branches | |
140 | for(size_t i=0; i<branches.size(); i++) | |
141 | branches[i]->ilt_generate_code(this); | |
142 | str=mputstr(str, "{\n"); // (1) | |
143 | // the state vars: definition | |
144 | str=mputprintf(str, "size_t %s_state[%lu];\n", | |
145 | mytmpid.c_str(), (unsigned long) state_var_vals.size()); | |
146 | // the state vars: initialization | |
147 | str=mputstr(str, out_statevars); | |
148 | // the alt status flags: definition | |
149 | str=mputprintf(str, "alt_status %s_alt_flag[%lu];\n", | |
150 | mytmpid.c_str(), (unsigned long) (c_b+1)); | |
151 | // the definitions | |
152 | str=mputstr(str, out_def); | |
153 | // the core of the interleave | |
154 | str=mputprintf(str, "%s:\n" | |
155 | "for(size_t tmp_i=0; tmp_i<%lu; tmp_i++)" | |
156 | " %s_alt_flag[tmp_i]=ALT_UNCHECKED;\n" | |
157 | "%s_alt_flag[%lu]=ALT_MAYBE;\n" | |
158 | "TTCN_Snapshot::take_new(FALSE);\n" | |
159 | "for( ; ; ) {\n" // (2) | |
160 | "if(", mytmpid.c_str(), | |
161 | (unsigned long) c_b, mytmpid.c_str(), | |
162 | mytmpid.c_str(), (unsigned long) c_b); | |
163 | // checking for final condition | |
164 | for(size_t i=0; i<branches.size(); i++) { | |
165 | ILT_branch *b=branches[i]; | |
166 | str=mputprintf(str, "%s%s_state[%lu]==1", | |
167 | i?" && ":"", mytmpid.c_str(), (unsigned long) b->get_my_state_var()); | |
168 | } | |
169 | str=mputstr(str, ") break;\n"); | |
170 | // the altsteps :) | |
171 | str=mputstr(str, out_code); | |
172 | // checking defaults | |
173 | str=mputprintf(str, "if(%s_alt_flag[%lu]==ALT_MAYBE) {\n" | |
174 | "%s_alt_flag[%lu]=TTCN_Default::try_altsteps();\n" | |
175 | "if(%s_alt_flag[%lu]==ALT_YES || %s_alt_flag[%lu]==ALT_BREAK)" | |
176 | " break;\n" | |
177 | "else if(%s_alt_flag[%lu]==ALT_REPEAT) goto %s;\n" | |
178 | "}\n", | |
179 | mytmpid.c_str(), (unsigned long) c_b, | |
180 | mytmpid.c_str(), (unsigned long) c_b, | |
181 | mytmpid.c_str(), (unsigned long) c_b, | |
182 | mytmpid.c_str(), (unsigned long) c_b, | |
183 | mytmpid.c_str(), (unsigned long) c_b, | |
184 | mytmpid.c_str()); | |
185 | str=il->update_location_object(str); | |
186 | // checking deadlock | |
187 | str=mputprintf(str, "for(size_t tmp_i=0; tmp_i<%lu; tmp_i++)" | |
188 | " if(%s_alt_flag[tmp_i]!=ALT_NO) goto %s_newsnapshot;\n", | |
189 | (unsigned long) (c_b+1), mytmpid.c_str(), mytmpid.c_str()); | |
190 | str = mputstr(str, "TTCN_error(\"None of the branches can be chosen in the " | |
191 | "interleave statement in file "); | |
192 | str = Code::translate_string(str, il->get_filename()); | |
193 | int first_line = il->get_first_line(), last_line = il->get_last_line(); | |
194 | if (first_line < last_line) str = mputprintf(str, | |
195 | " between lines %d and %d", first_line, last_line); | |
196 | else str = mputprintf(str, ", line %d", first_line); | |
197 | str = mputprintf(str, ".\");\n" | |
198 | "%s_newsnapshot:\n" | |
199 | "TTCN_Snapshot::take_new(TRUE);\n" | |
200 | "continue;\n", mytmpid.c_str()); | |
201 | // the code of branches | |
202 | str=mputstr(str, out_branches); | |
203 | str=mputstr(str, "}\n" // (2) | |
204 | "}\n"); // (1) | |
205 | return str; | |
206 | } | |
207 | ||
208 | // ================================= | |
209 | // ===== ILT_branch | |
210 | // ================================= | |
211 | ||
212 | ILT_branch::ILT_branch(branchtype_t p_bt, AltGuard *p_ag, | |
213 | string p_state_cond, size_t p_state_var, | |
214 | size_t p_state_var_val, size_t p_goto_label_num) | |
215 | : branchtype(p_bt), root(0), branch_i((size_t)-1), | |
216 | state_cond(p_state_cond), state_var(p_state_var), | |
217 | state_var_val(p_state_var_val), goto_label_num(p_goto_label_num) | |
218 | { | |
219 | switch(p_bt) { | |
220 | case BT_ALT: | |
221 | case BT_IL: | |
222 | if(!p_ag) FATAL_ERROR("ILT_branch::ILT_branch()"); | |
223 | ag=p_ag; | |
224 | // the checker should check this but one never knows :) | |
225 | if(ag->get_type()!=AltGuard::AG_OP) | |
226 | FATAL_ERROR("ILT_branch::ILT_branch()"); | |
227 | break; | |
228 | default: | |
229 | FATAL_ERROR("ILT_branch::ILT_branch()"); | |
230 | } // switch | |
231 | } | |
232 | ||
233 | ILT_branch::ILT_branch(branchtype_t p_bt, Statement *p_stmt, | |
234 | string p_state_cond, size_t p_state_var, | |
235 | size_t p_state_var_val, size_t p_goto_label_num) | |
236 | : branchtype(p_bt), root(0), branch_i((size_t)-1), | |
237 | state_cond(p_state_cond), state_var(p_state_var), | |
238 | state_var_val(p_state_var_val), goto_label_num(p_goto_label_num) | |
239 | { | |
240 | switch(p_bt) { | |
241 | case BT_RECV: | |
242 | if(!p_stmt) FATAL_ERROR("ILT_branch::ILT_branch()"); | |
243 | stmt=p_stmt; | |
244 | break; | |
245 | default: | |
246 | FATAL_ERROR("ILT_branch::ILT_branch()"); | |
247 | } // switch | |
248 | } | |
249 | ||
250 | ILT_branch::~ILT_branch() | |
251 | { | |
252 | // nothing to do | |
253 | } | |
254 | ||
255 | ILT_branch *ILT_branch::clone() const | |
256 | { | |
257 | FATAL_ERROR("ILT_branch::clone()"); | |
258 | } | |
259 | ||
260 | void ILT_branch::set_my_root(ILT_root *p_root) | |
261 | { | |
262 | if(!p_root) FATAL_ERROR("ILT_branch::set_my_root()"); | |
263 | root=p_root; | |
264 | } | |
265 | ||
266 | ILT_root* ILT_branch::get_my_root() | |
267 | { | |
268 | if (!root) FATAL_ERROR("ILT_branch::get_my_root()"); | |
269 | return root; | |
270 | } | |
271 | ||
272 | ILT_branch *ILT_branch::get_as_branch() | |
273 | { | |
274 | return this; | |
275 | } | |
276 | ||
277 | bool ILT_branch::is_toplevel() | |
278 | { | |
279 | return false; | |
280 | } | |
281 | ||
282 | char*& ILT_branch::get_out_def() | |
283 | { | |
284 | return root->get_out_def(); | |
285 | } | |
286 | ||
287 | char*& ILT_branch::get_out_code() | |
288 | { | |
289 | return root->get_out_code(); | |
290 | } | |
291 | ||
292 | char*& ILT_branch::get_out_branches() | |
293 | { | |
294 | return root->get_out_branches(); | |
295 | } | |
296 | ||
297 | const string& ILT_branch::get_my_tmpid() | |
298 | { | |
299 | return root->get_my_tmpid(); | |
300 | } | |
301 | ||
302 | size_t ILT_branch::get_new_tmpnum() | |
303 | { | |
304 | return root->get_new_tmpnum(); | |
305 | } | |
306 | ||
307 | size_t ILT_branch::get_new_state_var(bool toplevel) | |
308 | { | |
309 | return root->get_new_state_var(toplevel); | |
310 | } | |
311 | ||
312 | size_t ILT_branch::get_new_state_var_val(size_t p_state_var) | |
313 | { | |
314 | return root->get_new_state_var_val(p_state_var); | |
315 | } | |
316 | ||
317 | size_t ILT_branch::get_new_label_num() | |
318 | { | |
319 | return root->get_new_label_num(); | |
320 | } | |
321 | ||
322 | void ILT_branch::ilt_generate_code(ILT *ilt) | |
323 | { | |
324 | const string& mytmpid=get_my_tmpid(); | |
325 | char*& out_code=get_out_code(); | |
326 | Statement *recv_stmt | |
327 | =branchtype==BT_RECV?stmt:ag->get_guard_stmt(); | |
328 | out_code=recv_stmt->update_location_object(out_code); | |
329 | // ALT_UNCHECKED -> ALT_MAYBE or ALT_NO (state vars & guard expr) | |
330 | { | |
331 | out_code=mputprintf(out_code, | |
332 | "if(%s_alt_flag[%lu]==ALT_UNCHECKED) {\n" // (1) | |
333 | "if(", | |
334 | mytmpid.c_str(), (unsigned long) branch_i); | |
335 | if(!state_cond.empty()) | |
336 | out_code=mputprintf(out_code, "%s && ", state_cond.c_str()); | |
337 | out_code=mputprintf(out_code, "%s_state[%lu]==%lu) {\n", // (2) | |
338 | mytmpid.c_str(), (unsigned long) state_var, | |
339 | (unsigned long) state_var_val); | |
340 | // only alt branch can have guard expression | |
341 | Value *guard_expr=branchtype==BT_ALT?ag->get_guard_expr():0; | |
342 | if(guard_expr) { | |
343 | out_code=guard_expr->update_location_object(out_code); | |
344 | expression_struct expr; | |
345 | Code::init_expr(&expr); | |
346 | guard_expr->generate_code_expr(&expr); | |
347 | out_code=mputstr(out_code, expr.preamble); | |
348 | out_code=mputprintf(out_code, | |
349 | "%s_alt_flag[%lu]=(%s)?ALT_MAYBE:ALT_NO;\n", | |
350 | mytmpid.c_str(), (unsigned long) branch_i, | |
351 | expr.expr); | |
352 | out_code=mputstr(out_code, expr.postamble); | |
353 | Code::free_expr(&expr); | |
354 | } // if guard_expr | |
355 | else out_code=mputprintf(out_code, | |
356 | "%s_alt_flag[%lu]=ALT_MAYBE;\n", | |
357 | mytmpid.c_str(), (unsigned long) branch_i); | |
358 | out_code=mputprintf(out_code, | |
359 | "}\n" // (2) | |
360 | "else %s_alt_flag[%lu]=ALT_NO;\n" | |
361 | "}\n", // (1) | |
362 | mytmpid.c_str(), (unsigned long) branch_i); | |
363 | } | |
364 | // ALT_MAYBE -> ALT_YES or ALT_REPEAT or ALT_NO (guard stmt) | |
365 | { | |
366 | out_code=mputprintf(out_code, | |
367 | "if(%s_alt_flag[%lu]==ALT_MAYBE) {\n", // (1) | |
368 | mytmpid.c_str(), (unsigned long) branch_i); | |
369 | expression_struct expr; | |
370 | Code::init_expr(&expr); | |
371 | recv_stmt->generate_code_expr(&expr); | |
372 | // indicates whether the guard operation might return ALT_REPEAT | |
373 | bool can_repeat=recv_stmt->can_repeat(); | |
374 | if(expr.preamble || expr.postamble) { | |
375 | out_code=mputstr(out_code, "{\n"); | |
376 | out_code=mputstr(out_code, expr.preamble); | |
377 | } | |
378 | out_code=mputprintf(out_code, "%s_alt_flag[%lu]=%s;\n", | |
379 | mytmpid.c_str(), (unsigned long) branch_i, expr.expr); | |
380 | if(expr.preamble || expr.postamble) { | |
381 | out_code=mputstr(out_code, expr.postamble); | |
382 | out_code=mputstr(out_code, "}\n"); | |
383 | } | |
384 | Code::free_expr(&expr); | |
385 | if(can_repeat) | |
386 | out_code=mputprintf(out_code, | |
387 | "if(%s_alt_flag[%lu]==ALT_REPEAT) goto %s;\n", | |
388 | mytmpid.c_str(), (unsigned long) branch_i, | |
389 | mytmpid.c_str()); | |
390 | } | |
391 | if(branchtype==BT_RECV) | |
392 | out_code=mputprintf(out_code, | |
393 | "if(%s_alt_flag[%lu]==ALT_YES) goto %s_l%lu;\n", | |
394 | mytmpid.c_str(), (unsigned long) branch_i, | |
395 | mytmpid.c_str(), (unsigned long) goto_label_num); | |
396 | else { | |
397 | out_code=mputprintf(out_code, | |
398 | "if(%s_alt_flag[%lu]==ALT_YES) ", | |
399 | mytmpid.c_str(), (unsigned long) branch_i); | |
400 | // statement block code generation; | |
401 | StatementBlock *block=ag->get_block(); | |
402 | bool has_recv=block->has_receiving_stmt(); | |
403 | char*& out_stmt=has_recv?get_out_branches():out_code; | |
404 | if(has_recv) { | |
405 | out_code=mputprintf(out_code, | |
406 | "goto %s_branch%lu;\n", | |
407 | mytmpid.c_str(), (unsigned long) branch_i); | |
408 | out_stmt=mputprintf(out_stmt, "%s_branch%lu:\n", | |
409 | mytmpid.c_str(), (unsigned long) branch_i); | |
410 | block->ilt_generate_code(this); | |
411 | } | |
412 | else { | |
413 | out_stmt=mputstr(out_stmt, "{\n"); // (2) | |
414 | out_stmt=block->generate_code(out_stmt); | |
415 | } | |
416 | if(branchtype==BT_IL) { | |
417 | out_stmt=mputprintf(out_stmt, "%s_state[%lu]=1;\n", | |
418 | mytmpid.c_str(), (unsigned long) state_var); | |
419 | if(!ilt->is_toplevel()) { | |
420 | vector<ILT_branch>& ilt_branches = ilt->get_as_branch()->branches; | |
421 | bool use_loop=ilt_branches.size() > 8; | |
422 | size_t bmin=0, bmax=0; | |
423 | if (use_loop) { | |
424 | size_t st_var=ilt_branches[0]->get_my_state_var(); | |
425 | bmin=st_var; bmax=st_var; | |
426 | for (size_t i=1; i<ilt_branches.size(); i++) { | |
427 | st_var=ilt_branches[i]->get_my_state_var(); | |
428 | if (st_var<bmin) bmin=st_var; | |
429 | if (st_var>bmax) bmax=st_var; | |
430 | } | |
431 | use_loop=(bmax-bmin+1) == ilt_branches.size(); | |
432 | } | |
433 | if (use_loop) { | |
434 | out_stmt=mputprintf(out_stmt, | |
435 | "for(size_t tmp_i=%lu; tmp_i<%lu; tmp_i++)" | |
436 | " if (%s_state[tmp_i]!=1) goto %s;\n", | |
437 | (unsigned long) bmin, (unsigned long) bmax+1, | |
438 | mytmpid.c_str(), mytmpid.c_str()); | |
439 | } else if (ilt_branches.size() > 1) { | |
440 | // Check if any branches of non top level interleave need to be exe. | |
441 | for(size_t i=0, j=0; i<ilt_branches.size(); i++) { | |
442 | ILT_branch *b=ilt_branches[i]; | |
443 | if (b != this) | |
444 | out_stmt=mputprintf(out_stmt, "%s%s_state[%lu]!=1", | |
445 | j++?" || ":"if (", mytmpid.c_str(), | |
446 | (unsigned long) b->get_my_state_var()); | |
447 | } | |
448 | out_stmt=mputprintf(out_stmt, ") goto %s;\n", mytmpid.c_str()); | |
449 | } | |
450 | // non top level interleave: Handle finish case | |
451 | out_stmt=mputprintf(out_stmt, "goto %s_l%lu;\n", | |
452 | mytmpid.c_str(), (unsigned long) goto_label_num); | |
453 | } else // top level: finish condition is checked at the beginning | |
454 | out_stmt=mputprintf(out_stmt, "goto %s;\n", mytmpid.c_str()); | |
455 | } else // not interleave: finish after one branch execution | |
456 | out_stmt=mputprintf(out_stmt, "goto %s_l%lu;\n", | |
457 | mytmpid.c_str(), (unsigned long) goto_label_num); | |
458 | if(!has_recv) | |
459 | out_stmt=mputstr(out_stmt, "}\n"); // (2) | |
460 | } | |
461 | out_code=mputstr(out_code, "}\n"); // (1) | |
462 | // the nested branches | |
463 | for(size_t i=0; i<branches.size(); i++) | |
464 | branches[i]->ilt_generate_code(this); | |
465 | } | |
466 | ||
467 | } // namespace Ttcn |