mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2025-04-06 17:32:05 +00:00
[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:
parent
89dac4a1b8
commit
3797b1083b
8 changed files with 239 additions and 24 deletions
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Reference in a new issue