[qfcc] Implement loops for spir-v

Mostly, at least: continue and break aren't functional yet, and for
loops haven't been tested, but while and do-while seem to work.
This commit is contained in:
Bill Currie 2024-11-16 19:25:20 +09:00
parent 89dac4a1b8
commit 3797b1083b
8 changed files with 239 additions and 24 deletions

View file

@ -349,6 +349,23 @@ typedef struct {
symtab_t *symtab;
} ex_decl_t;
typedef struct {
const expr_t *test;
const expr_t *body;
const expr_t *break_label;
const expr_t *continue_label;
bool body_first; ///< true for do-while loops
bool not;
} ex_loop_t;
typedef struct {
const expr_t *test;
const expr_t *true_body;
const expr_t *false_body;
rua_loc_t els;
bool not;
} ex_select_t;
typedef struct expr_s {
expr_t *next;
rua_loc_t loc; ///< source location of expression
@ -395,6 +412,8 @@ typedef struct expr_s {
ex_field_t field; ///< field reference expression
ex_array_t array; ///< array index expression
ex_decl_t decl; ///< variable declaration expression
ex_loop_t loop; ///< loop construct expression
ex_select_t select; ///< selection construct expression
};
} expr_t;
@ -928,6 +947,14 @@ expr_t *new_decl_expr (specifier_t spec, symtab_t *symtab);
expr_t *append_decl (expr_t *decl, symbol_t *sym, const expr_t *init);
expr_t *append_decl_list (expr_t *decl, const expr_t *list);
expr_t *new_loop_expr (bool not, bool body_first,
const expr_t *test, const expr_t *body,
const expr_t *break_label, const expr_t *continue_label);
expr_t *new_select_expr (bool not, const expr_t *test,
const expr_t *true_body,
const expr_t *els, const expr_t *false_body);
/** Create an expression of the correct type that references the specified
parameter slot.
@ -992,13 +1019,14 @@ const expr_t *deref_pointer_expr (const expr_t *pointer);
const expr_t *pointer_deref (const expr_t *pointer);
const expr_t *offset_pointer_expr (const expr_t *pointer, const expr_t *offset);
const expr_t *address_expr (const expr_t *e1, const type_t *t);
const expr_t *build_if_statement (int not, const expr_t *test, const expr_t *s1,
const expr_t *els, const expr_t *s2);
const expr_t *build_while_statement (int not, const expr_t *test,
const expr_t *build_if_statement (bool not, const expr_t *test,
const expr_t *s1, const expr_t *els,
const expr_t *s2);
const expr_t *build_while_statement (bool not, const expr_t *test,
const expr_t *statement,
const expr_t *break_label,
const expr_t *continue_label);
const expr_t *build_do_while_statement (const expr_t *statement, int not,
const expr_t *build_do_while_statement (const expr_t *statement, bool not,
const expr_t *test,
const expr_t *break_label,
const expr_t *continue_label);

View file

@ -74,6 +74,8 @@ EX_EXPR(cond) ///< ?: conditional expression (::ex_cond_t)
EX_EXPR(field) ///< field reference expression (::ex_field_t)
EX_EXPR(array) ///< array index expression (::ex_array_t)
EX_EXPR(decl) ///< delcaration expression (::ex_array_t)
EX_EXPR(loop) ///< loop construct expression (::ex_loop_t)
EX_EXPR(select) ///< select construct expression (::ex_select_t)
#undef EX_EXPR

View file

@ -145,6 +145,8 @@ get_type (const expr_t *e)
return nullptr;
case ex_return:
case ex_decl:
case ex_loop:
case ex_select:
internal_error (e, "unexpected expression type");
case ex_label:
case ex_compound:
@ -996,7 +998,7 @@ new_ushort_expr (unsigned short ushort_val)
bool
is_error (const expr_t *e)
{
return e->type == ex_error;
return e && e->type == ex_error;
}
bool
@ -1979,6 +1981,8 @@ has_function_call (const expr_t *e)
case ex_args:
case ex_type:
case ex_decl:
case ex_loop:
case ex_select:
return false;
case ex_multivec:
for (auto c = e->multivec.components.head; c; c = c->next) {
@ -2215,6 +2219,44 @@ new_decl_expr (specifier_t spec, symtab_t *symtab)
return decl;
}
expr_t *
new_loop_expr (bool not, bool body_first,
const expr_t *test, const expr_t *body,
const expr_t *break_label, const expr_t *continue_label)
{
auto loop = new_expr ();
loop->type = ex_loop;
loop->loop = (ex_loop_t) {
.test = test,
.body = body,
.break_label = break_label,
.continue_label = continue_label,
.body_first = body_first,
.not = not,
};
return loop;
}
expr_t *
new_select_expr (bool not, const expr_t *test,
const expr_t *true_body,
const expr_t *els,
const expr_t *false_body)
{
auto select = new_expr ();
select->type = ex_select;
select->select = (ex_select_t) {
.test = test,
.true_body = true_body,
.false_body = false_body,
.not = not,
};
if (els) {
select->select.els = els->loc;
}
return select;
}
expr_t *
append_decl (expr_t *decl, symbol_t *sym, const expr_t *init)
{
@ -2485,7 +2527,8 @@ address_expr (const expr_t *e1, const type_t *t)
}
const expr_t *
build_if_statement (int not, const expr_t *test, const expr_t *s1, const expr_t *els, const expr_t *s2)
build_if_statement (bool not, const expr_t *test, const expr_t *s1,
const expr_t *els, const expr_t *s2)
{
expr_t *if_expr;
expr_t *tl = new_label_expr ();
@ -2540,7 +2583,7 @@ build_if_statement (int not, const expr_t *test, const expr_t *s1, const expr_t
}
const expr_t *
build_while_statement (int not, const expr_t *test, const expr_t *statement,
build_while_statement (bool not, const expr_t *test, const expr_t *statement,
const expr_t *break_label, const expr_t *continue_label)
{
const expr_t *l1 = new_label_expr ();
@ -2576,7 +2619,7 @@ build_while_statement (int not, const expr_t *test, const expr_t *statement,
}
const expr_t *
build_do_while_statement (const expr_t *statement, int not, const expr_t *test,
build_do_while_statement (const expr_t *statement, bool not, const expr_t *test,
const expr_t *break_label,
const expr_t *continue_label)
{

View file

@ -133,6 +133,8 @@ is_lvalue (const expr_t *expr)
case ex_type:
case ex_incop:
case ex_decl:
case ex_loop:
case ex_select:
break;
case ex_cond:
return (is_lvalue (expr->cond.true_expr)

View file

@ -80,6 +80,8 @@ edag_add_expr (const expr_t *expr)
case ex_type:
case ex_incop:
case ex_decl:
case ex_loop:
case ex_select:
// these are never put in the dag
return expr;
case ex_list:

View file

@ -156,6 +156,12 @@ proc_field (const expr_t *expr)
return e;
}
static const expr_t *
proc_label (const expr_t *expr)
{
return expr;
}
static const expr_t *
proc_block (const expr_t *expr)
{
@ -357,10 +363,41 @@ proc_decl (const expr_t *expr)
return block;
}
static const expr_t *
proc_loop (const expr_t *expr)
{
auto test = expr_process (expr->loop.test);
auto body = expr_process (expr->loop.body);
auto break_label = expr->loop.break_label;
auto continue_label = expr->loop.continue_label;
bool body_first = expr->loop.body_first;
bool not = expr->loop.not;
scoped_src_loc (expr);
return new_loop_expr (not, body_first, test, body,
break_label, continue_label);
}
static const expr_t *
proc_select (const expr_t *expr)
{
auto test = expr_process (expr->select.test);
auto true_body = expr_process (expr->select.true_body);
auto false_body = expr_process (expr->select.false_body);
scoped_src_loc (expr);
auto select = new_select_expr (expr->select.not, test, true_body, nullptr,
false_body);
select->select.els = expr->select.els;
return select;
}
const expr_t *
expr_process (const expr_t *expr)
{
if (!expr) {
return expr;
}
static process_f funcs[ex_count] = {
[ex_label] = proc_label,
[ex_block] = proc_block,
[ex_expr] = proc_expr,
[ex_uexpr] = proc_uexpr,
@ -374,6 +411,8 @@ expr_process (const expr_t *expr)
[ex_cond] = proc_cond,
[ex_field] = proc_field,
[ex_decl] = proc_decl,
[ex_loop] = proc_loop,
[ex_select] = proc_select,
};
if (expr->type >= ex_count) {

View file

@ -1186,15 +1186,13 @@ expression_statement
selection_statement
// : IF '(' expression ')' selection_rest_statement
: IF '(' expression ')' statement[true] %prec IFX
: IF '(' expression[test] ')' statement[true] %prec IFX
{
$$ = build_if_statement (false, $expression,
$true, nullptr, nullptr);
$$ = new_select_expr (false, $test, $true, nullptr, nullptr);
}
| IF '(' expression ')' statement[true] else statement[false]
| IF '(' expression[test] ')' statement[true] else statement[false]
{
$$ = build_if_statement (false, $expression,
$true, $else, $false);
$$ = new_select_expr (false, $test, $true, $else, $false);
}
;
@ -1270,17 +1268,18 @@ iteration_statement
: WHILE new_scope break_label continue_label '(' condition ')'
statement_no_new_scope
{
$$ = build_while_statement (false, $continue_label,
$statement_no_new_scope, break_label,
continue_label);
$$ = new_loop_expr (false, false,
$condition, $statement_no_new_scope,
break_label, continue_label);
break_label = $break_label;
continue_label = $continue_label;
current_symtab = $new_scope->block.scope->parent;
}
| DO break_label continue_label statement WHILE '(' expression ')' ';'
{
$$ = build_do_while_statement ($statement, false, $expression,
break_label, continue_label);
$$ = new_loop_expr (false, true,
$expression, $statement,
break_label, continue_label);
break_label = $break_label;
continue_label = $continue_label;
}
@ -1289,13 +1288,15 @@ iteration_statement
'(' for_init_statement conditionopt ';' expressionopt ')'
statement_no_new_scope
{
auto loop = new_loop_expr (false, false,
$conditionopt, $statement_no_new_scope,
break_label, continue_label);
auto block = new_block_expr (nullptr);
if ($for_init_statement) {
$for_init_statement = build_block_expr ((expr_t *) $for_init_statement,
false);
append_expr (block, $for_init_statement);
}
$$ = build_for_statement ($for_init_statement, $conditionopt,
$expressionopt, $statement_no_new_scope,
break_label, continue_label);
$$ = append_expr (block, loop);
break_label = $break_label;
continue_label = $continue_label;
current_symtab = $new_scope->block.scope->parent;

View file

@ -427,6 +427,7 @@ type_id (const type_t *type, spirvctx_t *ctx)
return id;
}
// put a label into decl_space. used only when starting a function
static unsigned
spirv_DeclLabel (spirvctx_t *ctx)
{
@ -443,6 +444,54 @@ spirv_Label (spirvctx_t *ctx)
return INSN (insn, 1);
}
static unsigned
spirv_LabelId (unsigned id, spirvctx_t *ctx)
{
auto insn = spirv_new_insn (SpvOpLabel, 2, ctx->code_space);
INSN (insn, 1) = id;
return id;
}
static void
spirv_LoopMerge (unsigned merge, unsigned cont, spirvctx_t *ctx)
{
auto insn = spirv_new_insn (SpvOpLoopMerge, 4, ctx->code_space);
INSN (insn, 1) = merge;
INSN (insn, 2) = cont;
INSN (insn, 3) = SpvLoopControlMaskNone;
}
static void
spirv_Branch (unsigned label, spirvctx_t *ctx)
{
auto insn = spirv_new_insn (SpvOpBranch, 2, ctx->code_space);
INSN (insn, 1) = label;
}
static unsigned
spirv_SplitBlockId (unsigned id, spirvctx_t *ctx)
{
spirv_Branch (id, ctx);
spirv_LabelId (id, ctx);
return id;
}
static unsigned
spirv_SplitBlock (spirvctx_t *ctx)
{
return spirv_SplitBlockId (spirv_id (ctx), ctx);
}
static void
spirv_BranchConditional (unsigned test, unsigned true_label,
unsigned false_label, spirvctx_t *ctx)
{
auto insn = spirv_new_insn (SpvOpBranchConditional, 4, ctx->code_space);
INSN (insn, 1) = test;
INSN (insn, 2) = true_label;
INSN (insn, 3) = false_label;
}
static void
spirv_Unreachable (spirvctx_t *ctx)
{
@ -1272,6 +1321,53 @@ spirv_field (const expr_t *e, spirvctx_t *ctx)
return id;
}
static unsigned
spirv_loop (const expr_t *e, spirvctx_t *ctx)
{
unsigned loop = spirv_id (ctx);
unsigned merge = spirv_id (ctx);
unsigned cont = spirv_id (ctx);
if (e->loop.body_first) {
spirv_Branch (loop, ctx);
spirv_LabelId (loop, ctx);
spirv_LoopMerge (merge, cont, ctx);
spirv_SplitBlock (ctx);
spirv_emit_expr (e->loop.body, ctx);
spirv_SplitBlockId (cont, ctx);
unsigned test = spirv_emit_expr (e->loop.test, ctx);
if (e->loop.not) {
spirv_BranchConditional (test, merge, loop, ctx);
} else {
spirv_BranchConditional (test, loop, merge, ctx);
}
spirv_LabelId (merge, ctx);
} else {
spirv_Branch (loop, ctx);
spirv_LabelId (loop, ctx);
spirv_LoopMerge (merge, cont, ctx);
spirv_SplitBlock (ctx);
unsigned body = spirv_id (ctx);
unsigned test = spirv_emit_expr (e->loop.test, ctx);
if (e->loop.not) {
spirv_BranchConditional (test, merge, body, ctx);
} else {
spirv_BranchConditional (test, body, merge, ctx);
}
spirv_LabelId (body, ctx);
spirv_emit_expr (e->loop.body, ctx);
spirv_SplitBlockId (cont, ctx);
spirv_Branch (loop, ctx);
spirv_LabelId (merge, ctx);
}
return 0;
}
static unsigned
spirv_select (const expr_t *e, spirvctx_t *ctx)
{
return 0;
}
static unsigned
spirv_emit_expr (const expr_t *e, spirvctx_t *ctx)
{
@ -1288,6 +1384,8 @@ spirv_emit_expr (const expr_t *e, spirvctx_t *ctx)
[ex_extend] = spirv_extend,
[ex_cond] = spirv_cond,
[ex_field] = spirv_field,
[ex_loop] = spirv_loop,
[ex_select] = spirv_select,
};
if (e->type >= ex_count) {