223 lines
7.2 KiB
C
223 lines
7.2 KiB
C
/*
|
|
* Copyright (c) 2001-2023 Stephen Williams (steve@icarus.com)
|
|
*
|
|
* This source code is free software; you can redistribute it
|
|
* and/or modify it in source code form under the terms of the GNU
|
|
* General Public License as published by the Free Software
|
|
* Foundation; either version 2 of the License, or (at your option)
|
|
* any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
# include "vvp_priv.h"
|
|
# include <limits.h>
|
|
# include <string.h>
|
|
# include <assert.h>
|
|
# include <stdlib.h>
|
|
# include <stdbool.h>
|
|
|
|
const unsigned LABEL_NONE = UINT_MAX;
|
|
static unsigned break_label = LABEL_NONE;
|
|
static unsigned continue_label = LABEL_NONE;
|
|
|
|
#define PUSH_JUMPS(bl, cl) do { \
|
|
save_break_label = break_label; \
|
|
save_continue_label = continue_label; \
|
|
break_label = bl; \
|
|
continue_label = cl; \
|
|
} while (0)
|
|
|
|
#define POP_JUMPS do { \
|
|
break_label = save_break_label; \
|
|
continue_label = save_continue_label; \
|
|
} while (0)
|
|
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
|
int show_stmt_break(ivl_statement_t net, ivl_scope_t sscope)
|
|
{
|
|
if (break_label == LABEL_NONE) {
|
|
fprintf(stderr, "vvp.tgt: error: 'break' not in a loop?!\n");
|
|
return 1;
|
|
}
|
|
|
|
fprintf(vvp_out, " %%jmp T_%u.%u; break\n", thread_count, break_label);
|
|
return 0;
|
|
}
|
|
|
|
int show_stmt_continue(ivl_statement_t net, ivl_scope_t sscope)
|
|
{
|
|
if (continue_label == LABEL_NONE) {
|
|
fprintf(stderr, "vvp.tgt: error: 'continue' not in a loop?!\n");
|
|
return 1;
|
|
}
|
|
|
|
fprintf(vvp_out, " %%jmp T_%u.%u; continue\n", thread_count, continue_label);
|
|
return 0;
|
|
}
|
|
#pragma GCC diagnostic pop
|
|
|
|
int show_stmt_forever(ivl_statement_t net, ivl_scope_t sscope)
|
|
{
|
|
int rc = 0;
|
|
ivl_statement_t stmt = ivl_stmt_sub_stmt(net);
|
|
unsigned lab_top = local_count++;
|
|
unsigned lab_out = local_count++;
|
|
|
|
show_stmt_file_line(net, "Forever statement.");
|
|
|
|
unsigned save_break_label, save_continue_label;
|
|
PUSH_JUMPS(lab_out, lab_top);
|
|
|
|
fprintf(vvp_out, "T_%u.%u ;\n", thread_count, lab_top);
|
|
rc += show_statement(stmt, sscope);
|
|
fprintf(vvp_out, " %%jmp T_%u.%u;\n", thread_count, lab_top);
|
|
|
|
fprintf(vvp_out, "T_%u.%u ;\n", thread_count, lab_out);
|
|
POP_JUMPS;
|
|
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
* The for-loop should be emitted like this:
|
|
*
|
|
* <ivl_stmt_init_stmt>
|
|
* top_label:
|
|
* if (<ivl_stmt_cond_expr> is false)
|
|
* goto out_label;
|
|
* <ivl_stmt_sub_stmt>
|
|
* continue_label:
|
|
* <ivl_stmt_step_stmt>
|
|
* goto top_label
|
|
* out_label:
|
|
*
|
|
* This is very similar to how the while loop is generated. The obvious
|
|
* difference is that there is an init statement and a step statement
|
|
* that fits into the flow.
|
|
*/
|
|
int show_stmt_forloop(ivl_statement_t net, ivl_scope_t scope)
|
|
{
|
|
int rc = 0;
|
|
unsigned top_label = local_count++;
|
|
unsigned out_label = local_count++;
|
|
unsigned cont_label= local_count++;
|
|
unsigned save_break_label, save_continue_label;
|
|
PUSH_JUMPS(out_label, cont_label);
|
|
|
|
show_stmt_file_line(net, "For-loop statement.");
|
|
|
|
/* Draw the init statement before the entry point to the loop. */
|
|
if (ivl_stmt_init_stmt(net))
|
|
rc += show_statement(ivl_stmt_init_stmt(net), scope);
|
|
|
|
/* Top of the loop, draw the condition test. */
|
|
fprintf(vvp_out, "T_%u.%u ; Top of for-loop \n", thread_count, top_label);
|
|
int use_flag = draw_eval_condition(ivl_stmt_cond_expr(net));
|
|
fprintf(vvp_out, " %%jmp/0xz T_%u.%u, %d;\n",
|
|
thread_count, out_label, use_flag);
|
|
clr_flag(use_flag);
|
|
|
|
/* Draw the body of the loop. */
|
|
rc += show_statement(ivl_stmt_sub_stmt(net), scope);
|
|
|
|
fprintf(vvp_out, "T_%u.%u ; for-loop step statement\n",
|
|
thread_count, cont_label);
|
|
if (ivl_stmt_step_stmt(net))
|
|
rc += show_statement(ivl_stmt_step_stmt(net), scope);
|
|
fprintf(vvp_out, " %%jmp T_%u.%u;\n", thread_count, top_label);
|
|
|
|
fprintf(vvp_out, "T_%u.%u ; for-loop exit label\n",
|
|
thread_count, out_label);
|
|
|
|
POP_JUMPS;
|
|
|
|
return rc;
|
|
}
|
|
|
|
int show_stmt_repeat(ivl_statement_t net, ivl_scope_t sscope)
|
|
{
|
|
int rc = 0;
|
|
unsigned lab_top = local_count++, lab_out = local_count++;
|
|
ivl_expr_t expr = ivl_stmt_cond_expr(net);
|
|
const char *sign = ivl_expr_signed(expr) ? "s" : "u";
|
|
|
|
unsigned save_break_label, save_continue_label;
|
|
PUSH_JUMPS(lab_out, lab_top);
|
|
|
|
show_stmt_file_line(net, "Repeat statement.");
|
|
|
|
/* Calculate the repeat count onto the top of the vec4 stack. */
|
|
draw_eval_vec4(expr);
|
|
|
|
/* Test that 0 < expr, escape if expr <= 0. If the expr is
|
|
unsigned, then we only need to try to escape if expr==0 as
|
|
it will never be <0. */
|
|
fprintf(vvp_out, "T_%u.%u %%dup/vec4;\n", thread_count, lab_top);
|
|
fprintf(vvp_out, " %%pushi/vec4 0, 0, %u;\n", ivl_expr_width(expr));
|
|
fprintf(vvp_out, " %%cmp/%s;\n", sign);
|
|
if (ivl_expr_signed(expr))
|
|
fprintf(vvp_out, " %%jmp/1xz T_%u.%u, 5;\n", thread_count, lab_out);
|
|
fprintf(vvp_out, " %%jmp/1 T_%u.%u, 4;\n", thread_count, lab_out);
|
|
/* This adds -1 (all ones in 2's complement) to the count. */
|
|
fprintf(vvp_out, " %%pushi/vec4 1, 0, %u;\n", ivl_expr_width(expr));
|
|
fprintf(vvp_out, " %%sub;\n");
|
|
|
|
rc += show_statement(ivl_stmt_sub_stmt(net), sscope);
|
|
|
|
fprintf(vvp_out, " %%jmp T_%u.%u;\n", thread_count, lab_top);
|
|
fprintf(vvp_out, "T_%u.%u ;\n", thread_count, lab_out);
|
|
fprintf(vvp_out, " %%pop/vec4 1;\n");
|
|
|
|
POP_JUMPS;
|
|
|
|
return rc;
|
|
}
|
|
|
|
int show_stmt_while(ivl_statement_t net, ivl_scope_t sscope)
|
|
{
|
|
int rc = 0;
|
|
|
|
unsigned top_label = local_count++;
|
|
unsigned out_label = local_count++;
|
|
|
|
unsigned save_break_label, save_continue_label;
|
|
PUSH_JUMPS(out_label, top_label);
|
|
|
|
show_stmt_file_line(net, "While statement.");
|
|
|
|
/* Start the loop. The top of the loop starts a basic block
|
|
because it can be entered from above or from the bottom of
|
|
the loop. */
|
|
fprintf(vvp_out, "T_%u.%u ;\n", thread_count, top_label);
|
|
|
|
|
|
/* Draw the evaluation of the condition expression, and test
|
|
the result. If the expression evaluates to false, then
|
|
branch to the out label. */
|
|
int use_flag = draw_eval_condition(ivl_stmt_cond_expr(net));
|
|
fprintf(vvp_out, " %%jmp/0xz T_%u.%u, %d;\n",
|
|
thread_count, out_label, use_flag);
|
|
clr_flag(use_flag);
|
|
|
|
/* Draw the body of the loop. */
|
|
rc += show_statement(ivl_stmt_sub_stmt(net), sscope);
|
|
|
|
/* This is the bottom of the loop. branch to the top where the
|
|
test is repeated, and also draw the out label. */
|
|
fprintf(vvp_out, " %%jmp T_%u.%u;\n", thread_count, top_label);
|
|
fprintf(vvp_out, "T_%u.%u ;\n", thread_count, out_label);
|
|
|
|
POP_JUMPS;
|
|
|
|
return rc;
|
|
}
|