[qfcc] Implement select (if) constructs

And break/continue too, since my test for them required select.
This commit is contained in:
Bill Currie 2024-11-16 23:13:46 +09:00
parent 93d116cf16
commit a7f9d96a02
4 changed files with 104 additions and 37 deletions

View file

@ -71,11 +71,11 @@ typedef struct ex_expr_s {
typedef struct ex_label_s { typedef struct ex_label_s {
struct ex_label_s *next; struct ex_label_s *next;
struct reloc_s *refs; ///< relocations associated with this label
struct sblock_s *dest; ///< the location of this label if known struct sblock_s *dest; ///< the location of this label if known
const char *name; ///< the name of this label const char *name; ///< the name of this label
struct symbol_s *symbol; ///< symbol used to define this label (maybe 0) struct symbol_s *symbol; ///< symbol used to define this label (maybe 0)
int used; ///< label is used as a target int used; ///< label is used as a target
unsigned id;
struct daglabel_s *daglabel; struct daglabel_s *daglabel;
} ex_label_t; } ex_label_t;

View file

@ -42,6 +42,8 @@ typedef struct {
void (*declare_sym) (specifier_t spec, const expr_t *init, void (*declare_sym) (specifier_t spec, const expr_t *init,
symtab_t *symtab, expr_t *block); symtab_t *symtab, expr_t *block);
void (*vararg_int) (const expr_t *e); void (*vararg_int) (const expr_t *e);
unsigned label_id;
} target_t; } target_t;
extern target_t current_target; extern target_t current_target;

View file

@ -63,6 +63,7 @@
#include "tools/qfcc/include/strpool.h" #include "tools/qfcc/include/strpool.h"
#include "tools/qfcc/include/struct.h" #include "tools/qfcc/include/struct.h"
#include "tools/qfcc/include/symtab.h" #include "tools/qfcc/include/symtab.h"
#include "tools/qfcc/include/target.h"
#include "tools/qfcc/include/type.h" #include "tools/qfcc/include/type.h"
#include "tools/qfcc/include/value.h" #include "tools/qfcc/include/value.h"
@ -515,6 +516,7 @@ new_label_expr (void)
l->type = ex_label; l->type = ex_label;
l->label.name = new_label_name (); l->label.name = new_label_name ();
l->label.id = current_target.label_id++;
return l; return l;
} }

View file

@ -59,9 +59,16 @@ typedef struct spirvctx_s {
defspace_t *code_space; defspace_t *code_space;
struct DARRAY_TYPE (unsigned) type_ids; struct DARRAY_TYPE (unsigned) type_ids;
struct DARRAY_TYPE (unsigned) label_ids;
unsigned id; unsigned id;
} spirvctx_t; } spirvctx_t;
static unsigned
spirv_id (spirvctx_t *ctx)
{
return ++ctx->id;
}
static unsigned static unsigned
spirv_type_id (const type_t *type, spirvctx_t *ctx) spirv_type_id (const type_t *type, spirvctx_t *ctx)
{ {
@ -84,6 +91,42 @@ spirv_add_type_id (const type_t *type, unsigned id, spirvctx_t *ctx)
ctx->type_ids.a[type->id] = id; ctx->type_ids.a[type->id] = id;
} }
static unsigned
spirv_label_id (const ex_label_t *label, spirvctx_t *ctx)
{
if (label->id < ctx->label_ids.size && ctx->label_ids.a[label->id]) {
return ctx->label_ids.a[label->id];
}
unsigned id = spirv_id (ctx);
if (label->id >= ctx->label_ids.size) {
size_t base = ctx->label_ids.size;
DARRAY_RESIZE (&ctx->label_ids, label->id + 1);
while (base < label->id + 1) {
ctx->label_ids.a[base++] = 0;
}
}
ctx->label_ids.a[label->id] = id;
return id;
}
static bool
is_block_terminated (defspace_t *code_space)
{
def_t *last_insn = nullptr;
if (code_space->def_tail != &code_space->defs) {
last_insn = (def_t *) code_space->def_tail;
}
if (last_insn
&& ((INSN(last_insn, 0) & SpvOpCodeMask) == SpvOpReturn
|| (INSN(last_insn, 0) & SpvOpCodeMask) == SpvOpReturnValue
|| (INSN(last_insn, 0) & SpvOpCodeMask) == SpvOpUnreachable
|| (INSN(last_insn, 0) & SpvOpCodeMask) == SpvOpBranchConditional
|| (INSN(last_insn, 0) & SpvOpCodeMask) == SpvOpBranch)) {
return true;
}
return false;
}
static def_t * static def_t *
spirv_new_insn (int op, int size, defspace_t *space) spirv_new_insn (int op, int size, defspace_t *space)
{ {
@ -106,12 +149,6 @@ spirv_str_insn (int op, int offs, int extra, const char *str, defspace_t *space)
return insn; return insn;
} }
static unsigned
spirv_id (spirvctx_t *ctx)
{
return ++ctx->id;
}
static void static void
spirv_Capability (SpvCapability capability, defspace_t *space) spirv_Capability (SpvCapability capability, defspace_t *space)
{ {
@ -461,6 +498,14 @@ spirv_LoopMerge (unsigned merge, unsigned cont, spirvctx_t *ctx)
INSN (insn, 3) = SpvLoopControlMaskNone; INSN (insn, 3) = SpvLoopControlMaskNone;
} }
static void
spirv_SelectionMerge (unsigned merge, spirvctx_t *ctx)
{
auto insn = spirv_new_insn (SpvOpSelectionMerge, 3, ctx->code_space);
INSN (insn, 1) = merge;
INSN (insn, 2) = SpvSelectionControlMaskNone;
}
static void static void
spirv_Branch (unsigned label, spirvctx_t *ctx) spirv_Branch (unsigned label, spirvctx_t *ctx)
{ {
@ -483,13 +528,13 @@ spirv_SplitBlock (spirvctx_t *ctx)
} }
static void static void
spirv_BranchConditional (unsigned test, unsigned true_label, spirv_BranchConditional (bool not, unsigned test, unsigned true_label,
unsigned false_label, spirvctx_t *ctx) unsigned false_label, spirvctx_t *ctx)
{ {
auto insn = spirv_new_insn (SpvOpBranchConditional, 4, ctx->code_space); auto insn = spirv_new_insn (SpvOpBranchConditional, 4, ctx->code_space);
INSN (insn, 1) = test; INSN (insn, 1) = test;
INSN (insn, 2) = true_label; INSN (insn, 2) = not ? false_label : true_label;
INSN (insn, 3) = false_label; INSN (insn, 3) = not ? true_label : false_label;
} }
static void static void
@ -639,14 +684,7 @@ spirv_function (function_t *func, spirvctx_t *ctx)
if (func->exprs) { if (func->exprs) {
spirv_emit_expr (func->exprs, ctx); spirv_emit_expr (func->exprs, ctx);
} }
def_t *last_insn = nullptr; if (!is_block_terminated (ctx->code_space)) {
if (ctx->code_space->def_tail != &ctx->code_space->defs) {
last_insn = (def_t *) ctx->code_space->def_tail;
}
if (!last_insn
|| ((INSN(last_insn, 0) & SpvOpCodeMask) != SpvOpReturn
&& (INSN(last_insn, 0) & SpvOpCodeMask) != SpvOpReturnValue
&& (INSN(last_insn, 0) & SpvOpCodeMask) != SpvOpUnreachable)) {
if (is_void (ret_type)) { if (is_void (ret_type)) {
spirv_new_insn (SpvOpReturn, 1, ctx->code_space); spirv_new_insn (SpvOpReturn, 1, ctx->code_space);
} else { } else {
@ -1160,14 +1198,25 @@ spirv_call (const expr_t *call, spirvctx_t *ctx)
return id; return id;
} }
static unsigned
spirv_jump (const expr_t *e, spirvctx_t *ctx)
{
auto target_label = &e->branch.target->label;
unsigned target = spirv_label_id (target_label, ctx);
spirv_Branch (target, ctx);
return 0;
}
static unsigned static unsigned
spirv_branch (const expr_t *e, spirvctx_t *ctx) spirv_branch (const expr_t *e, spirvctx_t *ctx)
{ {
if (e->branch.type == pr_branch_call) { if (e->branch.type == pr_branch_call) {
return spirv_call (e, ctx); return spirv_call (e, ctx);
} }
//FIXME if (e->branch.type == pr_branch_jump) {
return 1; return spirv_jump (e, ctx);
}
internal_error (e, "unexpected branch");
} }
static unsigned static unsigned
@ -1324,35 +1373,26 @@ spirv_field (const expr_t *e, spirvctx_t *ctx)
static unsigned static unsigned
spirv_loop (const expr_t *e, spirvctx_t *ctx) spirv_loop (const expr_t *e, spirvctx_t *ctx)
{ {
unsigned loop = spirv_id (ctx); auto break_label = &e->loop.break_label->label;
unsigned merge = spirv_id (ctx); auto continue_label = &e->loop.continue_label->label;
unsigned cont = spirv_id (ctx); unsigned merge = spirv_label_id (break_label, ctx);
spirv_Branch (loop, ctx); unsigned cont = spirv_label_id (continue_label, ctx);
spirv_LabelId (loop, ctx);
if (e->loop.do_while) { if (e->loop.do_while) {
unsigned loop = spirv_SplitBlock (ctx);
spirv_LoopMerge (merge, cont, ctx); spirv_LoopMerge (merge, cont, ctx);
spirv_SplitBlock (ctx); spirv_SplitBlock (ctx);
spirv_emit_expr (e->loop.body, ctx); spirv_emit_expr (e->loop.body, ctx);
spirv_SplitBlockId (cont, ctx); spirv_SplitBlockId (cont, ctx);
unsigned test = spirv_emit_expr (e->loop.test, ctx); unsigned test = spirv_emit_expr (e->loop.test, ctx);
if (e->loop.not) { spirv_BranchConditional (e->loop.not, test, merge, loop, ctx);
spirv_BranchConditional (test, merge, loop, ctx);
} else {
spirv_BranchConditional (test, loop, merge, ctx);
}
spirv_LabelId (merge, ctx); spirv_LabelId (merge, ctx);
} else { } else {
spirv_Branch (loop, ctx); unsigned loop = spirv_SplitBlock (ctx);
spirv_LabelId (loop, ctx);
spirv_LoopMerge (merge, cont, ctx); spirv_LoopMerge (merge, cont, ctx);
spirv_SplitBlock (ctx); spirv_SplitBlock (ctx);
unsigned body = spirv_id (ctx); unsigned body = spirv_id (ctx);
unsigned test = spirv_emit_expr (e->loop.test, ctx); unsigned test = spirv_emit_expr (e->loop.test, ctx);
if (e->loop.not) { spirv_BranchConditional (e->loop.not, test, body, merge, ctx);
spirv_BranchConditional (test, merge, body, ctx);
} else {
spirv_BranchConditional (test, body, merge, ctx);
}
spirv_LabelId (body, ctx); spirv_LabelId (body, ctx);
spirv_emit_expr (e->loop.body, ctx); spirv_emit_expr (e->loop.body, ctx);
spirv_SplitBlockId (cont, ctx); spirv_SplitBlockId (cont, ctx);
@ -1365,6 +1405,28 @@ spirv_loop (const expr_t *e, spirvctx_t *ctx)
static unsigned static unsigned
spirv_select (const expr_t *e, spirvctx_t *ctx) spirv_select (const expr_t *e, spirvctx_t *ctx)
{ {
unsigned merge = spirv_id (ctx);
unsigned true_label = spirv_id (ctx);
unsigned false_label = merge;
if (e->select.false_body) {
false_label = spirv_id (ctx);
}
unsigned test = spirv_emit_expr (e->select.test, ctx);
spirv_SelectionMerge (merge, ctx);
spirv_BranchConditional (e->select.not, test, true_label, false_label, ctx);
spirv_LabelId (true_label, ctx);
spirv_emit_expr (e->select.true_body, ctx);
if (!is_block_terminated (ctx->code_space)) {
spirv_Branch (merge, ctx);
}
if (e->select.false_body) {
spirv_LabelId (false_label, ctx);
spirv_emit_expr (e->select.false_body, ctx);
if (!is_block_terminated (ctx->code_space)) {
spirv_Branch (merge, ctx);
}
}
spirv_LabelId (merge, ctx);
return 0; return 0;
} }
@ -1423,6 +1485,7 @@ spirv_write (struct pr_info_s *pr, const char *filename)
.code_space = defspace_new (ds_backed), .code_space = defspace_new (ds_backed),
.decl_space = defspace_new (ds_backed), .decl_space = defspace_new (ds_backed),
.type_ids = DARRAY_STATIC_INIT (64), .type_ids = DARRAY_STATIC_INIT (64),
.label_ids = DARRAY_STATIC_INIT (64),
.id = 0, .id = 0,
}; };
auto space = defspace_new (ds_backed); auto space = defspace_new (ds_backed);