From 3797b1083bd39e0072e07254b774bc27f1a68e2a Mon Sep 17 00:00:00 2001 From: Bill Currie Date: Sat, 16 Nov 2024 19:25:20 +0900 Subject: [PATCH] [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. --- tools/qfcc/include/expr.h | 36 ++++++++++-- tools/qfcc/include/expr_names.h | 2 + tools/qfcc/source/expr.c | 51 +++++++++++++++-- tools/qfcc/source/expr_assign.c | 2 + tools/qfcc/source/expr_dag.c | 2 + tools/qfcc/source/expr_process.c | 39 +++++++++++++ tools/qfcc/source/glsl-parse.y | 33 +++++------ tools/qfcc/source/target_spirv.c | 98 ++++++++++++++++++++++++++++++++ 8 files changed, 239 insertions(+), 24 deletions(-) diff --git a/tools/qfcc/include/expr.h b/tools/qfcc/include/expr.h index 62ea224db..ea18bad94 100644 --- a/tools/qfcc/include/expr.h +++ b/tools/qfcc/include/expr.h @@ -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); diff --git a/tools/qfcc/include/expr_names.h b/tools/qfcc/include/expr_names.h index e0237b8ab..9bfae278a 100644 --- a/tools/qfcc/include/expr_names.h +++ b/tools/qfcc/include/expr_names.h @@ -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 diff --git a/tools/qfcc/source/expr.c b/tools/qfcc/source/expr.c index 77b203113..206573ce2 100644 --- a/tools/qfcc/source/expr.c +++ b/tools/qfcc/source/expr.c @@ -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) { diff --git a/tools/qfcc/source/expr_assign.c b/tools/qfcc/source/expr_assign.c index 860b9aadf..66e329482 100644 --- a/tools/qfcc/source/expr_assign.c +++ b/tools/qfcc/source/expr_assign.c @@ -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) diff --git a/tools/qfcc/source/expr_dag.c b/tools/qfcc/source/expr_dag.c index 1840561a1..7b7788c65 100644 --- a/tools/qfcc/source/expr_dag.c +++ b/tools/qfcc/source/expr_dag.c @@ -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: diff --git a/tools/qfcc/source/expr_process.c b/tools/qfcc/source/expr_process.c index 36d52425b..a4b42c66a 100644 --- a/tools/qfcc/source/expr_process.c +++ b/tools/qfcc/source/expr_process.c @@ -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) { diff --git a/tools/qfcc/source/glsl-parse.y b/tools/qfcc/source/glsl-parse.y index c3050202b..644532fc5 100644 --- a/tools/qfcc/source/glsl-parse.y +++ b/tools/qfcc/source/glsl-parse.y @@ -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; diff --git a/tools/qfcc/source/target_spirv.c b/tools/qfcc/source/target_spirv.c index a40fcf9ec..35decbdb6 100644 --- a/tools/qfcc/source/target_spirv.c +++ b/tools/qfcc/source/target_spirv.c @@ -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) {